Reversing Multilayer .NET Malware

This post was authored by Dave McDaniel with contributions from Jaeson Schultz

Recently, we came across a malware sample that has been traversing the Internet disguised as an image of a woman. The malware sample uses several layers of obfuscation to hide its payload, including the use of steganography. Steganography is the practice of concealing a message, image, or file within another message, image, or file. Steganography can be used in situations where encryption might bring unwanted attention. Encrypted traffic from an unusual source is going to draw unwanted attention. Steganography allows malicious payloads to hide in plain sight. It also allows the attacker to bypass security devices. In our sample malware, steganography is used to decrypt and execute a second dropper, which in turn installs a user-land rootkit to further hide its intentions. The rootkit adds another layer of obfuscation by installing a DarkComet backdoor, using RC4 encryption to encrypt its configuration settings and send data to its command and control server.

Table of Contents

General Info

First Level Dropper

Second Level Dropper

The Back Door – Dark Comet

Decrypting the Traffic

Conclusion

General Info

Malware sample – ADA8229CE99B8386E89EB94ECAB678AE79C8638B3EAF3532B847CFF2B201C232
Upon execution, an image of a woman appears in the default image viewer for the user:

Followed by a very interesting crash message:

Which is followed by some cryptic TCP traffic (these same 22 bytes are sent over and over again).

It also contains a keylogger which logs to a file:
C:\Documents and Settings\UserApplication\Datadclogs<date>.dc (Keylog data)

Which contains log data such as:
——————————————————————————-
:: 393013_247495955389874_580132800_n.jpg – Windows Picture and Fax Viewer (1:15:03 PM)
:: Clipboard Change : size = 6 Bytes (1:15:03 PM) dclogs
:: My Documents (1:15:29 PM)
:: dclogs (1:15:33 PM)
:: Clipboard Change : size = 47 Bytes (1:15:33 PM) C:\Documents and Settings\User\Application Data
:: Program Manager (1:15:36 PM)

[F2]
:: 010 Editor – C:\Documents and Settings\User\Application Datad\clogs2014-09-12-5.dc (1:15:56 PM)
:: Clipboard Change : size = 27 Bytes (1:15:56 PM) ILSpy.SharpDevelop.LGPL.dll ——————————————————————————-

CFF Explorer shows that this executable is written in .NET.

Using a tool called ILSpy, you can decompile a .NET executable (or at least recover the obfuscated code) to Classes and namespaces. Each namespace is denoted by a {} with Classes under the namespaces. In this case, the program is heavily obfuscated and broken down like this:

{} AliXiRNztXCigLKx

Class Form1

Class MSTLoAuWUAMXT

Class mWYMsmhuVo: Button

Event OnClick()

Byte array cMuKPyMylNpcpWwcT

Function static byte[] JEKrxMYreXhFJ(byte[] ngIGsZoXRnTDjRXgM, byte[] cMuKPyMylNpcpWwcT)

Function static byte[] ZTfHKdaSogeSCNqp(Bitmap WikhBwotOOwRPTwNg)

Here is a quick view of what Form1 looks like. Form1 is where this malware begins execution. Note the arrows point to the areas mentioned above.

The First Level Dropper – Obfuscated .NET

To determine order of execution, it is helpful to copy and paste each important class/function into a single document. Saving the reader time, here is the cleaned up, de-obfuscated code to show only the important sections:

The Dropper – Order of Operations

To best understand what is happening behind the scenes with the dropper, breaking it down into steps is often helpful.

1. If you examine Form1’s Load() function, there is a call to this.myButton.PerformClick().
2. PerformClick() triggers an OnClick() event that will load a bitmap from the resource section with loadBitmap().
3. loadBitmap() creates an array that is the size of myBitmap.Width * myBitmap.Height * 4. The reason for this is because of how bitmaps are stored (which I will get to in a few moments)

Within this function, the first steps create a rectangle and then an object of type BitmapData. Then a call to Marshal.Copy copies data to the array starting at a position called Scan0):

Marshal.Copy(bitmapData.Scan0, array, 0, array.Length);

According to MSDN, BitmapData.Scan0 gets or sets the address of the first pixel data in the bitmap. The question now becomes, where is the first pixel of the bitmap and what determines where that pixel is? This is a great time to talk about bitmaps and how they are structured.

