_    _____ _       _ _       _
    / |  |     |_|_____|_| |_ ___| |_ ___
 _ / /   | | | | |     | | '_| .'|  _|- _|
|_|_/    |_|_|_|_|_|_|_|_|_,_|__,|_| |___|

2020-10-27

Mimikatz under the hood
=======================

The ability of Mimikatz to extract the NTLM hash of users at runtime from
Windows has always fascinated me. Although alternatives exist (as explored
in previous blog posts), there may still be situations during Red Team
engagements where live credential extraction from LSASS is wanted. In those
situations, defense evasion tactics such as heavily modifying Mimikatz or
using another implementation of Mimikatz are common.

As I'm interested in defense evasion techniques and always wanted to know
more about how Mimikatz work and become better at programming in C, I
figured it was time to try and see how hard/easy it would be implementing
the credential dumping (sekurlsa::logonpasswords) from LSASS that Mimikatz
is probably most known for. I did so by mostly reading the source code of
the Python implementation of Mimikatz by skelsec, Pypykatz, and
implementing roughly the same approach myself in a C++ project, wile almost
solely using C features, instead of C++.

During the process, I also ending up applying a user-mode API hook bypass
technique, of performing direct system calls, instead of using the Win32
API. I did so by copying the opcodes of the NtReadVirtualMemory function
from ntdll.dll (using Ghidra) into my own source code, and calling those at
runtime.

This project was made solely for Windows 10 version 1909, as that was the
OS used during development, but has been tested to also work on version
2004 as well. It should be noted that my implementation is not meant for
production use as-is, and no attempts have been made to ensure stability.
It is not pretty, and I’m sure it can be written both better, more
readable and with less memleaks. It is solely a research project for
learning purposes. That being said, I hope to inspire others to do the
same, as I learned a lot about LSASS and C programming.

In short, my implementation works by:

  1. Opening the LSASS process
  2. Opening LSASS' loaded lsasrv.dll module
  3. Searching for a known pattern in the memory of the lsasrv.dll module
  4. Following certain offsets from the pattern and pointers to the IV and
     AES/DES keys
  5. Searching for another known pattern
  6. Following certain offsets from that pattern to the logon sessions
     stored in memory
  7. Iterating through the logon sessions, and for each logon session
     identify domain, username and encrypted NTLM hash
  8. Decrypt the encrypted NTLM hash using the previously obtained IV and
     AES/DES keys

Screenshot of "MagnusKatz" in action: 
github.com/magnusstubman/MagnusKatz/blob/main/screenshot.png

Interestingly, without any obfuscation or other techniques, only one of
the 71 engines on VirusTotal detect it as being malicious:
virustotal.com/gui/file/626b881ab53049df050ee29e087eadc7


The full source code is available on GitHub:
github.com/magnusstubman/MagnusKatz



# User-mode API hook bypass with Direct Syscalls

I'd like to point out that the user-mode API hook bypass technique used
doesn't really have anything to do with Mimikatz, and is not needed at all.
I simply added it, as I was looking into it at the same time as this
project.

The purpose of bypassing user-mode API hooks is to evade detection that
rely on adding hooks to processes' instances of DLLs for detection, such as
e.g. certain AV/EDR/EPP products. The goal is the same as the goal for the
Dumpert tool, but with a different way of getting the job done.

Here's how it works:

  1. Get the opcodes of the NtReadVirtualMemory function from ntdll.dll,
     e.g. with Ghidra
  2. Allocate memory that is executable, meaning that we can use it to
     hold code that we execute later. 
  3. Move the opcodes into the executable memory
  4. Make a function pointer that points to the executable memory, that
     contains the opcodes from ntdll.dll, such that we can call it the
     same way we call any other function

Relevant source code:

  219 // Opcodes of NtReadVirtualMemory
  220 unsigned char opcodes[] =
  221     "\x4c\x8b\xd1\xb8\x3f\x00\x00\x00\xf6\x04\x25\x08\x03\xfe\x7f
      \x01\x75\x03\x0f\x05\xc3";
  222
  223 // allocate executable memory for opcodes
  224 void* executableMemory = VirtualAlloc(0, sizeof opcodes, MEM_COMMIT,
      PAGE_EXECUTE_READWRITE);
  225
  226 // move opcodes over from stack to executable memory
  227 memcpy(executableMemory, opcodes, sizeof opcodes);
  228 
  229 // make a real function pointer to the memory containing opcodes,
      such that we can actually call it
  230 fpNtReadVirtualMemory NtReadVirtualMemory
      = reinterpret_cast(executableMemory);

Thus, we end up with the ability to read other processes' memory by doing
direct syscalls, instead of using functionality from DLLs.



# Takeaways 

The biggest takeaway for me is that it's worth the effort to become
sufficiently good at programming that custom tools can be made, instead of
using various public tools. Both for the sake of defense evasion but also
for learning more and being better at Red Teaming or penetration testing,
as writing your own tools force you to actually understand the details.
I'm sure that what I learned doing this project will definitely make me
better at the next.

I learned a lot and recommend others to do the same!