Age | Commit message (Collapse) | Author | Files | Lines |
|
Tested with macOS 10.15.1 (Catalina). Works with python3 3.7.5 (requests
library) and openssl 1.1.1d (s_client) from Homebrew. Does not work with
curl 7.64.1 in /usr/bin/curl because it is signed and does not allow
DYLD environment variables to be passed when SIP is enabled.
|
|
When libssl.so.1.1.1 was not yet loaded, it would assume that an older
library was already loaded and avoid the new API. That assumption is not
correct, it is also possible that no library was loaded at all as is the
case with Python. Test:
./sslkeylog.sh python -c \
'import requests;print(requests.head("https://wireshark.org"))'
Before this fix it would output all zeroes as secret (a sign that
something is wrong).
|
|
OpenSSL 1.1.1 adds TLS 1.3 support which uses a new secrets format.
Previously it resulted in garbage keylog files, this has been fixed now.
OpenSSL 1.1.1 also introduces a new API for secrets extraction.
Consumers can use it like this (curl uses this code for example):
static void keylog_callback(const SSL *ssl, const char *line) {
/* write line and terminating '\n' */
}
{
SSL_CTX *ctx;
...
SSL_CTX_set_keylog_callback(ctx, keylog_callback);
SSL *ssl = SSL_new(ctx);
}
In case you cannot change the source code for an application, you can
use sslkeylog.c again. This will basically perform the above step, set
the key log callback before calling SSL_new.
Since the new OpenSSL 1.1.1 API requires no further interception of
SSL_read and other functions, a new NO_OPENSSL_110_SUPPORT macro was to
avoid intercepting these. Additionally, a NO_OPENSSL_102_SUPPORT macro
avoids the need for OpenSSL development headers.
Caveat: when building with OpenSSL <= 1.0.2, libsslkeylog.so will not be
compatible with runtime OpenSSL 1.1.0. OpenSSL 1.1.1 still works though.
Use of SSL_new and interception via SSL_CTX_set_keylog_callback was
initially proposed by Derick Rethans, thanks for the suggestion!
|
|
Lookup SSL_SESSION_get_master_key and SSL_get_client_random at runtime
too after intercepting a call instead of a link-time dependency.
|
|
Since the previous OpenSSL 1.1.0 compatibility patch, addition of the
SSL_get_session and SSL_SESSION_get_master_key required them to be
available at load time. Since applications are not necessarily linked
with -lssl, this can fail.
Avoid this dependency by looking up the symbols at runtime. Tested with
OpenSSL 1.0.2.k (using python+requests) and
OpenSSL_1_1_0-pre6-1439-g0e2c7b3ee (openssl s_client).
|
|
OpenSSL 1.1.0 makes some structures opaque, but luckily it provides new
functions to extract the client random and master secret which is all we
need from the structures.
Tested with OpenSSL 1.1.0-pre6 using openssl s_client and
OpenSSL 1.0.2.h using curl.
|
|
This solves a null deref in python ssl module in SSL_do_handshake.
|
|
These functions can trigger a renegotiation which changes the key
material (detected by using `curl` and `openssl s_server` and pressing
`R` in `openssl s_server`).
|
|
SSL_connect is somehow called multiple times on the same connection
by curl, this may result in duplicate keylog file entries. Detect when
the state changes, and only print the keys if it has changed.
|
|
Also intercept SSL_do_handshake (nginx) and SSL_accept (s_server).
|
|
Try to dump as many keys as possible, even if a fatal alert occurred.
Wireshark does not support SSLv2, so check that a successful connection
does not use SSLv2 before dumping keys (this fixes a crash).
|
|
This follows the preference name ssl.keylog_file.
|
|
For a gdb function, see http://security.stackexchange.com/a/80174/2630
To generate the line assuming you have a context with a SSL structure
(named "s") run this:
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
python pm()
|