0x00 Preface
---
The sekurlsa::wdigest module in Mimikatz is a frequently used feature in penetration testing. It can extract credentials from the lsass process, typically obtaining plaintext passwords of logged-in users (by default, this is not possible on Windows Server 2008 R2 and later systems; registry modifications are required, and the user must log in again to obtain them).
XPN documented his research insights on WDigest in his blog and open-sourced a POC, implementing credential extraction from the lsass process on Win10_1809 x64 using C++.
This article will extend XPN's POC to support Win7/Win8/Windows Server 2008/Windows Server 2008 R2/Windows Server 2012/Windows Server 2012 R2, detailing the implementation process and specifics.
XPN's blog:
https://blog.xpnsec.com/exploring-mimikatz-part-1/
POC:
https://gist.github.com/xpn/12a6907a2fce97296428221b3bd3b394
0x02 Introduction
---
This article will cover the following:
- Implementation approach
- Program implementation details
0x03 Implementation Approach
---
- Elevate to Debug Privileges
- Obtain lsass Process Handle
- Enumerate handles of all modules in the lsass process, locate the memory positions of wdigest.dll and lsasrv.dll
- Retrieve InitializationVector, AES, and 3DES values from lsasrv.dll for decryption
- Extract credential information from wdigest.dll, determine encryption algorithm, and decrypt to obtain plaintext passwords
Detailed explanation is as follows:
1. Elevate to Debug Privileges
Code can directly reuse previous code:
An open-source project
2. Obtain lsass Process Handle
- Create a process snapshot via CreateToolhelp32Snapshot
- Traverse the process list
- Search for the lsass.exe process and obtain its pid
- Obtain lsass process handle
3. Enumerate handles of all modules in the lsass process, locate the memory positions of wdigest.dll and lsasrv.dll
Enumerate handles of all modules in the lsass process via EnumProcessModules
4. Retrieve InitializationVector, AES, and 3DES values from lsasrv.dll for decryption
Offset positions vary across different systems; refer to mimikatz source code for details:
https://github.com/gentilkiwi/mimikatz/blob/68ac65b426d1b9e1354dd0365676b1ead15022de/mimikatz/modules/sekurlsa/crypto/kuhl_m_sekurlsa_nt6.c#L8-L32
The following four offsets differ:
- LsaInitializeProtectedMemory_KEY
- int IV_OFFSET
- int DES_OFFSET
- int AES_OFFSET
The data structures for AES and 3DES also vary across different systems:
Win7:
typedef struct _KIWI_BCRYPT_KEY { |
Note:
Source:
https://github.com/gentilkiwi/mimikatz/blob/master/modules/kull_m_crypto.h#L56
Windows 8 and Windows 10:
typedef struct _KIWI_BCRYPT_KEY81 { |
Note:
Source:
https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/sekurlsa/crypto/kuhl_m_sekurlsa_nt6.h#L22
Among them, KIWI_HARD_KEY in KIWI_BCRYPT_KEY and KIWI_BCRYPT_KEY81 stores AES and 3DES data, with the following structure:
typedef struct _KIWI_HARD_KEY { |
Note:
Source:
https://github.com/gentilkiwi/mimikatz/blob/master/modules/kull_m_crypto.h#L51
ULONG cbSecret indicates the length, BYTE data[ANYSIZE_ARRAY] is the actual encrypted content
5. Obtain credential information from wdigest.dll and decrypt the plaintext password
Credential information is located at a fixed offset and can be located by searching for a fixed structure (BYTE PTRN_WIN6_PasswdSet[] = {0x48, 0x3b, 0xd9, 0x74};)
Note:
Source:
https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/sekurlsa/packages/kuhl_m_sekurlsa_wdigest.c#L14
Each credential is stored in a doubly linked list format, as follows:
typedef struct _KIWI_WDIGEST_LIST_ENTRY { |
Note:
Source:
https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/sekurlsa/packages/kuhl_m_sekurlsa_wdigest.h#L14
Credential information is stored at offset 48 of each node, with the following format:
typedef struct _KIWI_GENERIC_PRIMARY_CREDENTIAL |
Note:
Source:
https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/sekurlsa/globals_sekurlsa.h#L36
Each credential selects an algorithm based on the length of the encrypted data:
- If the length of the encrypted data is a multiple of 8, AES is used in CFB mode.
- Otherwise, 3DES is used in CBC mode.
0x04 Program Implementation Details
---
XPN's POC supports extracting credentials from the lsass process on Win10_1809 x64, address as follows:
https://gist.github.com/xpn/12a6907a2fce97296428221b3bd3b394
To make it support Win7/Win8/Windows Server 2008/Windows Server 2008 R2/Windows Server 2012/Windows Server 2012 R2, the following issues need to be considered:
1. Add code to elevate to Debug privilege
BOOL EnableDebugPrivilege(BOOL fEnable) |
2. Determine the operating system version
The previous code can be used here, with the address as follows:
An open-source project
It should be noted that the code does not specifically determine the exact version of Win10, and different Win10 systems have different offsets, for example, Win10_1507 and Win10_1903
Note:
Source:
https://github.com/gentilkiwi/mimikatz/blob/master/mimikatz/modules/sekurlsa/crypto/kuhl_m_sekurlsa_nt6.c#L21-L22
3. Different operating system versions correspond to different offsets
Affects the following four parameters:
- LsaInitializeProtectedMemory_KEY
- int IV_OFFSET
- int DES_OFFSET
- int AES_OFFSET
4. Different operating system versions correspond to different AES and 3DES data structures
Win7:
KIWI_BCRYPT_KEY extracted3DesKey, extractedAesKey; |
Windows 8 and Windows 10:
KIWI_BCRYPT_KEY81 extracted3DesKey, extractedAesKey; |
5. Determine the length of encrypted data in each credential
Use different decryption algorithms and reflect this in the output:
- If the encrypted data length is a multiple of 8, use AES in CFB mode
- Otherwise, use 3DES in CBC mode
The complete code has been open-sourced at the following address:
An open-source project
The code implements credential reading for 64-bit systems, with output identical to mimikatz's sekurlsa::wdigest results, supporting the following operating systems:
- Windows 7 x64/Windows Server 2008 x64/Windows Server 2008 R2 x64
- Windows 8 x64/Windows Server 2012 x64/Windows Server 2012 R2 x64
- Windows 10 1507 (and before 1903) x64
To support Windows 10 1903, add identification for Windows 10 1903 and later versions, along with corresponding offset calculations
To support 32-bit systems, modify the offsets of corresponding variables
0x05 Supplement
---
For systems running Windows Server 2008 R2 and later, plaintext information cannot be stored in credentials under default configurations, thus preventing the export of plaintext passwords. This issue can be resolved by modifying the registry to enable Wdigest Auth, as follows:
cmd:
reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 1 /f |
or powershell:
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest -Name UseLogonCredential -Type DWORD -Value 1 |
After the user logs in again, plaintext information in the credentials can be obtained.
0x06 Summary
---
This article extends XPN's POC to support Win7/Win8/Windows Server 2008/Windows Server 2008 R2/Windows Server 2012/Windows Server 2012 R2,
implementing the functionality of Mimikatz's sekurlsa::wdigest, and documenting the details and process of the program implementation.