Introduction to SChannel TLS
This article explores the process of hijacking terminal operations to intercept TLS keys for decryption, with a primary focus on TLS traffic from Windows applications utilizing the SChannel TLS component. This includes applications like IIS, RDP, Internet Explorer, older versions of Edge, Outlook, PowerShell, and more. Applications that use OpenSSL or NSS, such as browsers other than Internet Explorer and the older Edge, are not included.
SChannel TLS
SChannel, also known as Secure Channel, is a Windows subsystem used whenever Windows applications want to perform any TLS-related actions, such as establishing an encrypted session with a remote server or accepting TLS connections from clients.
From a system perspective, SChannel implements the Security Support Provider Interface (SSPI) and is one of the SSP packages offered by Microsoft. Other SSP packages include CredSSP, Negotiate, NTLM, Kerberos, and Digest.
Examples of SChannel usage:
HTTPS Connections
Initiated by IE, Edge, PowerShellâs
Invoke-WebRequest
Received by IIS web server
RDP Connections
Clientâs mstsc.exe Terminal services on the server (termsrv.dll in svchost.exe)
LDAP server dynamic directoryâs LDAPS connections
Some WinRM (PS remoting) connections when the server HTTPS listener is enabled. PS remoting also supports SSL authentication using TLS client certificates, which is implemented through SChannel when enabled.
Previously mentioned browsers like Firefox and Google Chrome use different libraries for TLS processing, namely NSS and OpenSSL, thus their traffic is out of this articleâs scope. However, NSS and OpenSSL are both open-source, with documented methods for exporting secrets; for Firefox and Chrome, key export is built-in and can be activated using the SSLKEYLOGFILE
environment variable.
Decrypting TLS 1.2 Traffic and Ephemeral Keys with SChannel TLS
This research is not based on exploiting protocol vulnerabilities or weaknesses but rather on having complete control over the application or operating system establishing or accepting a connection, allowing us to retrieve any used keys and secrets to obtain the necessary information for decrypting TLS traffic.
Section 2.2 of the internal workings of TLS1 provides an excellent summary, which will not be detailed extensively in this article. Hereâs a quick overview of key concepts involved in TLS1.2 connections and encryption:
Ephemeral Keys
Whenever a TLS session is created, multiple keys associated with this connection are generated. Some keys are for encryption, others for message verification. Different keys exist for different directions (client to server and server to client). These keys are referred to as ephemeral keys to emphasize their short-term nature compared to long-term keys like server TLS certificate keys.
Perfect Forward Secrecy
All cipher suites can be categorized based on whether they support Perfect Forward Secrecy (PFS). With non-PFS cipher suites, any encrypted connection can be decrypted using captured traffic and the serverâs TLS private key. Conversely, for PFS cipher suites, you need the relevant sessionâs ephemeral keys to decrypt it.
Master Key
The process of forming ephemeral keys involves several steps. In TLS1.2, it begins with the server and client collaborating to produce some key material known as the Pre-Master Secret, which is then expanded into the Master Secret. Subsequently, a set of keys and IVs for encryption and authenticationâwrite keys and MAC keysâis generated. MAC keys are only used with non-AEAD ciphers.
TLS Session Tickets
Multiple independent TLS connections can belong to the same TLS session, meaning keys do not need recalculating every time. The previous method used session IDsâthe server sends a session ID to the client, which then uses it for subsequent connections. The server must store keys associated with the session, requiring considerable memory on the server. Thus, TLS session tickets (RFC5077) were proposed, whereby the server sends an encrypted session state to the client, encrypted with a key known only to the server, and the client returns it in the next connection. This implies that although ephemeral keys are supposed to be destroyed post-connection, they might persist in the memory of both the server and client. For details on the security implications of TLS session tickets, refer to section 12.
SSL Keylog File
To decrypt a TLS traffic dump, a method to provide this information is necessary. For TLS 1.2, the standard method supported by OpenSSL and NSS is using an SSL keylog file, where each line consists of a constant label string, a value identifying the TLS session, and the secretsâ value. As a reference, you can find Wiresharkâs keylog parsing routine in section 4.
Retrieve secrets used in each session Associate these keys with the session
Client Random and Session ID
TLS1.2 keylog file supports pre-master or master secrets for sessions. Sessions can be identified using client random (a random, unencrypted value sent by the client during the TLS handshake) or session ID (an unencrypted value sent by the server). An example of a TLS1.2 keylog file is as follows:
â`javascript COPY
CLIENT_RANDOM
Decrypting TLS 1.3 Traffic with SChannel TLS
Many aspects mentioned above regarding TLS1.2 also apply to TLS1.3. However, there are several alterations in how secrets are generated.
For TLS 1.2, the key generation scheme is as follows:
â`javascript COPY
(1) Pre-Master Secret => (2) Master Secret => (3) A set of write keys and IVs and possibly MAC keys for client and server
In TLS1.2, the keylog file format requires you to provide the secrets for step (1) or step (2).
For TLS1.3, the scheme has evolved into the following version (refer to RFC8446 page 93):
â`javascript COPY
(1) Input Keying Material (IKM) => (2) A set of Secrets: Early, Handshake, Master etc => (3) A set of keys and IVs
TLS1.3 keylog files also require you to provide the secrets from step (2). Unlike TLS 1.2, each TLS session requires multiple lines, with each line providing a specific secret and binding it to a TLS session using client random. You need at least four secrets:
- Client and server handshake secret
- Client and server communication secret
An example keylog file:
â`javascript COPY
CLIENT_HANDSHAKE_TRAFFIC_SECRET SERVER_HANDSHAKE_TRAFFIC_SECRET CLIENT_TRAFFIC_SECRET_0 CLIENT_TRAFFIC_SECRET_0
Key Isolation
The Windows Schannel API features the concept of Key Isolation (refer to section 5), which makes it more difficult to leak various confidential data by storing it in a centralized isolated location. Assume we have a process (e.g., terminal services client, mstsc) that wants to establish a TLS connection. However, the actual TLS handshake will be performed in another process (i.e., lsass.exe), and the secrets generated during the handshake (i.e., TLS1.2 pre-master and master keys) will never leave the memory of lsass.exe, nor will they ever touch mstsc.exe. All these operations are transparent to the application, which only uses functions from schannel.dll.
The schannel.dll on the application side uses ALPC to connect to the schannel.dll on the lsass side behind the scenes (refer to section 1, figures 2.6 and 2.7). ALPC calls are processed by a copy of schannel.dll loaded into lsass.exe, and then various key-related tasks are performed using a set of cryptographic APIs (CNG, mainly implemented in ncrypt.dll
and bcrypt.dll
).
This mode of operation is not unique to SChannel and applies to all security providers implementing SSPI. When you call InitializeSecurityContext
, this call is processed by the SpInitLsaModeContextFn
callback on the LSA side, and then the result is passed to SpInitUserModeContext
on the application side. This way, credentials donât need to be kept in memory, and Windows applications can use NTLM or Kerberos authentication.
For us, this means that lsass.exe is a good place to extract all ephemeral TLS keys used by any SChannel-enabled application. We need to hook the key creation/manipulation paths or find a way to reliably locate them in memory. We also need to bind them to a TLS session to utilize the obtained keys, preferably using methods supported by Wireshark (i.e., session ID or client random).
Objective
Our objective is to decrypt SChannel TLS traffic using Wireshark under full control of the application and/or operating system on the client or server side of the connection. Compared to the problem statement in Jacob Kambicâs paper1 (extracting keys from memory dumps), this approach is more flexible as it allows using memory scanning, debugging, and function hooking. Other key requirements are as follows:
Avoid relying on session resumption and other mechanisms, preventing keys from being cleared from memory when connections are closed; As much as possible, avoid relying on hardcoded offsets or other Windows and/or library-version specific elements; Extract keys from both ends of a connection; Extract keys from an area without requiring administrator privileges, i.e., not touching lsass.exeâs memory. Similar to the methods proposed in section 10.
Fetching TLS1.2 Keys
For TLS1.2, obtaining the pairing of client random and keys allows generating a keylog line that can be inserted into Wireshark for decryption.
Normal (Non-Resumption) Sessions
SslGenerateMasterKey
In a normal (non-resumption) TLS1.2 session, the SslGenerateMasterKey
function in ncrypt.dll
is called.
Refer to the official documentation of SslGenerateMasterKey:
â`javascript COPY
SECURITY_STATUS WINAPI SslGenerateMasterKey( _In_ NCRYPT_PROV_HANDLE hSslProvider, _In_ NCRYPT_KEY_HANDLE hPrivateKey, _In_ NCRYPT_KEY_HANDLE hPublicKey, _Out_ NCRYPT_KEY_HANDLE *phMasterKey, _In_ DWORD dwProtocol, _In_ DWORD dwCipherSuite, _In_ PNCryptBufferDesc pParameterList, _Out_ PBYTE pbOutput, _In_ DWORD cbOutput, _Out_ DWORD *pcbResult, _In_ DWORD dwFlags );
As indicated, the fourth parameter is annotated as Out (a type of âheader annotationâ), meaning that this pointer will be filled with the key address at the end of the function call.
Acquiring the Master Key
Following the address pointed by the *phMasterkey pointer leads to the NcryptSslKey
structure.
>
This structure contains an important magic value BDDD
at offset 0x04 (see section 1, page 77), and at offset 0x10 (x64 case, 0x0C for x86), it contains a pointer to another magic value 5lss
structure (see section 1, pages 64-68), viewed in the experimental environment memory (x64) as follows:
>
Following the pNcryptSslKey pointer address 000002a8bcd81e70 enters
SslMasterSecret` structure, hereafter referred to as SSL5 structure
According to section 1, page 68, the master key itself is located within the SSL5 structure at offset 0x1C (x64, 0x14 for x86), and is 48 (0x30) bytes in length:
Acquiring the Client Random
Returning to SslGenerateMasterKey
, note the parameter list:
â`javascript COPY
_In_ PNCryptBufferDesc pParameterList ... pParameterList [in] A pointer to an array of NCryptBuffer buffers that contain information used as part of the key exchange operation. The precise set of buffers is dependent on the protocol and cipher suite that is used. At the minimum, the list will contain buffers that hold the client and server supplied random values.
The client and server random are precisely what we need to bind the keys to the session. The NCryptBuffer
and NCryptBufferDesc
structures are documented in the MS Reference Source for the .NET framework, consulted in section 17:
â`javascript COPY
typedef struct _NCryptBufferDesc { ULONG ulVersion; ULONG cBuffers; PNCryptBuffer pBuffers; } NCryptBufferDesc, *PNCryptBufferDesc; typedef struct _NCryptBuffer { ULONG cbBuffer; ULONG BufferType; PVOID pvBuffer; } NCryptBuffer, *PNCryptBuffer;
Following the pParamterList pointer to reach the NcrytBufferDesc
, the number of buffers is obtained at offset 0x04, and the pointer to the array of NCryptBuffer structures is found at offset 0x08:
Following the pBuffers pointer:
At offset 0x04 in the pBuffers structure array is the data type of the buffer:
BufferType=0x14=20 denotes NCRYPTBUFFER_SSL_CLIENT_RANDOM BufferType=0x15=21 denotes NCRYPTBUFFER_SSL_SERVER_RANDOM
When BufferType is 0x14, the pointer to client random data is at offset 0x08