Heaven's gate and all the goodies
Last updated
Last updated
As a Practical Example for EarlyBird APC-Injection from Malware Evasion Through Injection part2 Episode of REM-Essentials, I found this interesting sample that's not only using EarlyBird APC Injection, but a whole lot of other tricks, since I found a niche in analyzing code, let's dive into this mess.
Sample sha256: 9173b5a1c2ca928dfa821fb1502470c7f13b66ac2a1638361fda141b4547b792
This Sample uses Early-Bird to inject malicious payload into a legitimate windows binary svchost.exe
in this case.
The sample starts by injecting into explorer.exe
, it drops two binaries, one of the injected payloads in turn injects into svchost.exe
.
1→
Firing the Sample in dnSpy
we find it's real name: phpv1ph20_cr.exe
, stepping into the Main > InitializeComponent()
which are just basic Methods for creating Applications in C#
inside InitializeComponent()
we see basic layout manipulation methods, then some objects and functions with encrypted names, which in short does the following:
Gets a .bitmap
image from the resources that's most likely is an encrypted executable. [1`]
Gets another resource of size 19bytes
, which we can guess is a key. [1``]
Saving the .bitmap
image into a byte array, and passing the array along with the key into a function that returns a stocuUXlBXJEyFZuoaR
object that loaded as Assembly and invoked[1```]
→ resources to load and decrypt.
2→
Setting breakpoints on each Assembly.Load()
/ .Invoke()
and running the debugger[2]. We hit the breakpoint before Assembly.Load()
and with checking the stocuUXlBXJEyFZuoaR
object we see an executable, that's definitely the .bitmap
image been decrypted[2``].
3→
We will Step Through the Assembly.Load()
and let the program load the assembly, Stepping Into .Invoke()
and it's relevant InvokeMethodFast()
method, we find ourselves executing inside Program()
function in stub.exe
[3`] . Checking this function we see it performs some anti-analysis checks[3``] and in turn loads a resource and by checking the resources we find a MAIN.bin
binary file that's 41kb
[3```], so we dump this out and jump to the next analysis step.
MAIN.bin
The Threat Intel step gave us the information that the sample injects into explorer.exe
, drops two PEs into it, and the injected payload inside explorer.exe
injects the final payload into svchost.exe
MAIN.bin: 6ad3ca9bb4e9d96c2507934486c013c1f58c0030d503d3b10b58e5d23209429e
MAIN.bin
has sections with self-modifying code[1], two exports RemoteMain()
, TestSvchost()
[1``] and loads of interesting strings[1```].
2→
MAIN.bin
This sample is pretty tricky and with a mixture of injection techniques/ execution and anti-analysis tricks, I will set a simple flow of it's execution and based on it we will debug through the sample with these major steps in mind.
A]
1 →
After querying running processes with CreateToolhelp32Snapshot()
/ Process32First()
/ Process32Next()
and running the resulted process name pe32.szExeFile
over some mangling, comparing it with this string c48947ea
, the sample opens explorer.exe
to obtain a handle to it.
2 →
Through an interesting wrapper function, the sample figures out if it's running under Wow64
or not. It first loads the IsWow64Process()
address[2`] then executes it over it's own context[2``].
passing the hProcess
of explorer.exe
, this wrapper function is executed again[2```], with return value of ZERO
meaning the specified process _ target process explorer.exe
_ is in fact a 64bit process. that's a good guess that maybe the sample would execute the payload through Heaven's Gate which we will get to later.
3 →
The sample then allocates memory at [0x000F0000]
in it's own context[3`], then through another wrapper function, an address inside it's memory space is passed, so we might expect a write operation next[3``].
the memory at [0x000F0000]
is populated in chunks through a typical memcpy()
with a binary in the sample's context at [0x013B8000]
, Next it parses the dropped payload's PE header to check if it's 32bit/ 64bit binary.
dump1: 47092f385933db565b2172cf767c13eaae2724e70e7a96f014a42837fb2a2fa6
After populating the allocated memory with the interesting dump the sample will virtually protect it, at this point we would save it and resolve the imports by fixing mapped offsets.
quickly analyzing the dumped binary we see it's 64-bit code with the name [dll.bin]
, with 6 exports and self-modifying sections.
4 →
For the next payload to be dropped, the sample creates a named file mapping object with CreateFileMappingA()
with the name wow32_64
which is a native windows image, yet it passes no hFile
to the file mapping object, but must pass a MaxSizeHigh
which interestingly enough was [
DBDC
``→``
56kb
]
the sample's own image size, the name of the file mapping object underlying section would be the same wow32_64
. and a section is created.
next the file mapping object along with the created section is mapped in the sample's context by a call to MapViewOfFile()
that in turn calls NtMapViewOfSection()
, we end up with a mapped empty 56kb
section in the sample's process memory.
next the mapped memory block [0x200000]
is allocated using memcpy
again, with the sample's binary at [0x13D0000]
size of [
DBDC
``→``
56kb
]
.
The sample then checks the written binary if it's 64bit
or 32bit
by parsing the PE header's IMAGE_FILE_HEADER
Machine value [
14C
``/``
8664
]
The file mapping object is then unmapped along with the underlying section through NtUnmapViewOfFile()
``→``
ZwUnmapViewOfSection()
5 →
Quickly analyzing the mapped memory block we see it's identical to the sample we are running MAIN.bin
yet it actually contains another executable at offset [0x8000]
that is the first dump we dumped at step3, with old school binary diffing, we see that this version of MAIN.bin
has unpacked what seemed to be an encrypted binary from the sample we're running to a copy of itself, maybe explaining the different PE parsing checks over that mapped memory region.
dump2: c6a7567623a6630866337231e6e9601ae6c7560820ba4f97efe8fa9d2b200f9c
B]
6 →
The Injection into explorer.exe
involves Heaven's Gate
which is a technique to inject into 64bit processes from 32bit payloads, this was clearly obvious in this sample as it made couple of IsWow64Process() checks over itself and the opened process explorer.exe
.
Since we didn't go this far to only get this far, let's talk a bit about Heaven's Gate explaining how it's done in this sample.
The Sample starts by figuring out it's environment, if it's running on a 32bit or 64bit system, the loader itself is 32bit running under Wow64, again Wow64 is considered a sandbox/interface to run 32bit code inside 64bit system without modifying the 32bit code. for every process/ application there is a SysWow64 version of it.
In Step2, the Sample figures out the environment it's running on [Wow64 → payload is 32bit] and the target process [explorer.exe
→ 64bit process], so then it would literally tunnel 32bit code to be executed in 64bit process as we will see next to allocate a block of memory, drop a payload inside it and pass execution to the dropped payload inside the injected target 64bit explorer.exe
process.
In order to access the 64bit environment of a process from a 32bit payload, the sample needs to use the 64bit version of DLLs and functions and the appropriate code segment selector, for both 32bit code and 64bit code there are two different segment selectors [0x23
for 32bit code/ 0x33
for 64bit code].
This process happen as follows:
To witness this happen, we will debug explorer.exe
in a 64bit debugger, and as a starter we will set a breakpoint on NtTestAlert()
to catch if the injected payload is executed as an APC:
Execution flow for injecting into explorer.exe
6` →
A] Allocate memory inside explorer.exe
B] Write payload inside the allocated memory block at &allocated_mem
C] Pass execution to payload inside explorer.exe
with RtlCreateUserThread()
specifying the StartAddress
of the created thread[C], which if we jumped to inside the injected payload in explorer.exe
we'd find another jump to a function in the payload[C``].
7 →
Now we get to the fun part where the main process _dropper's process_ passes execution to the payload inside explorer.exe
and we're left with a dropped payload inside a crucial process like explorer.exe
, things will get messy because we are debugging a process that's highly expected to have interrupts, so events outside debugging the intended payload will cause us mega headaches.
Once executing the RtlCreateUserThread()
call, the sample's process exits and we hit NtTestAlert()
breakpoint[7] in the debugged explorer.exe
that let's our payload _that is a queued APC_ execute, which is in fact the location specified to jump at for the created thread[7``].
8 →
Now we're executing from the injected payload inside explorer.exe
and since we're debugging the process, the only active thread now is the malicious thread, so right now anything else depends on explorer.exe
freezes since we're debugging it.
Viewing the strings inside the injected payload, we put breakpoints on interesting strings, we also see wow32_64
the same windows native image the original dropper used to write the second payload into.
C]
9 →
Following execution inside the dropped payload. we see it parses the PE header of the injected payload, resolves some APIs to use, next it constructs the path to 32bit version of svchost.exe
inside sysWow64
→ C:\\windows\syswow64\\svchost.exe
Next the payload creates the 32bit svchost.exe
process.
right now, we would set breakpoints on CreateProcessInternalW(), NtResumeThread(), RtlCreateUserThread(), NtQueueApcThread()
.
9 →
On run we hit CreateProcessInternalW()
breakpoint with the path to 32bit svchost.exe
, creating the process in a suspended state.
The payload then tries to allocate memory inside the spawned svchost.exe
with different calls to VirtualAllocEx()/ WriteProcessMemory()
to eventually drop the final payload.
Confusingly enough the sample just stops there, the malicious thread inside explorer.exe
exits leaving a suspended unmodified svchost.exe
, so I guess the injection into svchost.exe
involves some anti-analysis checks beforehand.
We should expect svchost.exe
to be injected with the final payload, with the main thread executed as an APC, dropping two files on disk.
Interestingly enough while debugging I opened Process Hacker under the infected explorer.exe
_ that I am still debugging _ and I suddenly hit NtQueueApcThread()
breakpoint in the payload, and I end up with a trojaned processhacker.exe
🤔🤔🤔
Yet it was injected by the first payload [64bit payload] that's supposedly will do the same thing the dropper did?? I really have no idea what's happening here.
Yet from analyzing the dropped payloads' assembly we find both main exports are called via NtQueueApcThread()
/ NtResumethread()
, though we couldn't catch the APC into svchost.exe
, we can quite understand how it happened.
For a closer look into the EarlyBird-APC Injection and how it's done over the resuming of a newly created/suspended thread, CyberBit has patched the same Sample, to reproduce the EarlyBird-APC Injection in a simple way to catch it on the other end svchost.exe
Here is the full synopsis of the technique:
https://www.cyberbit.com/blog/endpoint-security/new-early-bird-code-injection-technique-discovered/