Written by Mike Sweeney, Senior Incident Response/Forensics Analyst at Critical Defence. Mike has achieved a GREM certification from SANS GIAC twice, and holds a Bachelor of Science degree in Information Security and Forensics from the Rochester Institute of Technology.
Welcome back to our malware analysis blog series! Today we’re going to get into some assembly code, unpacking, and review a mistake that the authors of this Kuluoz malware made – which greatly helps our analysis!
Part 1 is posted here: https://criticaldefence.com/malware-analysis-part-1/
Dynamic Analysis and VirtualAlloc
So let’s go back and look at VirtualAlloc’s definition.
If we research VirtualAlloc on Microsoft’s website, we see the full writeup on it: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366887(v=vs.85).aspx
Specifically, the documentation states that VirtualAlloc “Reserves, Commits, or changes the state of a region of pages in the virtual address space of the calling process. Memory allocated by this function is automatically initialized to zero.”
But what does this mean in the context of malware analysis? To explain, we’ll take a look in x64dbg and IDA Pro.
Let’s start by opening the Kuluoz sample in x64dbg. We’ll then go to the Debug dropdown menu, and select Run Until User Code (in this case, the ‘user’ is our sample), as we don’t care to reverse ntdll.dll.
A quick tip: Analysis gets confusing quickly! X32dbg helps us orient ourselves through the window’s title bar.
The “Module:” field tells us what binary we’re currently reversing. If it’s ever not your malware sample, then you probably want to Debug->Run Until User Code, which will let the processor execute until we return to our malware code.
We’ll Run Until User Code a couple of times before we land at the Original Entry Point, where we begin to execute code inside of our malware.
To set a breakpoint on VirtualAlloc, run the command “SetBPX VirtualAlloc” in x64dbg. The command box is at the very bottom of the x32dbg window, on the lefthand side:
Then run the sample three times. Then choose “Run to User Code”.
Now, VirtualAlloc is helpful because it returns the address of the newly created region in memory in register EAX. In my case, we’re now holding a value of 00220000 (it may be different for you). If we right click on this, we can choose “Follow in Dump”->”Dump 1”. The “Follow in Dump” feature lets us review the contents of memory at specific addresses, given that they’re valid. Once we click “Follow in Dump”, we see…
VirtualAlloc simply allocates memory. It doesn’t populate it.
Run the sample again, and we’ll hit VirtualAlloc’s breakpoint again. Then return to user code again.
Now note that EAX contains a different address, in my case 0023000, which is the address of the newest memory region. Right click on EAX, and open that address in Dump 2.
Still no content! X32dbg gives us a handy feature though. It lets us set a breakpoint on a certain memory address, and when it is written to, it’ll stop execution so we can investigate. Right click on 00230010, choose Breakpoint->Hardware, Access->Byte. This sets a breakpoint on this address. Then run the code. You’ll notice that in Dump 2 suddenly fills with what sort of looks like…
… is that an MZ header? It is! Sort of. It’s obfuscated, and not directly usable.
Run the malware again, hit the VirtualAlloc breakpoint again, then return to user code. If we check EAX again for the handle to the new memory location, it now contains 00240000. Let’s follow this in Dump 3. Region 00240000 contains all zeroes, as we now expect. Set a memory access breakpoint on 00240000, and then continue running the sample. Address 00240000 changes from 00 to 4D. If you look in the ASCII column, we now see ‘M’.
Now, we need to make sure we don’t execute the entire rest of the malware (as trial and error has shown me that this malware doesn’t run VirtualAlloc again after this instance.) What we need to do is set another breakpoint somewhere which will stop us from continuing execution too far.
Run command ‘SetBPX VirtualFree’. I had to do some experimentation, but this function, in this case, is a reliable point to stop execution. The entire malware has had a chance to unpack, and we haven’t infected our VM yet. Run the malware, and then check the Dump 3 tab.
Dump 3 has now been completely replaced with the full MZ header, followed by a PE Header, followed by… this is a full Windows PE file!
Let’s go back to our memory map and find region 00240000. Right click on it, and choose “Dump Memory to File”.
What just happened?
Let’s start from the beginning.
When we first opened the malware in x32dbg, we used the command SetBPX to set a breakpoint on the VirtualAlloc function call. The malware, because it’s trying to hide it’s VirtualAlloc calls from us, doesn’t call it directly. So we don’t get a breakpoint hit when the malware calls VirtualAlloc, we get a breakpoint hit when Kernel32.dll is calling VirtualAlloc. That’s why we had to continue to ‘Return to user code.’ In this case, the “User Code” is our malware. If we were to explain this as if it were a conversation, it might go something like this:
Malware: “Hey kernel32.dll, I need the address of a function you have. It’s called VirtualAlloc. Can you tell me what address it has?”
Kernel32.dll: “OK cool. It lives at address 0x0A.”
Malware: “OK Kernel32.dll, here’s address 0x0A. Run whatever function lives there.”
Kernel32.dll: “Hey VirtualAlloc, do your thing.”
VirtualAlloc: “Done. Here’s the new memory region’s address. Send this back to your app.”
Kernel32.dll: “Hey Malware, here’s your new memory region.”
Again, this is just the malware attempting to be sneaky with how it calls VirtualAlloc.
Once we figured that out, and then learned that VirtualAlloc returns the memory address of our new memory region in EAX, it was smooth sailing! All we had to do was continue to monitor the addresses that VirtualAlloc was returning via EAX. Eventually, one of the memory regions was filled up with an un-obfuscated Windows PE file.
Then we dumped the memory region to a file. This will give us a new piece of malware to start working with.
Summary and Next Steps
It turns out that Kuluoz is a little bit like a Russian nesting doll. You have to do this VirtualAlloc trick a few more times before you land at the real malware. But – the authors either made a mistake, or there was an oversight.
Quit out of IDA Pro and x64dbg. When we dumped the contents of handle 00240000, we ended up with a .bin file. Let’s open this up in IDA pro and get the lay of the land.
Strangely enough, we only have six imports:
But, it’s the usual suspects.
Here’s the oversight: Open the .bin file we just dumped in a hex editor, and scroll to address 0x950. You’ll see something like this:
That’s a second MZ header inside of our .bin file! Using your hex editor, remove everything up to the magic number, MZ. Save this as a new file, and then open the resulting file in IDA Pro. You’ll land here:
Check out the Strings tab.
This looks much, much more promising! We can see information related to User-agent strings, a registry key, something that looks like a URI (‘/index.php?r=gate&id=’), and for some reason, “For base!!!!!”.
Now, let’s begin analyzing the real sample.
Working with our real sample
Now that we’ve got the real sample open in IDA Pro, what do we do?
First, let’s check out our imports. We have a lot of items present here. Sort this window by Name and review the list.
There’s a lot to go on here. What we focus on and where we go really depends on what we’re after. One important set of imports are all of the items from the WS2_32 library. WS2_32.dll implements code which lets a program access the network. Function WSAStartup is called when the program is ready to use the network.
Now, we can take as much time as we like to go through the code and find out the minutae of the malware and what it does. However, we can also use x32dbg, set some smart breakpoints, and get the indicators of compromise that we’re after.
Dynamically Finding IOCs
If x32dbg is open, close it. Re-open it, but now with our final unpacked sample loaded. Run until user code.
Now that all of the hard work is mostly over with, I advise taking a snapshot of your virtual machine. This will allow us a lot more room for error in case we accidentally ‘set off’ the malware. You’ll know that this has happened when the malware suddenly terminates.
Run the malware. It terminates! What happened?
Look at the code that runs as soon as we start the malware:
It allocates space on the stack, moves some values around, deallocates space on the stack, and then returns back to NTDLL.dll. What gives?
The answer lies in Export functions – a topic touched on earlier. An Export function is a function that a program or DLL makes available for other, external programs to call. We typically have one – every program has to start somewhere. But switch back to IDA Pro and take a look at our Exports tab.
There are two! We actually want the ‘Work’ export. Remember this image from above? It was the first thing we saw when we opened the malware in IDA Pro:
This is the same code which we are currently staring at in x32dbg! This is where export “DllEntryPoint” leads us. It doesn’t do anything useful! This is an anti-analysis trick.
OK, so can we call a specific export, in this case “Work”, with x32dbg? Unfortunately, we can’t (https://reverseengineering.stackexchange.com/questions/14930/calling-dll-exports-in-x64dbg), the feature doesn’t seem to exist.
But it’s not time to give up yet. Save a snapshot of this VM, because we’ll be coming back to analysis on Wednesday, July 18, 2018.
To start our week, we started a static/dynamic hybrid analysis of our malware sample. We diligently allowed the malware to unpack itself into a memory region, and we did some additional research into some API calls that it uses. We then dumped this unpacked sample to disk. It turns out that the authors didn’t exactly encode the final sample, we were able to cut out a bunch of unpacking by just editing our dumped memory region with a hex editor.
We have a great group of strings and imports to get into in the final dumped sample. We hit a snag with choosing the right export to start analysis inside of in x32dbg, but we have a solid solution planned for Wednesday’s finale to our Kuluoz analysis.