Credentials Dumping
Living-off-the-Land Binaries
rundll32.exe C:\Windows\System32\comsvcs.dll MiniDump PID lsass.dmp fullGlobfuscation
&$env:???t??r???\*2\r[t-u]???[k-l]?2* $(gi $env:???t??r???\*2\c?m?[v-w]*l | % {
$_.FullName }), `#-999999999999999999999999999999999999999999999999999999999999
999999999999999999999999999999999999999999999999999999999999999999999999999999
999999999999999999999999999999999999999999999999999999999999999999999999999999
99999999999999999976-decoy $(gps l?a*s).id c:\t??p\dmp.log full;MiniDumpWriteDump
To use the MiniDumpWriteDump API, we need a HANDLE to lsass.exe. Assuming we got such a handle, we do the following:
Create a new file and get its handle.
Invoke the MiniDumpWriteDump API on the
lsass.exehandle and the file handle.
However, there is a variant that doesn't require writing to disk at all - we can save the entire dump to a memory buffer! To achieve that, we:
Create a buffer with some initial size.
Create a structure of type
MINIDUMP_CALLBACK_INFORMATIONand specify a callback function (and context).Invoke the MiniDumpWriteDump API on the
lsass.exehandle and the callback pointer.The callback accumulates the data from the minidump argument upon receivng a
IoWriteAllCallbackcallback type. Ths might require us to dynamically enlarge the buffer with theHeapReAllocAPI.
There are two variants to get a handle to lsass.exe:
Using OpenProcess
This is the most direct and "normal" way of fetching the lsass.exe process handle:
Finding the process ID of
lsass.exewith CreateToolhelp32Snapshot API, as well as Process32FirstW and Process32NextW API calls.Use the OpenProcess API. For doing a Minidump, we will require the
PROCESS_QUERY_INFORMATIONandPROCESS_VM_READaccess flags.
Stealing An Existing Handle
Since OpenProcess is usually heavily monitored by security products, there is a sneakier option - duplicating an existing handle to lsass.exe some other process might have.
Similarly to the OpenProcess approach - we fetch the lsass.exe PID the usual way (CreateToolhelp32Snapshot and friends).
However, here our approach would be finding an existing handle to lsass.exe with sufficient access flags:
We call ntdll!NtQuerySystemInformation with the information class
SystemHandleInformation(defined as 16) with a sufficiently large buffer. As of now, that information class is officially undocumented (but can be found here).We treat the buffer as a SYSTEM_HANDLE_INFORMATION pointer (again officially undocumented).
For each handle, we skip if it belongs to the
lsass.exeprocess ID itself, to our own process or the SYSTEM process ID (4).We call
ntdll!NtDuplicateObjectto duplicate the handle. In this call we also specify the required access flags.We call
ntdll!NtQueryObjectwith typeOBJECT_TYPE_INFORMATION_CLASSto get the duplicated handle type and make sure it's aprocesshandle type.We call the QueryFullProcessImageNameW API and validate it's indeed
lsass.exe(note it's a full name so extra parsing is required).If all checks pass - we got a handle to
lsass.exethat we've just duplicated! Otherwise, we clean up used resources and try a different handle.
Note: a running OS is not guaranteed to have an open lsass.exe by some process with sufficient access flags - if that's the case we can always revert to running OpenProcess as usual. However, all the native APIs (and especially the handle duplication) make it way stealtier than calling OpenProcess directly.
PssCaptureSnapshot-based dumping
Similarly to the Minidump approach, we get a handle to lsass.exe by means of OpenProcess or handle duplication, but this time also with PROCESS_DUP_HANDLE access flag.
Then, we call the PssCaptureSnapshot API which returns us a pseudo-handle of type HPSS. As the name suggests, that handle captures a "snapshot" of the lsass.exe state (includng memory), and then could be used just like a normal handle to ReadProcessMemory or MiniDumpWriteDump.
The approach doesn't seem advantageous, but it's very reliable due to how it captures a snapshot (as opposed to direct ReadProcessMemory which has an inherent race condition, for example).
SilentProcessExit-based dumping
This technique relies on a Windows mechanism that can invoke a callback once a process exits, as well as fooling the system into thinking the process indeed exited. To implement this technique, we do the following:
Get an
lsass.exehandle (either viaOpenProcessor by handle duplication).Write to the registry under the Image File Execution Options (IFEO) key
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe. Value name isGlobalFlagwhich is of typeDWORD, and we set it to the value of0x200, which corresponds to enabling the Silent Process Exit monitoring feature.Write to the registry again, this time under
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe, the value is aDWORDwith the nameReportingModeand the value of2, which instructs the OS to perform a full memory dump.Set the value
LocalDumpFolderunderHKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exeto point to a folder we'd like to dump the memory to, and set the valueDumpType(which is aDWORD) to be2(local memory dump).Call
ntdll!RtlReportSilentProcessExit, which is an undocumented function that would report that a silent process exit should occur for a given process handle (lsass.exein our case).
At that point, our dump file has been created at the folder we assigned, and we can delete all added registry entries.
Shtinikering
Originally done by Deep Instinct, this method requires running as SYSTEM (can be validated using the GetTokenInformation API if necessary).
Just as before, we fetch a handle to lsass.exe with either OpenProcess or duplication, and then dumps using Windows Error Reporting (WER)!
To do this, we report an exception to WER via ALPC:
We get some thread of
lsass.exe(can be done with theCreateToolhelp32SnapshotAPI) - and save its thread ID.We create two events that we will pass to the ALPC message: a "recovery event" and a "completion event".
We create a memory-mapped file that will contain a structure that'd maintain exception information (requires writable memory). That structure contains information for WER, including the events that we created, as well as exception information, process ID, the failing thread ID and so on.
We make sure WER service starts using Windows Notification Facility (WNF) using
ntdll!NtUpdateWnfStateDataandntdll!EtwEventWriteNoRegistration.We wait for WER to be available using
ntdll!NtWaitForSingleObjecton\KernelObjects\SystemErrorPortReady.We connect to ALPC port
\WindowsErrorReportingServicePortviantdll!NtAlpcConnectPortand then send to message viantdll!NtAlpcSendWaitReceivePort.The dump will be under
%LocalAppData%\CrashDumps- we take the freshest dump.
Last updated