The Bitmap Structure and Determining the First Row

The dropper code references an image in the resource section called nozQEZkPzmBAIC. Since that is the only image in the resource section, it should be easy to spot. You can pull the image from the executable with ILSpy. Just look for the image name in the resource section and click “save”:

Well that image looks interesting. Let’s fill the image header structure with the data from this image. 010 Editor is great for this. On the right is the bitmap structure and on the left is that structure applied to the image that was just extracted and saved.

It is important to note the biBitCount of the bitmap image. 32 bits means that each pixel is defined by that many bits. MSDN says, “The value for blue is in the least significant 8 bits, followed by 8 bits each for green and red. The high byte in each DWORD is not used.”

The member, biWidth is the width of the bitmap, in pixels. So in this bitmap, each row is really 462 * 4 (0x738) bytes wide since it is a 32-bit bitmap. biHeight is simply the number of pixels high (the biBitCount doesn’t matter here). So the entire bitmap size minus the header can be summed up as 462 * 462 * 4 bytes in size or 853776.

Once this size is determined, the direction in which the data is read can be inferred. According to the same MSDN page:

“If biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper-left corner.”

The height in this bitmap is positive, so it will be read from the bottom up (This is where BitmapData.Scan0 will point to). To get the last row, you can simply subtract the row size from the end of the file (unless there is padding). To get a more accurate address, just perform the following math (note that I convert biBitCount to bytes):

So the offset that BitmapData.Scan0 points to in this file is 0xD000E. This is very important if you want to write a decryptor.

The Dropper – Order of Operations (Continued)

3. After loadBitmap() retrieves the pointer to the first row of bitmap data, it is copied to an array one row at a time, skipping the first 4 bytes of the first row and returning the new array.
Array.Copy(array, 4, array2, 0, array2.Length);

4. After the array is returned the decodeBitmap() function is called. This function runs a simple XOR against a static, 11-byte key, starting at byte[4] of the “first” row of the bitmap.

To see this in action, just take the first 11 bytes of the first row (after skipping 4 bytes) and XOR them against the key.

Look familiar? That is definitely looking like a PE32 header. Taking this information, I quickly wrote a tool to decrypt this image into the dropped binary using the headers found on MSDN and the simple XOR key.

The Second-Level Dropper: The Kazy Rootkit Installer

Previously, we dove into decrypting and running an executable from a bitmap loaded from the resource section of a .NET executable. Opening this newly dropped executable in ILSpy, it appears to contain global configuration variables for the Kazy Rootkit. That explains the “Kazy Invoker” crash error message found earlier (not necessarily why it crashed though). There are a lot of options contained within this new executable that include process/registry hiding as well as process elevation techniques:

This rootkit takes advantage of userland APIs such as this to detect Wireshark:

It also uses other simple techniques like this to hide files:

Not super interesting. Normally, it would appear that this kit uses the webClient class to download files contained in a list called “DownNames”. It then executes that file with ZoneId=2 (downloaded from a trusted site):

But that list is empty… So where in the world is the network traffic coming from and what are those 22 bytes? Let’s take a look at what appears to be a second file that is dropped and executed.

The Second Level Dropper – Unpacking the Resources

Displaying the Image to the Victim

A few steps into the Main function a resource is retrieved from the resource section and executed. This is the gzipped image of a woman that popped up earlier. Saving this resource and then opening it in a 7-Zip will reveal the actual JPG.

Unpacking the ‘INVOKER’ resource

The next section used from the resource area is called ‘INVOKER’ and it is basically the KAZY executable. It is not compressed and is just sitting in the resource section (.NET executable). It is executed as csrss.exe with arguments specified in the dropper:

Unpacking The ‘MAIN’ Resource

This is followed by another function that retrieves, decompresses and executes the ‘MAIN’ in the resource section, decompresses it (if compressed), and then executes it.

Knowing that this segment is compressed,( private static bool Compressed = true; ), and that it will inject the executable into itself ( private static string InjectLoc = “itself”; ); The Decompress() method (above) shows that this segment is likely just a gzip compressed segment of data. So just pull that out of the resource section.

