In August 2019, Kaspersky discovered a malicious campaign distributing a fully fledged C++ Trojan that we call Milum. All the victims we registered were organizations from the Middle East. At least some of them are related to industrial sector. Our Kaspersky Threat Attribution Engine (KTAE) doesn’t show any code similarities with known campaigns. Nor have we seen any target intersections. In fact, we found just three almost unique samples, all in one country. So we consider the attacks to be targeted and have currently named this operation WildPressure.
The compilation timestamps for all these files is the same – March 2019. This is consistent with the fact that we registered no infections before May 31, 2019, so the compilation dates don’t seem to be spoofed. For their campaign infrastructure, the operators used rented OVH and Netzbetrieb virtual private servers (VPS) and a domain registered with the Domains by Proxy anonymization service.
The malware uses the JSON format for configuration data and as a C2 communication protocol over HTTP as well. Inside the encrypted communications within the HTTP POST requests, we found several interesting fields. One of them shows the malware version – 1.0.1. A version number like this indicates an early stage of development. Other fields suggest the existence of, at the very least, plans for non-C++ versions.
The only encryption implemented is the RC4 algorithm with different 64-byte keys for different victims. Also, the developers were kind enough to leave RTTI data inside the files. Kaspersky products detect this malware as Backdoor.Win32.Agent. For more information, please contact: email@example.com
Why we call it Milum and why it’s of interest
All the aforementioned C++ Trojans are compiled as standalone PE files, originally named Milum46_Win32.exe. The word ‘milum’ is used in the C++ class names inside the malware, so we named the Trojan after it.
Another distinctive characteristic is that the malware exports lots of zlib compression functions, such as zlibVersion(), inflate() or deflate(). This compression is needed for C2 communication, but in reality there is no need to export them in the case of a standalone application.
The JSON configuration fields are not limited to just the version and programming language; the campaign operators also use target IDs that are found in the samples. Among them, we found HatLandM30 and HatLandid3 – neither of which we are familiar with. The following table provides Milum samples that have similar PE header compilation timestamps but different target IDs:
|Milum46_Win32.exe sample MD5 hash||Timestamp (GMT)||clientid|
Rather than describing all the configuration fields one by one, we have gathered them together in the following table, with all the main characteristics for this malware family:
|Programming language||C++ with STL functions used mostly to parse JSON data and exception handling.|
|Configuration data||Base64-encoded JSON data in PE resources. Includes timeouts, C2 URLs and keys for communication, including RC4 64-byte key.|
|Network protocol||Trojan transmits compressed JSON data in HTTP POST requests with gzip, base64-encoded and RC4 encrypted.|
|Beacon data||Encrypted JSON contains the malware version “1.0.1”, Epoch timestamp and client id. It also has specific fields such as “vt” and “ext” that correspond to programming language “c++” and file extension “exe”. If our hypothesis is correct, this suggests that non-C++ Trojan versions may be planned, if not already implemented.|
|Persistence||HKCU autorun system registry keys Run and RunOnce.|
|Encryption||The communication encryption used is RC4 with the 64-byte key stored in the configuration data.|
|Compression||For compression the Trojan uses an embedded gzip code. For some reason gzip functions are exported from PE, although the samples are standalone executables, not DLLs.|
Let’s dig a little deeper inside
The most popular sample in our telemetry was:
|Compiled||2019.03.09 06:17:19 (GMT)|
This application exists as an invisible toolbar window. The main malicious functions are implemented in a separate thread. Milum decodes its configuration data and, besides timeouts, it gets the parameters “clientid” and “encrypt_key” to use in RC4 encryption.
The following table describes the different configuration parameters:
|Config parameter||Parameter features|
|shortwait||Pause in milliseconds between C2 communication working cycles|
|clientid||Unique ASCII target name|
|encrypt_key||RC4 encryption key for JSON-based C2 communications|
|relays – url||Full URL to send HTTP POST beacon and GET commands|
|relays – key||Unique ASCII key for each C2 to communicate with it|
The operators can run the Trojan using the key (“b” or “B”) as the first argument and the file name as the second. In this case, Milum will delete the file sent as a parameter. Then the Trojan will create the C:\ProgramData\Micapp\Windows\ directory and parse its configuration data to form the beacon to send to its C2.
To send the beacon, Milum uses the HTTP POST request with three parameters as enumerated in the table below.
|Beacon parameter||Parameter values|
|md||Clientid from config, with prefix 01011 and random five-character ASCII suffix|
|nk||Key from config to communicate with C2, differs for each server|
|val||Compressed, encrypted and encoded command JSON data|
The first two parameters are taken from the configuration data. The third one is encrypted and after decryption, decompression, decoding and beautifying, it looks like this:
There are several fields worth mentioning here. We referred above to different programming languages besides C++: “vt” seems to reference a programming language and “ext” a file extension. The only reason that we could think of for keeping these is if the attackers have several Trojans, written in different languages, to work with the same control server.
Regarding the “command” field, the control servers were inaccessible at the time of the analysis, so we don’t have commands from them. However, we analyzed the command handlers in Milum’s code as described below:
|1||Execution||Silently execute received interpreter command and return result through pipe|
|2||Server to client||Decode received content in “data” JSON field and drop to file mentioned in “path” field|
|3||Client to server||Encode file mentioned in received command “path” field to send it|
|4||File info||Get file attributes: hidden, read only, archive, system or executable|
|5||Cleanup||Generate and run batch script to delete itself|
|6||Command result||Get command execution status|
|7||System information||Validate target with Windows version, architecture (32- or 64-bit), host and user name, installed security products (with WQL request “Select From AntiVirusProduct WHERE displayName <>’Windows Defender’”)|
|8||Directory list||Get info about files in directory: hidden, read only, archive, system or executable|
|9||Update||Get the new version and remove the old one|
Who was attacked?
According to our telemetry, the Milum Trojan was exclusively used to attack targets in the Middle East from at least the end of May 2019.
We were able to sinkhole one of the WildPressure C2 domains (upiserversys1212[.]com) in September 2019. The vast majority of visitor IPs were also from the Middle East, and we believe the rest were network scanners, TOR exit nodes or VPN connections.
And who’s behind it?
To date we haven’t observed any strong code- or victim-based similarities with any known actor or set of activity. Their C++ code is quite common, regarding configuration data and communication protocol malware uses base64-encoded JSON-formatted configuration data stored in the binary’s resource section and parses it with Standard Template Library (STL) functions. However, these commonalities are not conclusive enough for attribution and our hypothesis is that they are merely coincidence. We will continue to monitor this activity.
To sum up
To date, we don’t have any data regarding Milum’s spreading mechanism. A campaign that is, apparently, exclusively targeting entities in the Middle East (at least some of them are industrial-related) is something that automatically attracts the attention of any analyst. Any similarities should be considered weak in terms of attribution, and may simply be techniques copied from previous well-known cases. Indeed, this “learning from more experienced attackers” cycle has been adopted by some interesting new actors in recent years.
We should also be cautious regarding the true targeting of this new set of activities, as it is probably too soon to jump to conclusions. The targeted nature seems to be clear, but the targeting itself might be limited by our own visibility. The malware is not exclusively designed against any kind of victim in particular and might be reused in other operations.
Indicators of compromise
Original file names are Milum46_Win32.exe; on the target side they exist as system32.exe