Most malware is distributed in "packed" form: typically an executable containing code to evade antivirus detection and sandboxes before extracting and executing the intended payload.
There are many commodity packers written in Microsoft .NET, usually but not always containing malware also written in .NET.
We discuss two prevalent such packers used to distribute a wide variety of malware but hiding the intended payload in images.
Steganography
Steganography is the technique of sending hidden messages in apparently innocent forms. For hiding data in images, the main techniques are:
- Store the hidden data at the end of an image file
- Store the hidden data within the image metadata (e.g., EXIF)
- Store the hidden data within the actual pixel data
To be truly "hidden" the latter would arguably mean using only the least significant bits of the data so that the image appears "normal" when rendered.
The packers discussed here generally use the entire image pixel data so aren't truly "hidden"; if they were displayed, the images would appear random.
"CyaX" packer
In this packer, the .NET executable contains a square PNG image in a .NET resource, which is typically a large proportion of the whole file size.
The image can be decoded to an intermediate executable, which contains a .NET resource which in turn can be decoded to the payload. Sometimes the intermediate executable uses an additional commodity packer such as ConfuserEx or .NET Reactor.
Details
The first stage payload is decoded from the Blue, Green, Red, and Alpha (BGRA) channels taking pixels in columns. Some versions use Red, Green, and Blue (RGB) channels instead.
For example, in sample SHA256 - 026b38e8eb0e4f505dc5601246143e7e77bbd2630b91df50622e7a14e0728675:
Figure 1: Image taken from sample SHA256: 026b38e8eb0e4f505dc5601246143e7e77bbd2630b91df50622e7a14e0728675
Using channels BGRA from the image we get data starting:
In general, the extracted data is then XORed with a short XOR key or the first 16 bytes of the data and possibly decompressed with gzip, yielding an intermediate stage .NET executable.
For the above sample, the XOR key is (in hex) "74 43 74 17 74 02 74 23 74", which gives the executable:
This intermediate stage is often itself packed with ConfuserEx, but after unpacking that, it contains a .NET resource which contains the payload, typically XORed with two keys:, a short (often truncated Unicode) one, followed by a 16-byte key stored at the start of the resulting file.
In the above sample, the intermediate executable is packed with .NET Reactor. After deobfuscation with a tool such as de4dot, the deobfuscated executable contains a resource "2EJp1.resources" which starts:
XORing with key "00 77 00 55 00 6c 00 59 00 71 00 79 00 4e" ("wUlYqyNZJIbjVN" in Unicode, truncated to half the length):
and then XORing with the first 16 bytes of the result gives the payload, Agent Tesla (a prevalent information stealer) in this case:
In some early versions of this packer, this .NET resource was named "CyaX_Sharp.Properties.Resources.resources" hence the name we have given to this packer family.
Gzip variant
As mentioned above, some samples use the Red, Green, and Blue (RGB) channels, and some compress the intermediate executable with gzip.
For example, in sample SHA256 - 083521fa4522245adc968b1b7dd18da29b193fd41572114c9d7dd927918234ea:
Figure 2: Image taken from sample SHA256: 083521fa4522245adc968b1b7dd18da29b193fd41572114c9d7dd927918234ea
the image uses RGB channels which decode to:
XORing with key (in hex) "24 04 33" gives:
which is a 4-byte DWORD containing the uncompressed file size, followed by a gzip-ed file, starting with a 10-byte gzip header, which decompresses to the intermediate .NET executable:
This contains a .NET resource "d2o6x4FhIdl.resources" starting:
which when XORed with keys "00 66 00 43 00 73 00 6b 00 62 00 67 00" ("fCskbgkLbLArI" in Unicode, truncated) and then "07 2e 8c d5 50 23 1b e3 be be 38 4f 0f 4b 8d ca" gives:
which contains the payload, Agent Tesla again.
Steganographic variant
In a recent variation of this packer, the first stage payload is actually stored in a second PNG image extracted from the least significant bits of the Red, Green, and Blue channels in the first image, taking pixels in rows (so "proper" steganography in this case). The intermediate stage .NET executable is then extracted from the Blue, Green, Red, and Alpha channels of the second image with pixels taken in columns, without XOR this time.
For example, in sample SHA256 – 04794ec7e7eb5c6611aada660fb1716a91e01503fb4703c7d2f2099c089c9017:
Figure 3: Image taken from sample SHA256: 04794ec7e7eb5c6611aada660fb1716a91e01503fb4703c7d2f2099c089c9017
the image has RGB channels and, taking pixels by rows first rather than columns, leads to:
(There is also an Alpha channel, with all values set to 0xff.)
Taking groups of 8 bytes and then the least significant bits in reverse order gives us (for example "ff 01 00 ff 01 00 fe 01" -> "10011011" -> 0x9b):
This is a file size stored in a DWORD (0x1e09b) followed by the second PNG image. Using BGRA and columns first, this decodes to:
which contains a .NET resource "biGzxmYEphCl":
which when XORed with "4c 00 6b 00 74 00 79 00 54 00 65 00 66 00 65 00" ("LktyTefe" in Unicode):
gives the payload, which in this case is Remcos RAT.
"Hectobmp" packer
In this packer, the .NET executable contains typically several hundred small images in .NET resources, which each contain a part of the payload and need to be reassembled in the correct order.
Earlier versions used the BMP file format, and later versions have switched to using PNG. The name we have given to this packer comes from "hecto-" from the metric system prefix for a hundred.
Details
Figure 4: .NET resources list (from ILSpy)
For example, in sample SHA256 – 0091c6bdceecf3e0143b4eaaefca1cd56cbfdfc55f99c167f9dd1f3a48928bb5:
Figure 5: First image taken from sample SHA256: 0091c6bdceecf3e0143b4eaaefca1cd56cbfdfc55f99c167f9dd1f3a48928bb5
which contains 135 images, the first image decodes, using Green, Red and Blue channels, rows first, to:
This includes the start of a Windows executable.
The size of the chunk extracted from each image is stored in the first four bytes (DWORD), 0x30d in this case, less 15, and the required chunk of data starts at the 6th byte.
The chunks need to be assembled in numerical order of the resource names, which is different from the alphabetical order they appear in the file which is:
and the order they are referenced in the .NET metadata which is:
The reassembled payload in this case is Loki Bot Stealer.
In the following sample, SHA256 – 09c8cbd9cdfda1fcb7c6a051887213dc3e3ccf00a5877eca3d3e374f077b98d5, the images are BMPs and the first one looks like:
Figure 6: Image taken from sample SHA256: 09c8cbd9cdfda1fcb7c6a051887213dc3e3ccf00a5877eca3d3e374f077b98d5
The image decodes to the following, with chunk size highlighted in green, chunk data highlighted in yellow and blue:
In this case, when assembled from the images, the payload is compressed using zlib Deflate, starting at byte 0xb0, highlighted in blue.
Decompressing gives:
which again is Agent Tesla in this case.
Conclusion
Generally, packers have different features that allow them to circumvent detection mechanisms by appearing as benign files, being difficult to reverse engineer, or incorporating sandbox evasion techniques. In this blog we've looked at two packers which use embedded images to hide the payload, one using a single image and the other using hundreds of them. These are just a few of the many tools threat actors have at their disposal to aid in distributing malware, collecting sensitive information, and gaining unauthorized access to systems.
IOCs
IOC |
Type |
Description |
026b38e8eb0e4f505dc5601246143e7e77bbd2630b91df50622e7a14e0728675 |
SHA256 |
CyaX PNG sample with channels BGRA |
c8c79ba04ab76c96db913f05b4b5bab36e7e0148fd72148df170a4be94d879a3 |
SHA256
|
Agent Tesla payload in 026b38e8eb0e4f505dc5601246143e7e77bbd2630b91df50622e7a14e0728675 |
083521fa4522245adc968b1b7dd18da29b193fd41572114c9d7dd927918234ea |
SHA256
|
CyaX PNG sample with gzipped data |
a6f7edd2654412c25d7c565cb5b52e1382799a8b86d6bc44e965b554f6344618 |
SHA256
|
Agent Tesla payload in 083521fa4522245adc968b1b7dd18da29b193fd41572114c9d7dd927918234ea |
04794ec7e7eb5c6611aada660fb1716a91e01503fb4703c7d2f2099c089c9017 |
SHA256
|
CyaX PNG sample with double steganography |
6d9c861bf6f1495a4bddc7c745eb5b504692b4d6eae31e89453f0829760b1b90 |
SHA256
|
Remcos RAT payload in 04794ec7e7eb5c6611aada660fb1716a91e01503fb4703c7d2f2099c089c9017 |
0091c6bdceecf3e0143b4eaaefca1cd56cbfdfc55f99c167f9dd1f3a48928bb5 |
SHA256
|
Hectobmp sample with PNGs |
1180c158968faaf0a4951e9a0c59996f0fb29cdad9443aa2097efb5bc7f123f4 |
SHA256
|
Loki Bot payload in 0091c6bdceecf3e0143b4eaaefca1cd56cbfdfc55f99c167f9dd1f3a48928bb5 |
09c8cbd9cdfda1fcb7c6a051887213dc3e3ccf00a5877eca3d3e374f077b98d5 |
SHA256
|
Hectobmp sample with BMPs
|
c3b85d8291281d73cfdd8373cb2b32cdc4c3a602233f99ab3cbbd34bd4e3c99b |
SHA256
|
Agent Tesla payload in 09c8cbd9cdfda1fcb7c6a051887213dc3e3ccf00a5877eca3d3e374f077b98d5 |
References
De4dot
ILSpy
Agent Tesla: A day in a life of IR, Full description of an Agent Tesla campaign using CyaX packer (steganographic variant)