« Back to home

Analysis of APT28 hospitality malware (Part 2)

In the first part of this malware review, we looked at the VBA code used by APT28 to drop a DLL onto the victims' machine as part of their recently highlighted hospitality campaign.

In this post, we will look at the dropped file, and understand just what it does, and how we can analyse it using IDA Pro.

So we know from the first post that we have a DLL, which is run using the following command:

rundll32.exe %APPDATA%\user.dat,#1

Loading the extracted DLL into IDA, the first thing that we notice is that we have an exported function of load with an ordinal of 1:

We know from the rundll32.exe command that this will be our entry point, so we start our analysis here.

Within the load function, a number of strings are constructed on the stack in Unicode, which when decoded look like this:

Interestingly, one of the strings of mvtband.dat closely matches with the C2 server identified by FireEye of mvtband.net.

Entering the first function at address 10001000h, we see another Unicode string constructed on the stack of "Environment" before RegOpenKeyExW is called to open a handle to HKCU\Environment.

Next a path is constructed of %appdata%\mrset.bat and written to the UserInitMprLogonScript registry value within HKCU\Environment:

Note: If we stop and look for other examples of malware using this technique, we can see a number of related posts unsurprisingly pointing to other Sofacy malware droppers using the same method.

Continuing to the next function, we find what immediately appears to be a decryption loop, using a fixed XOR key of 0x26:

One the bytes at this address are decrypted, the contents are written to %appdata%\mvtband.dat.

This is a perfect opportunity to use IDAPython to recover the encrypted data. We know from the disassembly that the loop runs for 0x7600 bytes, and XOR's a byte at a time from the address 0x10009B90 with a fixed key of 0x26. Translating this into IDAPython, we have the following script:

v = ""
bytes = idaapi.get_many_bytes(0x10009B90, 0x7600)
for i in range(0,len(bytes)):
    v += chr(ord(bytes[i]) ^ 0x26)

f = open("out.bin", "wb")
f.write(v)
f.close()

Once executed, this script will decrypt the contents of address 0x10009B90 and write the output to out.bin.

An initial review of the decrypted contents show that this is a PE32 DLL, and if we upload the sample to VirusTotal we see that a matching sample was first seen on 17-07-2017 with a name of mvtband.dll and signatures matching Sofacy:

Continuing into the final function of this dropper, we find a similar decryption loop for a different memory location and the same XOR key:

Repurposing our above IDAPython script, we can extract the contents with the following:

v = ""
bytes = idaapi.get_many_bytes(0x10009B20, 0x6A)
for i in range(0,len(bytes)):
    v += chr(ord(bytes[i]) ^ 0x26)

f = open("out2.bin", "wb")
f.write(v)
f.close()

Reviewing the decrypted contents, we find the following:

set inst_pck = "%appdata%\mvtband.dat"
if NOT exist %inst_pck % (exit)
start rundll32.exe %inst_pck %,#1

This simple .bat file is being used by the UserInitMprLogonScript registry value on reboot to launch the mvtband.dat payload via rundll32.exe.

Once the .bat file script is decrypted by the dropper, the contents are written to %appdata%\mrset.bat before being launched using CreateProcess.

And there we have it, APT28's simple dropper and persistence malware, with a bit of IDAPython reversing thrown in. We see that this DLL functions to decrypt 2 embedded payloads, "mrset.bat" which is a BAT file executed by "UserInitMprLogonScript", and "mvtband.dat" which is the main payload of the malware which is executed via rundll32.exe.

So what are the takeaways from this for our red-team engagements? Well first, we see that adversaries are now increasingly using rundll32.exe in malware campaigns, which allows a payload to be stored without a typical .exe extension. More importantly, this also gives malware a better chance at being successfully executed within a restricted environment which whitelists Microsoft signed binaries.

Secondly, we have UserInitMprLogonScript being used for persistence to launch a .bat file as a GPO script. While certainly not unheard of, the use of a GPO value is less likely to draw attention than say, adding a RUN key value, or adding a new schtask.

Hopefully this has been a good introduction to the APT28 dropper and how we can use IDAPython during a reversing exercise, and as always, comments and feedback are welcome via the usual channels.