Wireshark-dev: Re: [Wireshark-dev] Support for TLS1.2 decryption using derived keys
Date Prev · Date Next · Thread Prev · Thread Next
From: Peter Wu <[email protected]>
Date: Fri, 1 May 2020 00:23:13 +0200
Hi George,

On Thu, Apr 30, 2020 at 03:10:44PM +0300, webpentest wrote:
[..]
> Thanks for these additional resources! Just to clarify: extraction of
> secrets from lsass.exe is absolutely possible; I was able to do that
> successfully for on current windows 10.
> 
> The problem here is the increasing layers of protection from accessing
> lsass memory, such as Protected Process Light and others. Disabling
> these protections is not trivial and in some cases might even be
> impossible. Thus the main objective of my research was to explore ways
> to not touch the memory of lsass.exe.

Specifically for Protected Process Light, I found
https://github.com/Mattiwatti/PPLKiller. It will probably not be
suitable for production machines, but I see your point. Having all
potential mechanisms documented would help :-)

[..]
> > The traffic secret is described in
> > https://tools.ietf.org/html/rfc8446#section-7.3, it is the same
> > handshake and application data secret that Wireshark already supports!
> > So for TLS 1.3 support, hopefully you can use this mechanism without
> > requiring further modifications to Wireshark. 
> 
> So, just to make sure that I understand you correctly, what you are
> implying is that for TLS1.3 connections I might be able to extract not
> the write keys, but the derived secrets, which would be preferable for
> wireshark as it would enable using already-existing sslkeylog-parsing
> functionality.

Indeed. These are the keys for a simplified TLS 1.2 handshake:

    premaster secret = shared secret from RSA or DH key exchange
    master secret = hash(premaster secret)
    client/server write key/iv = hash(master_secret)

Wireshark allows application data decryption when either the first
secret or the second secret is provided. The "CLIENT_RANDOM" label maps
to the second case.

In TLS 1.3, not only the application data is encrypted, but part of the
handshake as well. Both use different write key/iv values with different
"master secrets". To complicate things, there are also different master
secrets for the client->server and server->client directions.

While TLS 1.3 decryption in Wireshark could be implemented with just the
"premaster secret" (the (EC)DHE secret) and the optional PSK, at some
point we settled with the hashes of those two secrets. That is why
TLS 1.3 decryption in Wireshark needs at least four keys.

As you can see in the simplified handshake, the master key alone is
sufficient to compute the "write key" and "write iv".

> I agree, but I fear that these structures you mention above are, alike
> the tls1.2 master key, may exist only in the memory of lsass, but not
> the application that uses schannel API - because of the same key
> isolation feature.

Indeed the secrets are available in lsass, but apparently for TLS 1.3
there exists a way to export these secrets. Maybe not purely passively
though, I did not check whether this information is always communicated.
In fact, I don't know much about Windows programming at all.

> > Maybe something similar is
> > available for TLS 1.2 and earlier support. If not, then we could add
> > support for write key/iv, but only as a last resort.
> 
> Seems to me that we have two separate (although, related) tasks at hand:
> 
> 1. A generic way to export schannel key material in SSLKEYLOG-like
> format using elevated privilege and lsass.exe debugging / memory.
> Preferably - the data that wireshark supports already - master secret
> for tls <= 1.2 and the intermediate traffic secrets for tls 1.3

That would be great :-)

> 2. A generic way to export schannel key material for a particular
> process _without_ using elevated privilege. Here I'm almost certain that
> for TLS <= 1.2 exporting master secret is not possible, and I strongly
> suspect that for TLS1.3 exporting traffic secrets is not possible as
> well (but this I haven't yet verified). So if wireshark is to support
> keys extracted this way, we need a patch that will allow using write
> key/iv from a keylog - both for tls <= 1.2 and 1.3

As mentioned before, TLS 1.3 has double the number of keys (handshake
and application data) while TLS 1.2 only has keys for application data.
I guess that for simplicity, perhaps it is better to restrict this
mechanism to TLS 1.2 for now.

As a side note, I just realized that this mechanism could also be used
for kTLS in the Linux kernel. That depends on the userspace application
to perform the TLS handshake and provide (effectively) the write key and
IV to the kernel. If one can tap into the kernel that way, it can
transparently extract secrets without modifying userspace applications.
https://www.kernel.org/doc/html/latest/networking/tls.html

[..]
> So, i would need to extend ssl_session->state with an additional flag
> for cases where only the write keys are available (say, SSL_WRITE_KEYS)
> and account for this situation in ssl_generate_keyring_material.
> 
> I'll also have to extend SslDecryptSession to accomodate for write keys
> and ivs, because currently I pass these values using
> ssl_master_key_map_t parameter that I added to
> ssl_generate_keyring_material prototype, and changing the prototype is
> probably ill-advised?

You should not require additional fields in SslDecryptSession. Instead,
in ssl_generate_keyring_material, if you detect that no master secrets
are available, you could try to look up the four keys in the hash table.
Then if that exists, you can continue with the "key expansion" part
where you can skip the "prf()" call.

> > - Do not allow the QUIC_ prefix in the regex. It is deprecated and will
> >   be removed later.
> Thanks for clarification.
> > - The write key and write IV are usually available at the same time.
> >   What about defining a single format such as
> >   "WRITE_KEY <crand> <client key> <client iv> <server key> <server iv>"?
> >   This would reduce the number of hashtables required as well. For
> >   non-AEAD ciphers there is also a client/server MAC key for verifying
> >   the decryption result. In theory these could also be added to ensure
> >   full functionality. Not sure how important it is.
>
> Again, separate lines for separate values were just easier to implement
> quickly. Considering the use of one hashtable - I'll have to place a
> there a struct of four StringInfo* (instead of just StringInfo* as it
> currently is for all hashtables that are involved in keylogfiles
> parsing). This in turn will require special handling in
> tls_keylog_process_lines (currently the code expected all hashtables to
> contain StringInfo*). Other option would be to somehow serialize the
> keys and ivs into one StringInfo, but given their variable length this
> is also a little bit ugly. Are there any other options that I'm missing
> here.

Given that the key expansion splits one block into multiple keys,
perhaps concatenating all four keys into one block for the hash table
does not seem to be a bad idea after all.

The key log file format and the internal implementation detail are two
separate concerns. Even if internally in Wireshark it is one
concatenated block, there are two options for the key log format:

 - Concatenate all four keys, effectively producing the key block from
   https://tools.ietf.org/html/rfc5246#section-6.3
   An advantage of this format is that you automatically get the MAC key
   for non-AEAD ciphers. Suggested label: "KEY_BLOCK".
 - Keep the four separated on one line, that sets clear boundaries
   between the semantics of each item. A disadvantage is that the MAC
   key can be missing.

Given the MAC key situation, I am tempted to go for the first option.
Can you verify whether you can get all of this data from SChannel? Try
for example TLS_RSA_WITH_AES_128_CBC_SHA.
-- 
Kind regards,
Peter Wu
https://lekensteyn.nl