A Random Filename, Six Years of Infection
Four files in my honeypot’s sample directory were exactly 4,766 bytes each. Same size, four
different dates across June and July, four different SHA-256 hashes. The file command
called all of them data. Not text, not ELF, nothing it could name.
They contain the same payload, byte for byte. Eight bytes separate them, and those eight bytes have nothing to do with the malware.
My first assumption was that this broke hash-based detection. A random value inside the captured artifact would produce a worthless indicator every time, or so I thought. VirusTotal disagreed. It knows all four hashes, and it has known one of them since November 2019.
Those eight bytes are not noise. They fingerprint individual infected hosts across years and across unrelated sensors.
The sensor
A t3.nano running Ubuntu 24.04 on EC2, deployed with Terraform, running the SANS Internet Storm Center DShield stack with cowrie emulating an SSH shell. I left it collecting for three months without looking at it.
Five files stood out by size alone.
| |
Five deliveries in five weeks. Four at an identical size the file command refused to
identify, and one 21 bytes shorter that it correctly recognized as a bash script.
Reading the first 32 bytes
Encrypted content was the obvious guess. A hex dump ruled it out immediately.
| |
That is the SCP protocol, not encryption. When a file is pushed with scp, the sender
transmits a control line ahead of the data:
| |
Mode 0755, size 4745, filename TZIfqXL9. The data stream terminates with a single null
byte. The arithmetic resolves cleanly: 20 bytes of header, 4,745 bytes of payload, one null
byte, 4,766 total. Cowrie stores the raw wire stream with the header intact and names the
file on disk after the SHA-256 of the whole thing.
Recovering the usable script takes one command.
| |
The fifth sample, at 4,745 bytes, arrived through a different mechanism and was stored without a wrapper. A header-stripped extraction from any of the other four matches it exactly, across its full length. One payload, five deliveries.
Where the filename comes from
Comparing two wrapped samples byte by byte isolates the difference precisely.
| |
| |
Eight differing bytes at offsets 12 through 19, which fall inside the filename field of the
SCP header. TZIfqXL9 in one sample, a different eight characters in the next.
The payload is a self-propagating SSH worm. Its propagation loop is short enough to read in full:
| |
Two things are fixed here rather than random per delivery. $NAME is computed once, outside
the infinite loop. More importantly, so is $MYSELF, and tracing where it comes from is what
makes the header meaningful.
The worm reaches a victim over scp, then runs over SSH as the pi user. That user is not
root, so the first branch of the script executes:
| |
$NEWMYSELF is drawn on the victim itself. After the reboot, rc.local launches
/opt/$NEWMYSELF as root, $EUID is zero, and the propagation branch runs. From that point
on, for the lifetime of that machine’s infection, $MYSELF is /opt/$NEWMYSELF.
The last link is a property of the SCP protocol rather than of the worm. In a transfer, the sender announces the basename of the source file, not the destination. The receiver writes to the destination path because the target is a file, but the name on the wire is the source’s. One command demonstrates it:
| |
The destination is BBBBBBBB, the announced name is AAAAAAAA.
So the filename in the captured header is $NEWMYSELF: a value drawn once, on the attacking
machine, at the moment that machine was compromised, and invariant for every attack it
launches afterwards.
The VirusTotal timeline
If that reading holds, each wrapped hash should have a sighting history that predates my capture, because every other honeypot the same host ever reached recorded identical bytes.
| Hash | Captured here | First seen | Last seen | Submissions | Detections |
|---|---|---|---|---|---|
d946dbc576… | 2026-06-30 | 2019-11-26 | 2026-06-19 | 14 | 40/74 |
783565adc1… | 2026-06-05 | 2021-07-01 | 2026-05-02 | 20 | 39/75 |
b4c8f6e4e5… | 2026-07-09 | 2021-10-01 | 2025-10-08 | 6 | 39/76 |
e63969f07e… | 2026-06-17 | 2023-05-20 | 2026-03-30 | 9 | 40/75 |
6d1fe6ab3c… (payload) | 2026-06-07 | 2017-06-14 | 2026-07-10 | 55 | 40/74 |
Every one is known, well detected, and repeatedly submitted over years by parties unrelated to me.
The first two columns are the finding. The host that reached my sensor on 30 June 2026 has been distributing the same file under the same name since 26 November 2019. Six years and seven months of continuous, unremediated infection. The other three have been running for three to five years.
Two smaller details sharpen the picture. The b4c8f6 sample had not been submitted anywhere
since October 2025, so my capture is the most recent public sighting of that host. And
d946db was submitted by someone else on 19 June, then reached me on 30 June, which places
that machine in the middle of an active sweep.
The payload itself was first submitted on 14 June 2017, eight days after Dr.Web published its analysis of the family. It has been submitted 55 times since, most recently in July 2026.
A note on naming
Detection labels disagree, as they usually do. Dr.Web flags this file as Linux.MulDrop.16,
VirIT as Linux.MulDrop.Q, Microsoft as Backdoor:Linux/IRCbot.YA!MTB, Symantec as
IRC.Backdoor.Trojan. VirusTotal’s derived consensus is trojan.ircbot/shell. The family is
commonly known as MulDrop.14 after the 2017 write-up, but the numbering has moved since, and
the hash is the only stable reference.
What the wrapper is worth
Treated as intelligence rather than as noise, the SCP header supports four things.
It bounds the age of a compromise. The first-submission date of a wrapped hash is the first
time any sensor anywhere caught that host, which places an upper bound on its infection date:
the machine behind d946db was compromised no later than November 2019, and possibly well
before. That is enough to separate a host compromised last month from one that has been
spreading for years.
It clusters deliveries by source. Two captures sharing a wrapped hash came from the same infected host, even when the source IP is long gone. That matters more than it sounds: cowrie’s default log rotation is short, and mine had already discarded the sessions for four of these five deliveries. I do not know a single source address for them. I do know, from the four distinct wrapped hashes, that they came from four distinct machines rather than repeat visits by one.
It connects a local sighting to a public timeline. Because the artifact is byte-stable, an observation joins the record maintained by every other sensor that host has touched.
And it measures remediation. A wrapped hash still appearing years after its first submission is a machine nobody has cleaned.
Limits
The claim that one wrapped hash means one infected host is the reading best supported by the code, but it is not proven, and three alternatives deserve mention.
mktemp -u draws from a pseudo-random generator, and poor seeding on minimal embedded
systems could produce name collisions between genuinely distinct hosts. A single long-lived
machine behind a dynamic address would be indistinguishable from a stable one. And a host
that reboots into the persistence path may or may not regenerate its name, a branch I have
not exercised.
Confirming the mechanism means running the worm in an isolated lab and observing name stability across reboots and across simulated victims, which I have not done. There is also a ceiling on the public evidence: VirusTotal’s per-submission timeline sits behind the premium tier, so first-seen, last-seen and submission counts are all I can show. A full timeline of sightings spread across six years would carry more weight than two endpoints and a total.
A correction
I began this analysis convinced the SCP header broke detection. Unique artifact hash per delivery, reputation lookups coming back clean, threat feeds filling with indicators that would never match again. That was wrong on every count. All four wrapped samples score 39 to 40 detections, and vendors handle the wrapper without difficulty.
One piece of the original idea survives. Deduplicating by artifact hash does overcount: four files in my sample directory represented a single payload, and any figure for unique samples observed that is built on raw cowrie filenames will be inflated. Normalization before counting is still worth doing.
But the header is not something to strip and discard. Strip it to analyze the payload, and keep it to identify the source.
Indicators
| |
Both keys are hardcoded in the payload and identical across all five deliveries. They identify the family rather than an individual host, which makes them the right indicators for fleet-wide hunting.
Takeaway
Honeypot captures carry more than the payload. Transport framing, control lines and filenames are all data the attacker produced without meaning to hand it over. Strip the header to analyze the payload, keep it to identify the source.
Samples were captured on a dedicated, isolated, disposable honeypot. Files of this kind should never be handled on a workstation. The payload is not reproduced here: it is functional and self-propagating.