For some reason, ILSpy wasn’t allowing the extraction of this resource. You can save the ‘MAIN’ segment of the resource segment using another tool called .NET Reflector to pull this out of the resource section.

The file header of this segment shows this with 0x1F8B as the file magic bytes: This is the GZIP file magic.

7Zip can open this with no problem:

It appears UPX Packed based on the sections, unpacking upx is a snap:

And we now have the unpacked executable from the resource section! It appears to be written with Delphi:

Time to examine some strings. There are a lot of seemingly backdoor-related functions (including keylogging info for the file I mentioned at the beginning of this post) to be found in this binary:

Comparing the strings found with other known malicious files tells us that this is likely the backdoor known as DarkComet.

The Backdoor – DarkComet

Decrypting the Configuration

This RAT uses a symmetrical key for RC4 encryption to send traffic that is based off of the version of DarkComet itself. For more information about how RC4 works in malware, please check out my post here. The key is generated in the following manner:

#KCMDDC<version>#-890<password>

Looking for the string KCMDDC in the binary does reveal the static part of the string (the version) but not the entire password. It is clear however, that this sample is DarkComet version 5.1:

As a sidenote, since this is in Delphi, you can generate an IDC script to help with identifying functions in IDA with an excellent tool called Interactive Delphi Reconstructor:

Once that is done, simply break at the main entry point and continue until a new thread is created. Break at the entry point of that thread:

A new thread is created that performs the keylogging functionality. This function is setting the hook procedure with the WH_KEYBOARD_LL constant signifying a hook procedure that monitors keystroke messages:

But that still doesn’t answer what the network traffic is. Setting breakpoints around the string “KCMDDC51” found earlier and watching for accesses to that data seems like a good place to start. Eventually, a routine is encountered that appeared to be the initialization of an SBox of size 0x100 (256) which implied that RC4 was used in this function.

The SBox is scrambled and stored on the stack. The password “#KCMDDC51#890” is used to decrypt the stored settings which include the password that the sent data is encoded with. Here is the dump of the encrypted data:

You can decrypt the data decrypted within the RC4 function just by using the key found earlier. The following is example output from an RC4 Decryptor written in Python:

These are all of the DarkComet settings!

Decrypting the Traffic

So at this point, we have all the settings used for DarkComet to including what key the network traffic is encoded with (see:”PWD = gotogoa” above).

Knowing from previous research on DarkComet that the traffic is encrypted as follows:

#KCMDDC<version>#-890<password>

which becomes

#KCMDDC51#-890gotogoa

We now have enough information to decrypt the 22 bytes I provided at the very beginning of this post:

Just decrypt the 22 bytes using the key we found earlier:

It turns out that this message was a #KEEPALIVE# signal this whole time. This is great for detection purposes because typical KEEPALIVE messages like these are submitted periodically to the C&C server. Catching this constant traffic is the best way to determine the infection because of how often the signal is repeated. Armed with this knowledge, we created coverage for Snort, CLAMAV and FireAMP:

Snort Detection

Sid: 31814

ClamAV Detection

Win.Trojan.Kazydropper:1:*:2809000006{5}6f1d00000a7409000001{21}281e00000a6f1f00000a
Win.Trojan.Kazyinvoker:1:*:4b0061007a00790049006e0076006f006b0065007200

FireAMP

Detection Name: W32.Auto.ada822.MASH.RT.SBX.VIOC

Conclusion

In researching this malware we noted several layers and techniques to hide its payload, including steganography to decrypt/execute a second dropper (a malicious binary who’s purpose is to download/install additional malicious programs), a userland rootkit, a DarkComet backdoor with RC4 encryption, and Dynamic DNS for C&C traffic. Attackers will continue to employ these kinds of obfuscation techniques in an attempt to avoid detection as well as making it more difficult to reverse engineer. Network administrators will need to remain vigilant with timely signature updates to their security devices to ensure coverage and protection against these more research intensive techniques.

Protecting Users Against These Threats

Advanced Malware Protection (AMP) is well suited to detect and block this type of malware.

CWS or WSA web scanning will prevent access to the C&C networks.

The Network Security protection of IPS and NGFW have up-to-date signatures and will block this threat.

ESA is not applicable for this attack, because the threat is not using email.