
Introduction
Imagine a container zombie outbreak where a single infected container scans the internet for an exposed Docker API, and bites exploits it by creating new malicious containers and compromising the running ones, thus transforming them into new “zombies” that will mine for Dero currency and continue “biting” new victims. No command-and-control server is required for the delivery, just an exponentially growing number of victims that are automatically infecting new ones. That’s exactly what the new Dero mining campaign does.
During a recent compromise assessment project, we detected a number of running containers with malicious activities. Some of the containers were previously recognized, while others were not. After forensically analyzing the containers, we confirmed that a threat actor was able to gain initial access to a running containerized infrastructure by exploiting an insecurely published Docker API. This led to the running containers being compromised and new ones being created not only to hijack the victim’s resources for cryptocurrency mining but also to launch external attacks to propagate to other networks. The diagram below describes the attack vector:
The entire attack vector is automated via two malware implants: the previously unknown propagation malware nginx
and the Dero crypto miner. Both samples are written in Golang and packed with UPX. Kaspersky products detect these malicious implants with the following verdicts:
- nginx:
Trojan.Linux.Agent.gen
; - Dero crypto miner:
RiskTool.Linux.Miner.gen
.
nginx: the propagation malware
This malware is responsible for maintaining the persistence of the crypto miner and its further propagation to external systems. This implant is designed to minimize interaction with the operator and does not require a delivery C2 server. nginx
ensures that the malware spreads as long as there are users insecurely publishing their Docker APIs on the internet.
The malware is named “nginx” to masquerade as the well-known legitimate nginx web server software in an attempt to evade detection by users and security tools. In this post, we’ll refer to this malware as “nginx”.
After unpacking the nginx
malware, we parsed the metadata of the Go binary and were able to determine the location of the Go source code file at compilation time: “/root/shuju/docker2375/nginx.go”.
Infecting the container
The malware starts by creating a log file at “/var/log/nginx.log”.
This log file will be used later to log the running activities of the malware, including data like the list of infected machines, the names of created malicious containers on those machines, and the exit status code if there were any errors.
After that, in a new process, a function called main.checkVersion
loops infinitely to make sure that the content of a file located at “/usr/bin/version.dat” inside the compromised container always equals 1.4
. If the file contents were changed, this function overwrites them.
If version.dat
doesn’t exist, the malicious function creates this file with the content 1.4
, then sleeps for 24 hours before the next iteration.
The malware uses the version.dat
file to identify the already infected containers, which we’ll describe later.
The nginx
sample then executes the main.monitorCloudProcess
function that loops infinitely in a new process making sure that a process named cloud
, which is a Dero miner, is running. First, the malware checks whether or not the cloud
process is running. If it’s not, nginx
executes the main.startCloudProcess
function to launch the miner.
In order to execute the miner, the main.startCloudProcess
function attempts to locate it at “/usr/bin/cloud”.
Spreading the infection
Host search
Next, the nginx
malware will go into an infinite loop of generating random IPv4 /16 network subnets to scan them and compromise more networks with the main.generateRandomSubnet
function.
The subnets with the respective IP ranges will be passed to the main.scanSubnet
function to be scanned via masscan, a port scanning tool installed in the container by the malware, which we will describe in more detail later. The scanner is looking for an insecure Docker API published on the internet to exploit by scanning the generated subnet via the following command: masscan -p 2375 -oL – –max-rate 360
.
The output of masscan is parsed via regex to extract the IPv4s that have the default Docker API port 2375 open. Then the extracted IPv4s are passed to the main.checkDockerDaemon
function. It checks if the remote dockerd daemon on the host with a matching IPv4 is running and responsive. To do this, the malware attempts to list all running containers on the remote host by executing a docker -H PS
command. If it fails, nginx
proceeds to check the next IPv4.
Container creation
After confirming that the remote dockerd daemon is running and responsive, nginx
generates a container name with 12 random characters and uses it to create a malicious container on the remote target.
The malicious container is created with docker -H run -dt –name –restart always ubuntu:18.04 /bin/bash
. The malware uses a –restart always
flag to start the newly created containers automatically when they exit.
Then nginx
prepares the new container to install dependencies later by updating the packages via docker -H exec apt-get -yq update
.
Next, the malicious sample uses a docker -H exec apt-get install -yq masscan docker.io
command to install masscan and docker.io in the container, which are dependencies for the malware to interact with the Docker daemon and to perform the external scan to infect other networks.
Then it transfers the two malicious implants, nginx
and cloud
, to the container by executing docker -H cp -L /usr/bin/ :/usr/bin
.
The malware maintains persistence by adding the transferred nginx
binary to /root/.bash_aliases
to make sure that it will automatically execute upon shell login. This is done via a docker -H exec bash –norc -c \'echo \"/usr/bin/nginx &\" > /root/.bash_aliases\'
command.
Compromising running containers
Up until this point, the malware has only created new malicious containers. Now, it will try to compromise the ubuntu:18.04-based running containers. The sample first executes the main.checkAndOperateContainers
function to check all the running containers on the remote vulnerable host for two conditions: the container has an ubuntu:18.04-base and it doesn’t contain a version.dat
file, which is an indicator that the container had been previously infected.
If these conditions are satisfied, the malware executes the main.operateOnContainer
function to proceed with the same attack vector described earlier to infect the running container. The infection chain is repeated, hijacking the container resources to scan and compromise more containers and mining for the Dero cryptocurrency.
That way, the malware does not require a C2 connection and also maintains its activity as long as there is an insecurely published Docker API that can be exploited to compromise running containers and create new ones.
cloud – the Dero miner
Executing and maintaining cloud
, the crypto miner, is the primary goal of the nginx
sample. The miner is also written in Golang and packed with UPX. After unpacking the binary, we were able to attribute it to the open-source DeroHE CLI miner project found on GitHub. The threat actor wrapped the DeroHE CLI miner into the cloud
malware, with a hardcoded mining configuration: a wallet address and a DeroHE node (derod) address.
If no addresses were passed as arguments, which is the case in this campaign, the cloud
malware uses the hardcoded encrypted configuration as the default configuration. It is stored as a Base64-encoded string that, after decoding, results in an AES-CTR encrypted blob of a Base64-encoded wallet address, which is decrypted with the main.decrypt
function. The configuration encryption indicates that the threat actors attempt to sophisticate the malware, as we haven’t seen this in previous campaigns.
Upon decoding this string, we uncovered the wallet address in clear text: dero1qyy8xjrdjcn2dvr6pwe40jrl3evv9vam6tpx537vux60xxkx6hs7zqgde993y
.
Then the malware decrypts another two hardcoded AES-CTR encrypted strings to get the dero node addresses via a function named main.sockz
.
The node addresses are encrypted the same way the wallet address is, but with other keys. After decryption, we were able to obtain the following addresses: d.windowsupdatesupport[.]link
and h.wiNdowsupdatesupport[.]link
.
The same wallet address and the derod node addresses had been observed before in a campaign that targeted Kubernetes clusters with Kubernetes API anonymous authentication enabled. Instead of transferring the malware to a compromised container, the threat actor pulls a malicious image named pauseyyf/pause:latest
, which is published on Docker Hub and contains the miner. This image was used to create the malicious container. Unlike the current campaign, the attack vector was meant to be stealthy as threat actors didn’t attempt to move laterally or scan the internet to compromise more networks. These attacks were seen throughout 2023 and 2024 with minor changes in techniques.
Takeaways
Although attacks on containers are less frequent than on other systems, they are not less dangerous. In the case we analyzed, containerized environments were compromised through a combination of a previously known miner and a new sample that created malicious containers and infected existing ones. The two malicious implants spread without a C2 server, making any network that has a containerized infrastructure and insecurely published Docker API to the internet a potential target.
Analysis of Shodan shows that in April 2025, there were 520 published Docker APIs over port 2375 worldwide. It highlights the potential destructive consequences of the described threat and emphasizes the need for thorough monitoring and container protection.
Docker APIs published over port 2375 ports worldwide, January–April 2025 (download)
Building your containerized infrastructure from known legitimate images alone doesn’t guarantee security. Just like any other system, containerized applications can be compromised at runtime, so it’s crucial to monitor your containerized infrastructure with efficient monitoring tools like Kaspersky Container Security. It detects misconfigurations and monitors registry images, ensuring the safety of container environments. We also recommend proactively hunting for threats to detect stealthy malicious activities and incidents that might have slipped unnoticed on your network. The Kaspersky Compromise Assessment service can help you not only detect such incidents, but also remediate them and provide immediate and effective incident response activities.
Indicators of compromise
File hashes
094085675570A18A9225399438471CC9 nginx
14E7FB298049A57222254EF0F47464A7 cloud
File paths
NOTE: Certain file path IoCs may lead to false positives due to the masquerading technique used.
/usr/bin/nginx
/usr/bin/cloud
/var/log/nginx.log
/usr/bin/version.dat
Derod nodes addresses
d.windowsupdatesupport[.]link
h.wiNdowsupdatesupport[.]link
Dero wallet address
dero1qyy8xjrdjcn2dvr6pwe40jrl3evv9vam6tpx537vux60xxkx6hs7zqgde993y
Dero miner zombies biting through Docker APIs to build a cryptojacking horde