Mockingjay Memory Allocation Primitive
Mirrored from WKL Security.
A new post from Security Joes brought attention to a process injection technique previously underutilized in offensive security. The RWX injection primitive, now dubbed “Mockingjay,” offers attackers an advantage to evade unbacked executable memory detection. The core idea behind this technique, reusing RWX regions from legitimate modules, is a valuable alternative to existing memory allocation and protection primitives.
The success of injection into RWX memory depends heavily on the module and memory region selected by the operator. RWX injection is similar to module stomping but may require novel detection techniques in optimal scenarios. Packed binaries are the most common targets for this technique. This blog offers guidance for selecting the appropriate targets, along with an enumeration script and a functional injection template.
To be clear, I did not develop this technique or even popularize it. I had success with RWX injection in the past, and now that it has become popular again, a post seemed justified.
The initial idea for RWX injection appears to have originated from Unknown Cheats. A 2018 post from namazo describes an anticheat bypass to avoid detections that rely on pointers to unbacked memory. A working injector was uploaded to GitHub a year later. Most recently, Security Joes uploaded their blog post on “Mockingjay,” which generated interest in the security community.
Process injection requires three to four primitives:
- Allocate or find memory for code.
- Write code to the memory.
- Change the memory permissions to allow code execution (may not be required if the memory was already executable).
- Execute the code.
RWX injection fulfills steps one and three. You will still need to write code to the RWX region and execute it. Using a module with RWX memory is beneficial because an executable on disk backs it. Module stomping also takes advantage of this attribute, but it’s more likely vendors can validate the data of memory that isn’t meant to be writeable.
To prevent this technique, some anticheat vendors apparently blocked all DLLs with RWX memory regions and made exceptions as necessary. This is an excellent solution for protecting a single application, but it likely isn’t an option for security vendors.
Discovering Module Targets
Potential targets of this attack are PE files with at least one section marked RWX by its characteristic flags in the PE header.
- IMAGE_SCN_MEM_EXECUTE - 0x20000000
- IMAGE_SCN_MEM_READ - 0x40000000
- IMAGE_SCN_MEM_WRITE - 0x80000000
We can search for this manually with a tool such as PE-bear. The “Characteristics” column of the “Section Hdrs” tab conveniently lists the value for each section and translates it to the familiar “RWX” format.
The manual process is slow, but we can automate it using the pefile Python package. This programmatic method can also check whether the module is signed and retrieve the region and raw data sizes. We uploaded a Python script to GitHub with these capabilities.
The best option would be a signed DLL with enough space to hold shellcode and only a minimal amount of raw data. However, any PE file with an RWX memory region may work. These criteria only reduce the likelihood of detection or instability and improve operational usefulness. It’s important to consider the effects of loading a module to ensure process safety. For instance, if a target DLL is injected, its DllMain function may run and impact the host process.
No affected PE files were found in C:\Windows\System32 on Windows 11, but we identified signed third-party modules that met the criteria.
- ISSetup.dll (.rsrc) - 135KB
- PEbiosinterface32.dll (UPX0) - 4.3MB
- libnxusb.dll (.textbss) - 1MB
- GoTo Opener.exe (UPX0) - 839KB
- Spotify.exe (.data) - 6.8MB
PEbiosinterface32.dll is similar to the example in namazo’s blog post. Any PE file packed with UPX (and likely any other packer) is a potential target for the technique.
You can find our local and remote injection templates on GitHub.
RWX injection is straightforward once you have identified a suitable target. In either local or remote injection, the process is as follows.
- Load the identified module into a target process.
- Calculate the virtual address of the RWX section using the module base address and RVA of the target section.
- Copy shellcode to the RWX section.
- Initiate execution of the shellcode.
As you can tell, the methodology has room for substantial variation. Our templates demonstrate a simple method for testing, but the primitives used for module load, memory write, and execution could easily be replaced. The code shows the RWX injection technique but likely won’t bypass any security products without modifying the other primitives.
Although not a new concept, RWX injection has been overlooked by security professionals until recently. Awareness of public techniques is crucial to ensure security vendors effectively address them. While this memory allocation and protection primitive may be difficult to detect, there are still opportunities for defenders to identify execution methods or post-exploitation activity.