Nota: a partire da OpenSSL 1.1.1 (non rilasciato), sarà possibile impostare una funzione di callback che riceve le linee di log delle chiavi. Consulta il manuale SSL_CTX_set_keylog_callback (3) per i dettagli. Questo può essere iniettato come al solito usando un debugger o un hook LD_PRELOAD
. Continua a leggere se sei bloccato con una versione precedente di OpenSSL.
Se hai appena accesso a gdb al processo live oa un core dump, puoi leggere i dati dalle strutture di dati. È anche possibile utilizzare una libreria di interposizione.
Nel testo seguente, viene descritta l'idea di base dell'estrazione di chiavi usando GDB, quindi viene fornito uno script automatico per eseguire l'acquisizione.
Utilizzo di GDB (idea di base)
Basato su questo post StackOverflow , sono stato in grado di costruire una funzione che può stampare una riga adatta al file di log delle chiavi pre-master di Wiresharks. Questo è particolarmente utile per core dump. In GDB, esegui:
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
Successivamente, dopo aver eseguito il passaggio verso l'alto nello stack fino a ottenere una struttura SSL
, richiamare il comando python pm()
. Esempio:
(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 : non dimenticare di installare OpenSSL con i simboli di debug ! Sui derivati Debian sarebbe chiamato qualcosa come libssl1.0.0-dbg , Fedora/RHEL call it
openssl-debuginfo ', ecc.
Utilizzo di GDB (approccio automatizzato migliorato)
L'idea di base descritta sopra funziona per piccoli test manuali. Per l'estrazione di massa delle chiavi (ad esempio da un server SSL), sarebbe più semplice automatizzare l'estrazione di queste chiavi.
Questo è fatto da questo script Python per GDB: link (vedi le sue intestazioni per le istruzioni di installazione e utilizzo). Funziona fondamentalmente in questo modo:
- Installa i punti di interruzione su diverse funzioni in cui possono sorgere nuove chiavi pre-master.
- Attendi che la funzione finisca e scrivi questi tasti (se non è noto prima) in un file (che segue il formato
SSLKEYLOGFILE
di NSS).
Accoppiato con Wireshark, esegui un'acquisizione dal vivo da un server remoto eseguendo questi comandi:
%pre%
Utilizzo di LD_PRELOAD
SSL / TLS può negoziare solo le chiavi durante le fasi di handshake SSL. Interponendo le interfacce della libreria di OpenSSL ( libssl.so
) che esegue dette azioni, sarai in grado di leggere la chiave pre-master.
Per i clienti, devi interporre SSL_connect
. Per i server è necessario interporre SSL_do_handshake
o SSL_accept
(a seconda dell'applicazione). Per supportare la rinegoziazione, devi anche intercettare SSL_read
e SSL_write
.
Una volta che queste funzioni sono state intercettate utilizzando una libreria LD_PRELOAD
, puoi utilizzare dlsym(RTLD_NEXT, "SSL_...")
per cercare il simbolo "reale" dalla libreria SSL. Chiama questa funzione, estrai le chiavi e passa il valore di ritorno.
Un'implementazione di questa funzionalità è disponibile all'indirizzo link .