Huntress previously reported on malicious activity from the exploitation of a 0-day vulnerability in Cleo software. The malware being delivered through this exploitation has now been analyzed and a technical breakdown of a new family of malware we’ve named Malichus is included in this post. The name is a play on the word Cleopatra and comes from Malichus I, who is noted to have burned Cleopatra’s navy fleet in revenge for his losses throughout a war that Cleopatra had initiated.
The malware makes use of a small PowerShell loader that sets up the host for further exploitation. It is stored as a base64 blob and gets decoded and is used to execute a Java Archive that gets deployed to the system with the name cleo.[numerical-identifier].
The loader creates a TCP connection to a C2 IP address to retrieve a second stage payload. The formatted loader is shown below:
The loader also sets a variable called Query which is used for retrieving the C2 address used in a subsequent Java backdoor and the victim IP address identifier. The C2 IP address and second stage dropper are different for each identified payload by Huntress.
The downloaded second stage archive is decrypted by the loader using a unique AES key per payload. Upon decrypting and decompiling the second stage downloader, this contained a manifest file indicating it would run the start class upon execution.
Starting from the main method, the backdoor will get the environment variable query and repair it to make it a valid base64 string. That decoded value contains the AES key, a unique value required to download stage 3, and the IPs of the C2 server and the victim host. A request is then sent to the C2 using that base64 encoded value appended with TLS v3.
A CyberChef recipe showing the output of this is as follows:
Using the allocated space we’ve renamed as recvBuf, the dropper then keeps accepting bytes until the full stage 3 payload has been downloaded. Then using the AES key and hardcoded IV, it decrypts the downloaded data which will yield an intentionally corrupted zip. They then repair the header by cutting off the first two bytes (cC in the example we analyzed), and move onto extraction and loading.
A CyberChef recipe showing the output of this is as follows:
Finally, the backdoor will unzip the downloaded archive using the provided helper method, and dynamically resolve the classes it contains.
The final stage is a modular Java based post exploitation framework which contains a significant amount of functionality. It is composed of 9 class files, with the primary driver being the Cli class loaded by the previous stage. The framework supports both Linux and Windows however Huntress only observed usage on Windows.
This begins with a public class called Cli that takes three parameters passed to it from the stage 2 downloader that are stored in the variables host, cliid, and stage1fn.
The passed parameters from stage 2 correspond to:
The class has a number of static variables defined that are used throughout the payload including one that identifies if it is operating on a Windows operating system or not.
This runs the main run() method and subsequent runDelFileCmd() method to delete the first stage payload (downloader) from disk (cleo.2607) using either PowerShell or Bash depending on whether the system was identified as Windows or not.
The run method is responsible for using the SrvSlot class to queue up connections to the C2 IP address retrieved from the previous query parameter on port 443. This class extends the class Slot allowing it to access the static variable st which is used to send custom commands to the implant from the connected C2 server and is part of a custom C2 protocol. These connections will continue until the number of connections is 5 or more, and the variable st is set to something other than 200 or 500.
This class also contains two helper methods: l (logging) and dmp (hex dump). The first is used throughout the implant to log various data to a log buffer. The second allows the malware to create a valid hexdump from a provided byte array, which is then logged using the logging helper function. The hexdump function is likely for debugging purposes as it isn’t called anywhere in the implant.
A zip file management class for uploading, packaging, and handling zip files for the operators to read and collect files or directories. A number of variables are used in this class in addition to a method named Dwn responsible for logging how much time has passed since the last update and assigning a queue of files to be uploaded to the C2 server stored in an arraylist called lvs.
The class contains a tick method used for keeping track of time passed and sending a status update to the C2 server if more than 5 seconds had passed since the last update.
The setStat method called by tick will set fields of the newly created tick packet with information about the current state of the Dwn class.
Interestingly this class also has a check called tick2 responsible for tracking the state of the files being exfiltrated, and managing the queue of files to be exfiltrated.
The addFile method is used to add the files to the queue for exfiltration.
This is supported by the readFile and write method to retrieve the file from disk.
Other methods zipOpen, zipClose, and getCurrZipSize use a new instance of the Mos class but other than that are fairly self-explanatory.
A simple class for managing an array of files preparing for zip archival and uploading to the attacker. It is used primarily in the Dwn class and is just for keeping state.
A small class for handling multi-archive zip files. In order to optimize zip file sending, zip files are split up for every 262154 bytes and then archived with ZipIDs during exfiltration.
This appears to be equivalent to 2 of their custom protocol packet lengths of 131072 bytes.
Proc contains implementations of the tasks that can be performed by the malware as well as a few reconnaissance capabilities on the victim’s machine. These commands are parsed in the SlotSrv class.
The Proc class allows the remote attackers to primarily issue execution commands and read from configuration files using the run function. The ishell command (interactive shell) is a command that uses the pipeMode function to handle interactive shells indicating the malware has capability to allow an attacker to go ‘hands-on-keyboard’ and interactively run commands through a terminal.
This aligns to activity identified by the Huntress SOC when a nltest command was seen to be interactively run by a threat actor. The run function is also the primary method used to retrieve Cleo software configuration via the prs-conf command.
The confParser method will retrieve elements from within a configuration file located at conf/Top.xml
on disk and retrieve all the host values from within this. For each host identified it will attempt to retrieve the corresponding xml file within hosts/<hostname.xml>
and parse this to be used in a function loadUserDirs
.
The function loadUserDirs is used to parse any Host or Mailbox nodes within the host xml file and extract the aliases defined within this file. In addition this will also parse the file for references to several directories which are sent alongside the alias they are paired with.
This includes a method to replace any custom variables named Homedirectory and Homedirectorytype that may have been defined in the application instance with their corresponding value.
This shows that the malware author had an understanding of specific configuration files used within the Cleo software, and was interested in understanding what types of trading relationships a Cleo Harmony, VLTrader, or LexiCom instance had been configured with in addition to where payload files such as files sent or received between these businesses resided on disk.
Interestingly this contained a method called checkDir that appears to be looking for if a directory is readable or not; however, this was never called in the code so may have been used for debugging purposes or it is a feature which has not yet been implemented.
This class allows the attackers to perform basic read and write operations on the filesystem using FileOutputStream and FileInputStream classes. This is referenced in file handling procedures within the SvrSlot class.
There are also a number of helper functions that help SFile read, write, and manage files:
This is a multi-channel communication link with the channel used for handling asynchronous communication, each ScSlot acts as a unique pipeline for data using the SvrSlot class.
This class is responsible for establishing and managing the connection to the C2 server. It initializes two buffers for sending and receiving data:
This class is mainly involved with receiving and sending responses from the C2 server. Upon connection to a C2 server this class will log the packet number using the method l within the Cli class for debugging purposes before setting st to 2 indicating the connection has been successfully established and was ready to receive a hello packet. This overrides a method with the same name in Slot used to determine if connection has been established or not.
The hello packet sent to the C2 has a 16 byte header, the structure is as follows:
Byte Offset | Value |
---|---|
0 - 9 | Math.random() * 256.0D |
10 | 2 |
11 | 119 |
12 | 33 |
13 | -1 |
14 | 1 |
15 | Cli.fIsWin ? 0 : 1 |
An interesting bit to note is the 10 junk bytes that are added to the front of the packet. This is likely an attempt to bypass network fingerprinting techniques, however the other bytes in the packet are static making that relatively trivial.
The first packet sent to the C2 by the malware is a “hello” packet which contains an identifier paired with a hostname if it was previously retrieved in Cli (cliid + “\t” + hostname), as well as synchronization required for encrypting further packets.
After that hello packet is sent, the implant awaits a hello response from the server which gets parsed in the prsHelloPkt method.
SvrSlot also contains an option to record statistics on each compromised endpoint which appears to be used for health tracking of their own C2 and endpoint.
There are three debugging options used for collecting information, the primary command is #dbg# which will collect information on how many zip file errors, how many times they encountered a server queue status, and the availability of free memory on the host.
The command #ll# displays how many files have been added to zips referencing the Dwn Class mentioned above.
The command #lsz# performs information about zip file gathering, detailing the last zip file size, the ID of the zip, the zip file number, and the offset of the zip file - this is likely used to debug their multi-zip Archival process performed within the Mos class mentioned above.
Malichus makes use of a fully custom C2 protocol. Each incoming and outgoing packet is processed by the function in SrvSlot called pkt0. It takes in a complete packet, and calculates its CRC32 value to confirm the packet's integrity. Bytes 3 and 4 are then set to the high and low bytes of that CRC32 value before it is encrypted.
After the packet’s CRC32 has been updated, pkt0 calls addEncr which is responsible for encrypting the packet. There are a few global variables that are used to manage both encryption and decryption state:
The C2 and the client must stay in sync for the encryption/decryption to work as the mutation of the state and index variables are updated by the value of the previously encrypted/decrypted byte.
A similar method exists for decrypting packets but it makes use of a different set of variables to keep track of the state:
As we mentioned before, the pkt0 method is the final packet constructor used by all other 6 packet types, most of which are overloaded methods of the name pkt. These each have slightly different uses but share the commonality that the first byte is the packet identifier. There are 4 generic packet types, and 2 specialized ones. Of the generic packets:
The two special packets that are used are helloPkt and zipPkt, with the helloPkt being detailed earlier:
After the packets are created by pkt0, they are put into the outbuf queue and are sent to the C2 using the evWrite method in the Slot class:
Whilst the full extent of who created this malware and why still remains under speculation, this blog hopes to shine a light on a new family of malware targeting Cleo software, its functionality, and gives potential insight into what the malware author was hoping to achieve with this custom piece of malware.
This blog post was independently created in tandem with other industry vendors, and mostly aligns with analysis also independently performed by:
Packet Number | Value | Explanation |
---|---|---|
1 | Hello | Hello packet to initialize connections |
2 | Channel | Get C2 channel identifier |
3 | Data | Data Packet |
4 | Prepare File | Prepares a file for upload |
5 | Close Channel | Kill C2 channel and any open handles |
6 | Echo | Return received data to C2 |
7 | Halt | Resets input buffers |
8 | Command | Run received command |
9 | Stop | Stop process, channel, or file transfer |
10 | Resume | Restart process, channel, or file transfer |
11 | Ack | Confirm synchronization of processed commands |
12 | File I/O Status | Confirms that implant is able to receive or process zip data |
13 | Zipdata Reassembly | Reassemble zip chunks and remove them from a queue |
Filename | SHA256 |
---|---|
cleo.2607 | 6705eea898ef1155417361fa71b1078b7aaab61e7597d2a080aa38df4ad87b1c |
Cli | 0c57b317b572d071afd8ccdb844dd6f117e20f818c6031d7ba8adcbd32be0617 |
Dwn | 429d24e3f30c7e999033c91f32b108db48d669fde1c3fa62eff9da2697ed078e |
DwnLevel | f80634ce187ad4834d8f68ac7c93500d9da69ee0a7c964df1ffc8db1b6fff5a9 |
Mos | 0b7b1b24f85a0107829781b10d08432db260421a7727230f1d3caa854370cb81 |
Proc | 1ba95af21bac45db43ebf02f87ecedde802c7de4d472f33e74ee0a5b5015a726 |
SFile | 57ec6d8891c95a259636380f7d8b8f4f8ac209bc245d602bfa9014a4efd2c740 |
ScSlot | 87f7627e98c27620dd947e8dd60e5a124fdd3bb7c0f5957f0d8f7da6d0f90dee |
Slot | 1e351bb7f6e105a3eaa1a0840140ae397e0e79c2bdc69d5e1197393fbeefc29b |
SrvSlot | f4e5a6027b25ede93b10e132d5f861ed7cca1df7e36402978936019930e52a16 |
https://github.com/huntresslabs/threat-intel/tree/main/2024/2024-12/Cleo
Get insider access to Huntress tradecraft, killer events, and the freshest blog updates.