Enumerating RWX Protected Memory Regions for Code Injection
Code Injection, Defense Evasion
Injecting and executing shellcode from a local or target process requires memory where the shellcode could be written to, read from and executed.
Some shellcode injection techniques allocate PAGE_EXECUTE_READWRITE
memory block, fill it with shellcode and create a thread pointing to that shellcode. It's not a very common thing for bening applications to do and this is something AV/EDR solutions may punish you for if they catch you doing it.
Some techniques allocate PAGE_READWRITE
first, write shellcode to the allocated memory, protect it with PAGE_EXECUTE_READ
and then execute it, which means that at no point in time there's an RWX memory block in the target process. It is a bit stealthier and may help one sneak past AVs/EDRs.
What both techniques have in common is that they still need to allocate and protect memory (RW -> RX or RWX). Having said that, it is possible to brute-force/enumerate currently running target processes on the compromised system - search through their allocated memory blocks and check if any those are protected with RWX, so we can attempt to write/read/execute them, which may help evade some optics.
Overview
In this lab I will write a simple program that will:
Loop through all the processes on the system
Query each process's memory information
Loop through all allocated memory blocks in each process
Check for any memory block that is protected with RWX && is private && is committed
If the above condition is met
Print out address of the memory block
Write shellcode to that memory block
Create a remote thread that points to the shellcode written in the above step
Running the Code with breakpoint set on line 31 will be hit if the conditions on line 27 are met. The conditions we are checking for are:
Once the breakpoint is hit, we can see that the memory region 27c727a0000 is RX protected, is private and commited and now contains our shellcode (starting with bytes fc 48 83 e4) :
If you noticed and were wondering...
Why were we able to WRITE to an RX memory region?When you call
WriteProcessMemory
and tell it to write to memory that is read-only, theWriteProcessMemory
succeeds. How can that be?Because
WriteProcessMemory
tries really hard to please you.As I noted some time ago, the primary audience for functions like
CreateRemoteThread
andWriteProcessMemory
is debuggers. And when debuggers try to patch memory, it’s often for things like patching in a breakpoint instruction or doing some edit-and-continue magic. So theWriteProcessMemory
tries really hard to get those bytes written. If the page is read-only,WriteProcessMemory
temporarily changes the permission to read-write, updates the memory, and then restores the original permission.“No need to thank me, just trying to help.”
There is a race condition if the target process happens to be manipulating the page protection at the same time that
WriteProcessMemory
is. But that’s okay, because the intended audience is debuggers, and debuggers will freeze the target process before trying to edit its memory.There is no security hole here, because the way the
WriteProcessMemory
function changes the page protection is basicallyVirtualProtectEx
, so it will succeed only if you already could have modified the protections yourself anyway. If you didn’t have permission to change the protections, thenWriteProcessMemory
‘s attempt to change the protections would fail too.Source: https://devblogs.microsoft.com/oldnewthing/20181206-00/?p=100415
Demo
Let's build the program and run it - we can see we got some meterpreter shells.
The below provided code is a dirty POC and may crash certain processes and the Visual Studio banner appearing in the above GIF proves it - the shellcode got injected into Visual Studio (devenv.exe) that crashed and restarted itself.
Code
References
Last updated