Skip to content

Instantly share code, notes, and snippets.

@jborean93
Last active June 27, 2024 12:18
Show Gist options
  • Save jborean93/6c1f1b3130f2675f1618da56633eb1fa to your computer and use it in GitHub Desktop.
Save jborean93/6c1f1b3130f2675f1618da56633eb1fa to your computer and use it in GitHub Desktop.
Logs Wireshark compatible TLS keys like the SSLKEYLOGFILE env var
#Requires -Module PSDetour
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]
$LogPath
)
$LogPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($LogPath)
$session = New-PSDetourSession -ProcessId lsass -ErrorAction Stop
try {
Invoke-Command -Session $session -ScriptBlock {
Function Get-SecretKey {
param([Parameter(Mandatory)][IntPtr]$KeyPtr)
$dddbStructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($KeyPtr)
$ssl3StructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($dddbStructPtr, 0x10)
$uuurStructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($ssl3StructPtr, 0x20)
$mskyStructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($uuurStructPtr, 0x10)
$secretLength = [System.Runtime.InteropServices.Marshal]::ReadInt32($mskyStructPtr, 0x10)
$secretPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($mskyStructPtr, 0x18)
$secret = [byte[]]::new($secretLength)
[System.Runtime.InteropServices.Marshal]::Copy($secretPtr, $secret, 0, $secret.Length)
, $secret
}
$tlsLog = [System.IO.File]::Open($using:LogPath, "Append", "Write", "Read")
$tlsWriter = [System.IO.StreamWriter]::new($tlsLog)
$tlsWriter.AutoFlush = $true
$state = @{
GetSecretKeyFunc = ${function:Get-SecretKey}.ToString()
ClientRandoms = [Hashtable]::Synchronized(@{})
Stages = [Hashtable]::Synchronized(@{})
TlsWriter = $tlsWriter
}
$ncrypt = @{ DllName = 'ncrypt.dll' }
Start-PSDetour -State $state -Hook @(
New-PSDetourHook @ncrypt -MethodName SslHashHandshake -Action {
<#
.DESCRIPTION
Called for TLS 1.3 sessions and TLS 1.2 with RFC 7627 session hashing.
It is used to capture the client_random value in the TLS ClientHello
message for future reference in the other hooked functions.
#>
[OutputType([int])]
param (
[IntPtr]$SslProvider,
[IntPtr]$HandshakeHash,
[IntPtr]$InputBytes,
[int]$InputLength,
[int]$Flags
)
# This buffer is the TLS packets, we are interested in the ClientHello
# which is msgType 1 and version 0x0303 (TLS 1.2)
$data = [byte[]]::new($InputLength)
[System.Runtime.InteropServices.Marshal]::Copy($InputBytes, $data, 0, $data.Length)
$msgType = $data[0]
$version = if ($msgType -eq 1) {
[System.BitConverter]::ToUInt16($data, 4)
}
if ($msgType -eq 1 -and $version -eq 0x0303) {
$crandom = [byte[]]::new(32)
[System.Buffer]::BlockCopy($data, 6, $crandom, 0, $crandom.Length)
$tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId
$this.State.ClientRandoms[$tid] = $crandom
}
$this.Invoke($SslProvider, $HandshakeHash, $InputBytes, $InputLength, $Flags)
}
New-PSDetourHook @ncrypt -MethodName SslExpandTrafficKeys -Action {
<#
.DESCRIPTION
Called for TLS 1.3 sessions twice.
First is for the handshake traffic key and second for the first
handshake traffic secret.
The function is undocumented but Param4 is the client secret and Param5
is the server secret.
#>
[OutputType([int])]
param (
[IntPtr]$Param1,
[IntPtr]$Param2,
[IntPtr]$Param3,
[IntPtr]$Param4,
[IntPtr]$Param5
)
$res = $this.Invoke($Param1, $Param2, $Param3, $Param4, $Param5)
$null = New-Item -Path Function:\Get-SecretKey -Value ([ScriptBlock]::Create(
$this.State.GetSecretKeyFunc))
$tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId
if ($this.State.Stages[$tid]) {
$suffix = 'TRAFFIC_SECRET_0'
}
else {
$suffix = 'HANDSHAKE_TRAFFIC_SECRET'
$this.State.Stages[$tid] = $true
}
$clientSecret = Get-SecretKey -KeyPtr $Param4
$serverSecret = Get-SecretKey -KeyPtr $Param5
$cr = $this.State.ClientRandoms[$tid]
($this.State.TlsWriter).WriteLine('CLIENT_{0} {1} {2}', $suffix,
[System.Convert]::ToHexString($cr),
[System.Convert]::ToHexString($clientSecret))
($this.State.TlsWriter).WriteLine('SERVER_{0} {1} {2}', $suffix,
[System.Convert]::ToHexString($cr),
[System.Convert]::ToHexString($serverSecret))
$res
}
New-PSDetourHook @ncrypt -MethodName SslExpandExporterMasterKey -Action {
<#
.DESCRIPTION
Called for TLS 1.3 sessions.
Gets the exporter secret through the undocumented function.
#>
[OutputType([int])]
param (
[IntPtr]$Param1,
[IntPtr]$Param2,
[IntPtr]$Param3,
[IntPtr]$Param4
)
$res = $this.Invoke($Param1, $Param2, $Param3, $Param4)
$null = New-Item -Path Function:\Get-SecretKey -Value ([ScriptBlock]::Create(
$this.State.GetSecretKeyFunc))
$tid = [System.Threading.Thread]::CurrentThread.ManagedThreadId
$secret = Get-SecretKey -KeyPtr $Param4
$cr = $this.State.ClientRandoms[$tid]
($this.State.TlsWriter).WriteLine('EXPORTER_SECRET {0} {1}',
[System.Convert]::ToHexString($cr),
[System.Convert]::ToHexString($secret))
$res
}
New-PSDetourHook @ncrypt -MethodName SslGenerateSessionKeys -Action {
<#
.DESCRIPTION
Called for TLS 1.2 sessions.
Gets the master secret key for TLS 1.2 sessions.
#>
[OutputType([int])]
param (
[IntPtr]$SslProvider,
[IntPtr]$MasterKey,
[IntPtr]$ReadKey,
[IntPtr]$WriteKey,
[IntPtr]$ParameterList,
[int]$Flags
)
<#
typedef struct _NCryptBufferDesc {
ULONG ulVersion;
ULONG cBuffers;
PNCryptBuffer pBuffers;
} NCryptBufferDesc, *PNCryptBufferDesc;
typedef struct _NCryptBuffer {
ULONG cbBuffer;
ULONG BufferType;
PVOID pvBuffer;
} NCryptBuffer, *PNCryptBuffer;
#>
$bufferCount = [System.Runtime.InteropServices.Marshal]::ReadInt32($ParameterList, 4)
$bufferPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($ParameterList, 8)
for ($i = 0; $i -lt $bufferCount; $i++) {
$bufferSize = [System.Runtime.InteropServices.Marshal]::ReadInt32($bufferPtr)
$bufferType = [System.Runtime.InteropServices.Marshal]::ReadInt32($bufferPtr, 4)
$bufferValuePtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($bufferPtr, 8)
if ($bufferType -eq 20) {
# NCRYPTBUFFER_SSL_CLIENT_RANDOM
$cr = [byte[]]::new($bufferSize)
[System.Runtime.InteropServices.Marshal]::Copy($bufferValuePtr, $cr, 0, $cr.Length)
break
}
$bufferPtr = [IntPtr]::Add($bufferPtr, 8 + ([IntPtr]::Size))
}
# The client_random should always be in the ParameterList but the
# fallback is still used.
if (-not $cr) {
$cr = ($this.State.ClientRandoms)[([System.Threading.Thread]::CurrentThread.ManagedThreadId)]
}
$ssl5StructPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($MasterKey, 0x10)
$secretPtr = [IntPtr]::Add($ssl5StructPtr,
([IntPtr]::Size -eq 8 ? 28 : 20))
$secret = [byte[]]::new(48)
[System.Runtime.InteropServices.Marshal]::Copy($secretPtr, $secret, 0, $secret.Length)
($this.State.TlsWriter).WriteLine('CLIENT_RANDOM {0} {1}',
[System.Convert]::ToHexString($cr),
[System.Convert]::ToHexString($secret))
$this.Invoke($SslProvider, $MasterKey, $ReadKey, $WriteKey, $ParameterList, $Flags)
}
)
Write-Host "Press any key to stop TLS logging..."
$null = $host.UI.RawUI.ReadKey()
Stop-PSDetour
$tlsWriter.Dispose()
$tlsLog.Dispose()
}
}
finally {
$session | Remove-PSSession
}
@jborean93
Copy link
Author

Thanks to https://b.poc.fun/decrypting-schannel-tls-part-1/ for providing all the hard work on what to hook and how.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment