Module Stomping for Shellcode Injection

Code Injection

Overview

Module Stomping (or Module Overloading or DLL Hollowing) is a shellcode injection (although can be used for injecting full DLLs) technique that at a high level works as follows:

  1. Injects some benign Windows DLL into a remote (target) process

  2. Overwrites DLL's, loaded in step 1, AddressOfEntryPoint point with shellcode

  3. Starts a new thread in the target process at the benign DLL's entry point, where the shellcode has been written to, during step 2

In this lab, I will inject amsi.dll into a notepad.exe process, but this of course could be done with any other DLL and process.

Pros

  1. Does not allocate RWX memory pages or change their permissions in the target process at any point

  2. Shellcode is injected into a legitimate Windows DLL, so detections looking for DLLs loaded from weird places like c:\temp would not work

  3. Remote thread that executes the shellcode is associated with a legitimate Windows module

Cons

ReadProcessMemory/WriteProcessMemory API calls are usually used by debuggers rather than "normal" programs.

ReadProcessMemory is used to read remote process injected module's image headers, meaning we could ditch the ReadProcessMemory call and read those headers from the DLL on the disk.

We could also use NtMapViewOfSection to inject shellcode into the remote process, reducing the need for WriteProcessMemory.

Code

Demo

Below shows the technique in action - amsi.dll gets loaded into notepad and a reverse shell is spawned by the shellcode injected into amsi.dll AddressOfEntryPoint:

Observation

Note how powershell window shows that amsi.dll is loaded at 00007FFF20E60000 and it's DLL AddressOfEntryPoint point is at 00007FFF20E67E00.

If we look at the stack trace of the cmd.exe process creation event in procmon, we see that frame 9 originates from inside amsi!AmsiUacScan+0x5675 (00007fff20e67f95) before the code transitions to kernelbase.dll where CreateProcessA is called:

Procmon logs

If we inspect notepad.exe threads, we can see thread 7372 with a start address of Amsi!AmsiUacScan+0x54e0.

If we inspect that memory location with a debugger, we see it resolves to Amsi!DLLMainCRTStartup and it contains our shellcode as expected:

References

Last updated

Was this helpful?