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

---

  1. Elevate to Debug Privileges
  2. Obtain lsass Process Handle
  3. Enumerate handles of all modules in the lsass process, locate the memory positions of wdigest.dll and lsasrv.dll
  4. Retrieve InitializationVector, AES, and 3DES values from lsasrv.dll for decryption
  5. 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 {
ULONG size;
ULONG tag; // 'MSSK'
ULONG type;
ULONG unk0;
ULONG unk1;
ULONG bits;
KIWI_HARD_KEY hardkey;
} KIWI_BCRYPT_KEY, *PKIWI_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 {
ULONG size;
ULONG tag; // 'MSSK'
ULONG type;
ULONG unk0;
ULONG unk1;
ULONG unk2;
ULONG unk3;
ULONG unk4;
PVOID unk5; // before, align in x64
ULONG unk6;
ULONG unk7;
ULONG unk8;
ULONG unk9;
KIWI_HARD_KEY hardkey;
} KIWI_BCRYPT_KEY81, *PKIWI_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 {
ULONG cbSecret;
BYTE data[ANYSIZE_ARRAY]; // etc...
} KIWI_HARD_KEY, *PKIWI_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 {
struct _KIWI_WDIGEST_LIST_ENTRY *Flink;
struct _KIWI_WDIGEST_LIST_ENTRY *Blink;
ULONG UsageCount;
struct _KIWI_WDIGEST_LIST_ENTRY *This;
LUID LocallyUniqueIdentifier;
} KIWI_WDIGEST_LIST_ENTRY, *PKIWI_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
{
LSA_UNICODE_STRING UserName;
LSA_UNICODE_STRING Domaine;
LSA_UNICODE_STRING Password;
} KIWI_GENERIC_PRIMARY_CREDENTIAL, *PKIWI_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)
{
BOOL fOk = FALSE;
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
fOk = (GetLastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return(fOk);
}

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.