Let's analyze NOBELIUM APT29 – Part 2
Last updated
Last updated
Welcome back, fellow analysts!
I recently began a deep-dive analysis series of the notorious APT29 NOBELIUM attack, and my goal was to shed light on some of the details that I thought were missing from the other technical blogs and reports out there as well as provide my insight into the techniques adopted by the threat actors.
In my first part of the series, we looked at how the threat actors have obtained Initial access into the victims and how they were trying to leave very minimal IOCs behind by removing any created registry entries after they have loaded in memory their next stage.
In this part of the series, we are going to continue from where we last left off and, as promised, conduct dynamic shellcode analysis to dump from memory the next stage of the malware infection chain(NativeZone DLL) and examine closely the export of interest, which will lead to the next stage of the attack.
Shellcode is a simple code very carefully crafted by hand, usually written in assembly to be very minimal in size so that it can fit into a buffer. Since it's not a valid PE, a debugger such as x64dgb or Windbg can't just load it in memory as it does with PE files since it lacks the necessary headers that tell the loader how to properly map the file from disk to memory. This is where the need for custom tools that can wrap up the shellcode into a valid PE file, and set the PE entry point to be the start of our shellcode comes in, another way for debugging shellcode is loading it into the memory of a host process and then transfer execution to.
I decided to go with the other way and for that, I found a great and easy-to-use tool called sclauncher, that was recently created by the well-known researcher Josh Stroschein, very thankful to have people like him in our community.
You can go there to the GitHub repo, download it, and just give it a try. The way it works is as I explained earlier, it creates a host process for the shellcode and sets a breakpoint at the start of the shellcode so you can start your debugging from there.
To begin with, you can fire up Windbg, it's is a very capable debugger in that it allows you to attach to a running process, and debug an executable with and without options.
In our case we are going to open up sclauncher for debugging in Windbg, with the following options:
The -f option is for specifying the shellcode file to load, and the -bp is to break at the start of the shellcode.
I would like to clarify that when using Windbg, it is important to specify the full path to the shellcode file we want to load. If you don't do this, your shellcode will not be able to load properly, unless you have specified the starting directory to be the same as your shellcode file, which I did not do in my case.
Now we can go ahead and start debugging!
Now in the command window of Windbg, we can type in g for go to run till we hit our shellcode breakpoint, and then we can start from there.
Once we do this, it should hit the 0xCC interrupt that was placed right before the first instruction of shellcode(that's the way sclauncher works btw).
Having made it that far, now this looks familiar from the previous post, the function at 01FB0032 is the main function of shellcode that we've statically looked at before.
Remember from the previous blog post that the first argument to the function at 01FB0032, which is our case whatever is in eax is the base address of our DLL, and that the second argument is the hash value for the export function of interest. The third and fourth arguments seem to be an array of 4-byte key values and the size of the key, while the last one is just a flag. Now with that in mind, let's dump that DLL pointed to by eax from memory, save it to disk, and then go to that export and examine it further. The command for this is:
We need to give it the full path of where to dump the file and then the address range we want to dump.
To determine the starting address of the range, we can simply use the value stored in the eax register. However, determining the ending address can be a bit tricky since we don't know the exact size of the DLL. One way to do this is to scroll down until we reach the end of the DLL and use that address as the ending address.
Having dumped the DLL from memory to disk, let's go ahead and take a look at it in IDA.
Now that we know from the shellcode analysis that one of the exports within the DLL is called. The question is which of the exports?
The DLL is highly obfuscated in many ways that we'll get to as we go through, but one of these ways is the presence of junk|dead code, actually, there's tons of functions exported by the DLL, but only one of these is called, and most likely that's the one that's gonna stage up for the next component of the malware.
After following the function call in Windbg to the entry point and comparing it to the other exports in our DLL, I discovered that the export being called was the "EqualService" function.
Junk code is a common anti-reversing technique used by malware to hinder the analysis process and make it a little more challenging.
As we navigate through the function, we come across numerous "junk function calls", irrelevant floating point operations, and code that seems purposeless and adds nothing to the malware's functionality. This code was intentionally inserted to divert the attention of analysts from recognizing or identifying the actual code.
The picky eye will recognize that the actual code inside is just copying encrypted code blocks of fixed sizes from the .rdata section to the .bss section as demonstrated here.
This goes on until all encrypted code blocks are copied from the ".rdata" section to ".bss".
Upon examining all write cross-references to the ".bss" section, we can see that there are 7 encrypted code blocks of varying sizes being copied in the same manner explained earlier.
The last reference to ".bss" is the setup for the xor decryption of the already copied encrypted code.
Since the key is read 4 bytes at a time from what appears to be an array of 4-byte xor keys, I wasn't able to automate the process of decrypting the code, so I decided to go through the decryption process in Windbg and then dump the decrypted code from memory the same way we dumped the DLL.
That we've finally dumped the final payload from memory, I submitted it to VT to get a little bit of intel on it, and it's got 53 out of 71 detections as a CobaltStrike beacon.
Now that we know what the final post-exploitation module is, and with a little bit of googling, i came across this amazing CobaltStrike parsing tool, which automates the process of extracting every bit of configuration information from the beacon, that tool was contributed by SentineLabs, and you can find it available at
Running that parse_beacon_config.py script against our beacon we get:
We see it's an HTTPS beacon communicating with the C2 server midcitylanews.com/news/update/aaa over port 443.