@gentilkiwi wrote Mimkatz to learn C. We can use it to learn Windows Internals. Let’s start with the most basic command: “Privilege::debug”. It might be overlooked, though we will see how deep the rabbit hole goes. #WindowsInternalsViaMimikatz 🧵1/20
“Privilege::debug” is used as a requirement for many other commands like “sekurlsa::logonpasswords”. We will start by figuring out what it does and then learn where else it can lead us. 2/20
First things first - finding the code behind it. If you know a little C, you should know a string is not enough to invoke a method - some other code has to parse it. 3/20
We don’t care how it is parsed, so we will skip this part and look for a clue what the parser executes by searching the whole project for the string “debug”. 4/20
Bingo! Only one result from Mimkatz code (another from SQLite we can ignore). Next to the “debug” string is the function called by the parser: kuhl_m_privilege_debug. It is a wrapper for a call to kuhl_m_privilege_simple with a const SE_DEBUG. 5/20
SE_DEBUG sounds interesting. What does DuckDuckGo have to say about it? The first two results sound interesting! The first link on MSDN has a vague description of SE_DEBUG_NAME: “Required to debug and adjust the memory of a process owned by another account.” 6/20
The second (by @ChenCravat) is more descriptive: “To debug processes owned by other users, you have to possess the SeDebugPrivilege privilege… If you let users... then they can debug processes owned by System, at which point they can inject code into the process...” 7/20
We know some Mimikatz commands interact with lsass.exe. Lsass.exe runs as a System user while Mimikatz (usually) runs as an admin user. Having Debug Privilege allows Mimikatz to inspect lsass.exe memory. 8/20
If you want to experiment, try running Mimikatz as a System user (using PSExec tool) vs. Admin user and running commands without “Privilege::debug”. Will they work? 9/20
With that out of the way, let’s dig deeper! Examining kuhl_m_privilege_simple, it is also a wrapper function for RtlAdjustPrivilege. Looking for its definition, we can find it is an external function. 10/20
What DDG has to say about RtlAdjustPrivilege? The first result is not what we were looking for. The second might explain the first. The third, from PInvoke, is a source I like - it documents (undocumented) functions’ signature and what DLL they can be found (ntdll.dll). 11/20
Let’s examine RtlAdjustPrivilege in IDA (a free version is available for you to download). After finding RtlAdjustPrivilege, click the Tab key to open the Pseudcode window. It is easier than reading assembly (with a risk of it making some mistakes). 12/20
Start with what you know, name the parameters (select the parameter, click ‘n’ to rename it). The function first checks CurrentThread's value and decides if it needs to open a thread or a process token (-1 is a pseudo-handle to current process while -2 is for current thread). 13/
The second parameter is the desired access for the Token Handle. It is easier to dissect it in hex (select the “40i64” number and click ‘h’ to transform to hex). Since it should represent a bit field, the value 0x28 is a combination of TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES. 14/20
The rest of the function is a wrapper around NtAdjustPrivilegesToken. It means the SE_DEBUG privilege is set in the kernel and probably queried there as well. Ready for the last plunge into the depths of Windows for our journey? Load ntoskrnl.exe in IDA! 15/20
The Kernel is huge. Let’s try to focus our research. What fails first in Mimikatz if we neglect using “Privilege::debug”? When using the “sekurlsa::logonpasswords” command, we get an error “ERROR kuhl_m_sekurlsa_acquireLSA ; Handle on memory (0x00000005)”. 16/20
If you dig into the code, the failing function call will be OpenProcess. Using similar methods described before, they will lead us to another syscall: NtOpenProcess. Now we have a place to start our kernel research! 17/20
NtOpenProcess is simply a wrapper around PsOpenProcess. We don’t need to understand every line of code. We name the parameters we know and flip through the code to find the smoking gun: SeDebugPrivilege, followed by a call to SePrivilegeCheck(). 18/20
Select SeDebugPrivilege and click ‘x’ to find all references to it. Now we can find all the functions that might examine it before performing their action: PsOpenThread, PsOpenProcess, NtSetInformationProcess, NtSetSystemInformation, NtGetNextThread,... 19/20
Another interesting insight: There is only one place where this value is set and not fetched (a mov opcode into cs:SeDebugPrivilege). SepVariableInitialization() sets many values. It sets SeDebugPrivilege to 20 (0x14). Remember SE_DEBUG? Guess what its value is? 20/20
Share this Scrolly Tale with your friends.
A Scrolly Tale is a new way to read Twitter threads with a more visually immersive experience.
Discover more beautiful Scrolly Tales like this.