Nota: a partir de OpenSSL 1.1.1 (no publicado), será posible establecer una función de devolución de llamada que reciba las líneas de registro clave. Consulte el manual SSL_CTX_set_keylog_callback (3) para obtener más información. Esto se puede inyectar como de costumbre usando un depurador o un gancho LD_PRELOAD
. Siga leyendo si está atascado con una versión anterior de OpenSSL.
Si solo tiene acceso gdb al proceso en vivo o un volcado de memoria, puede leer los datos de las estructuras de datos. También es posible utilizar una biblioteca de interposición.
En el siguiente texto, se describe la idea básica de la extracción de claves con GDB, luego se proporciona un script automatizado para realizar la captura.
Usando GDB (idea básica)
Basado en esta publicación de Stackoverflow , pude construir una función que podría imprimir una línea adecuada para el archivo de registro de clave pre-maestro de Wiresharks. Esto es especialmente útil para volcados de núcleo. En GDB, ejecute:
python
def read_as_hex(name, size):
addr = gdb.parse_and_eval(name).address
data = gdb.selected_inferior().read_memory(addr, size)
return ''.join('%02X' % ord(x) for x in data)
def pm(ssl='s'):
mk = read_as_hex('%s->session->master_key' % ssl, 48)
cr = read_as_hex('%s->s3->client_random' % ssl, 32)
print('CLIENT_RANDOM %s %s' % (cr, mk))
end
Luego, más adelante, después de que subas en la pila hasta que obtengas una estructura SSL
, invoca el comando python pm()
. Ejemplo:
(gdb) bt
#0 0x00007fba7d3623bd in read () at ../sysdeps/unix/syscall-template.S:81
#1 0x00007fba7b40572b in read (__nbytes=5, __buf=0x7fba5006cbc3, __fd=<optimized out>) at /usr/include/x86_64-linux-gnu/bits/unistd.h:44
#2 sock_read (b=0x7fba60191600, out=0x7fba5006cbc3 "7# Start logging SSL keys to file premaster.txt. Be careful *not* to
# press Ctrl-C in gdb, these are passed to the application. Use
# kill -TERM $PID_OF_GDB (or -9 instead of -TERM if that did not work).
(server) SSLKEYLOGFILE=premaster.txt gdb -batch -ex skl-batch -p 'pidof nginx'
# Read SSL keys from the remote server, flushing after each written line
(local) ssh user@host stdbuf -oL tailf premaster.txt > premaster.txt
# Capture from the remote side and immediately pass the pcap to Wireshark
(local) ssh user@host 'tcpdump -w - -U "tcp port 443"' |
wireshark -k -i - -o ssl.keylog_file:premaster.txt
3python
def read_as_hex(name, size):
addr = gdb.parse_and_eval(name).address
data = gdb.selected_inferior().read_memory(addr, size)
return ''.join('%02X' % ord(x) for x in data)
def pm(ssl='s'):
mk = read_as_hex('%s->session->master_key' % ssl, 48)
cr = read_as_hex('%s->s3->client_random' % ssl, 32)
print('CLIENT_RANDOM %s %s' % (cr, mk))
end
1(gdb) bt
#0 0x00007fba7d3623bd in read () at ../sysdeps/unix/syscall-template.S:81
#1 0x00007fba7b40572b in read (__nbytes=5, __buf=0x7fba5006cbc3, __fd=<optimized out>) at /usr/include/x86_64-linux-gnu/bits/unistd.h:44
#2 sock_read (b=0x7fba60191600, out=0x7fba5006cbc3 "7# Start logging SSL keys to file premaster.txt. Be careful *not* to
# press Ctrl-C in gdb, these are passed to the application. Use
# kill -TERM $PID_OF_GDB (or -9 instead of -TERM if that did not work).
(server) SSLKEYLOGFILE=premaster.txt gdb -batch -ex skl-batch -p 'pidof nginx'
# Read SSL keys from the remote server, flushing after each written line
(local) ssh user@host stdbuf -oL tailf premaster.txt > premaster.txt
# Capture from the remote side and immediately pass the pcap to Wireshark
(local) ssh user@host 'tcpdump -w - -U "tcp port 443"' |
wireshark -k -i - -o ssl.keylog_file:premaster.txt
3%pre%1%pre%10T", outl=5) at bss_sock.c:142
#3 0x00007fba7b40374b in BIO_read (b=0x7fba60191600, out=0x7fba5006cbc3, outl=5) at bio_lib.c:212
#4 0x00007fba7b721a34 in ssl3_read_n (s=0x7fba60010a60, n=5, max=5, extend=<optimized out>) at s3_pkt.c:240
#5 0x00007fba7b722bf5 in ssl3_get_record (s=0x7fba60010a60) at s3_pkt.c:507
#6 ssl3_read_bytes (s=0x7fba60010a60, type=23, buf=0x7fba5c024e00 "Z", len=16384, peek=0) at s3_pkt.c:1011
#7 0x00007fba7b720054 in ssl3_read_internal (s=0x7fba60010a60, buf=0x7fba5c024e00, len=16384, peek=0) at s3_lib.c:4247
...
(gdb) frame
#4 0x00007fba7b721a34 in ssl3_read_n (s=0x7fba60010a60, n=5, max=5, extend=<optimized out>) at s3_pkt.c:240
240 in s3_pkt.c
(gdb) python pm()
CLIENT_RANDOM 9E7EFAC51DBFFF84FCB9...81796EBEA5B15E75FF71EBE 6ED2EA80181...
10T", outl=5) at bss_sock.c:142
#3 0x00007fba7b40374b in BIO_read (b=0x7fba60191600, out=0x7fba5006cbc3, outl=5) at bio_lib.c:212
#4 0x00007fba7b721a34 in ssl3_read_n (s=0x7fba60010a60, n=5, max=5, extend=<optimized out>) at s3_pkt.c:240
#5 0x00007fba7b722bf5 in ssl3_get_record (s=0x7fba60010a60) at s3_pkt.c:507
#6 ssl3_read_bytes (s=0x7fba60010a60, type=23, buf=0x7fba5c024e00 "Z", len=16384, peek=0) at s3_pkt.c:1011
#7 0x00007fba7b720054 in ssl3_read_internal (s=0x7fba60010a60, buf=0x7fba5c024e00, len=16384, peek=0) at s3_lib.c:4247
...
(gdb) frame
#4 0x00007fba7b721a34 in ssl3_read_n (s=0x7fba60010a60, n=5, max=5, extend=<optimized out>) at s3_pkt.c:240
240 in s3_pkt.c
(gdb) python pm()
CLIENT_RANDOM 9E7EFAC51DBFFF84FCB9...81796EBEA5B15E75FF71EBE 6ED2EA80181...
Nota : ¡no olvides instalar OpenSSL con símbolos de depuración ! En los derivados de Debian, se llamaría algo así como libssl1.0.0-dbg , Fedora/RHEL call it
openssl-debuginfo ', etc.
Uso de GDB (enfoque mejorado y automatizado)
La idea básica que se describe anteriormente funciona para pruebas pequeñas y manuales. Para la extracción masiva de claves (desde un servidor SSL, por ejemplo), sería mejor automatizar la extracción de estas claves.
Esto se hace con este script de Python para GDB: enlace (vea Sus encabezados para las instrucciones de instalación y uso). Básicamente funciona así:
- Instale puntos de interrupción en varias funciones en las que pueden surgir nuevas claves pre-maestras.
- Espere a que la función termine y escriba estas claves (si no las conoce antes) en un archivo (que sigue el formato
SSLKEYLOGFILE
de NSS).
Junto con Wireshark, realiza una captura en vivo desde un servidor remoto ejecutando estos comandos:
%pre%
Usando LD_PRELOAD
SSL / TLS solo puede negociar claves en los pasos de negociación de SSL. Al interponer las interfaces de la biblioteca de OpenSSL ( libssl.so
) que realiza dichas acciones, podrá leer la clave pre-maestra.
Para los clientes, debe interponer SSL_connect
. Para los servidores, debe interponer SSL_do_handshake
o SSL_accept
(según la aplicación). Para apoyar la renegociación, también deberá interceptar SSL_read
y SSL_write
.
Una vez que estas funciones son interceptadas usando una biblioteca LD_PRELOAD
, puedes usar dlsym(RTLD_NEXT, "SSL_...")
para buscar el símbolo "real" de la biblioteca SSL. Llame a esta función, extraiga las claves y pase el valor de retorno.
Una implementación de esta funcionalidad está disponible en enlace .