Threat research · Capture 1 of 2

A self-propagating Go ransomware worm

Not a payload something else delivers. The scanner, the SSH brute-forcer, the encryptor and the ransom-note generator are all one binary. It lands, encrypts, then hunts the next host itself.

Captured 2026-05-06 05:49 UTC, SSH honeypot sensor
SHA-256 2fc0a056fd4eff5d31d06c103af3298d711f33dbcd5d122cae30b571ac511e5a
File 5,329,044 bytes — ELF 64-bit x86-64, statically linked, stripped
Analysis Static only. Nothing executed, nothing made executable, no attacker-supplied URL fetched.

Nothing was encrypted and no sensor was ever at risk. The honeypot is a fake shell — it recorded the session and wrote the uploaded bytes to disk. Nothing ran. The binary has sat inert since May.

It spreads itself

The binary contains its own credential attack. This is the part I missed on a first pass, and it is the part that changes what containment means.

[+] SUCCESS %s:%d  user=%s  pass=%s        <- scanner success output

static list      123456 ubuntu deploy tomcat qwerty abc123 111111
                 master monkey dragon
templated        {{user}}123   {{user}}!@#   {{user}}@123
                 {{user}}1234  {{user}}2024  {{user}}2025  {{user}}2026
targets read     known_hosts*      /etc/passwd      PRIVATE KEY
transport        full Go SSH + SFTP + SCP stack, mlkem768x25519-sha256

The templates build passwords from the account name currently under attack — deploy becomes deploy123, deploy@123, deploy2026. Combined with reading known_hosts, it walks outward along whatever SSH trust the compromised host already had.

That combination is the whole trick, and the two halves compound. known_hosts is a list of machines the box has already connected to — so it is not scanning the internet, it is walking a trust graph a human administrator built by hand. Those hosts are the most likely to accept a connection and the most likely to share credential conventions. /etc/passwd then supplies the account names, and the templates mint passwords from them. It is not guessing from a generic wordlist. It is guessing from your naming convention.

Operational consequence: containment is not "rebuild the encrypted box." Every host in that box's known_hosts, every account whose name is guessable from its own password, and every private key that was sitting on that disk are all in scope at the moment of infection. Restoring from backup addresses none of them. The credential rotation is the actual remediation.

How it arrived

There was no download URL, because it was not downloaded. It was uploaded over SFTP, three times, with an attempt to start it after each:

05:48:07  first login
05:49:49  SFTP upload   .sorry_YkZ2nVSo
05:50:02  chmod +x /tmp/.sorry_YkZ2nVSo
05:50:02  nohup /tmp/.sorry_YkZ2nVSo >/tmp/.sorry_oYThhvN5.log 2>&1 &
05:52:13  SFTP upload   .sorry_jOpj30TI     (same sequence)
05:52:49  SFTP upload   .sorry_oUs2nsy4     (same sequence)

Dot-prefixed filenames hide from a bare ls. .sorry_ is presumably the operator's marker; .sorry is also the extension the encryptor appends.

Correction. I first wrote that this was hands-on-keyboard, inferring a person from "three retries with fresh filenames." That was wrong, and the client fingerprint is what disproves it:

SSH client   SSH-2.0-Go   x14 sessions
credentials  root/password x4   root/(empty) x4   root/toor x4
timing       first login -> payload started in 1 minute 42 seconds

SSH-2.0-Go is an automated Go client — the same language as the payload, which suggests deployer and ransomware are one toolchain. The credentials are trivial defaults tried in sequence. 1m42s is machine speed. The three uploads are a retry loop, not a person getting impatient.

The evidence for calling it ransomware

Crypto. ChaCha20 (25 string hits) paired with X25519 (6 hits). That pairing is the current standard construction: a per-file symmetric key, wrapped to an operator public key so only they can unwrap it. Modern and correct, not amateur.

Victim-facing vocabulary. decrypt ×14, RECOVER ×10, tox ×12, .locked, onion. Tox is the tell — a peer-to-peer messenger ransomware crews use for negotiation precisely because it has no infrastructure to seize.

Target extensions. Virtualisation (.qcow2 .nvram .vmdk .vhdx), mail stores (.edb .ost .pst), SQL Server data, logs and backups (.mdf .ldf .bak), database dumps (.sql .dbf), and outliers like .sas7bdat — SAS statistical data. Nobody targets SAS datasets or VMDK disks on a consumer laptop. This is aimed at servers.

Processes it kills. mysqld, sqlservr, oracle, postgres, postmaster, mongod, redis-server, plus systemctl stop mariadb and service cassandra stop. Stop the database so its files unlock, then encrypt them.

What it spares, so the box still boots and the victim can still pay: /boot/, /proc/, /lost+found, .cache, node_modules, __pycache__.

No contact address is compiled in

A search for a Tox ID — 76 uppercase hex characters — returns zero matches. The note template contains "Our TOX ID: %v", a runtime substitution, and the binary carries config, embed and command-line flag paths for supplying it. The victim identifier is sorry_id_%v%v, seeded from the hostname.

This is an affiliate build: one builder, many operators, contact address supplied per build. A Tox ID recovered from one victim does not identify the operator behind another, and the per-victim ID is what maps a payment to a decryption key.

What I can and cannot say

ClaimConfidence
This is ransomwareHigh. Encryption primitives, victim-recovery vocabulary, a target-extension list and a database kill-list in one binary. Any one is ambiguous; together they are not.
It is aimed at servers, not consumer machinesHigh. The extension and process lists are specific and unambiguous.
The kill-list is inherited, not originalMedium. sqlbrowser/xfssvccon/ocautoupds/thebat is a widely-reused list that has propagated across families for years. It indicates lineage, not identity.
The familyNot established. The binary is stripped and obfuscated with garble — source filenames are garbled, symbols randomised. Naming a family here would be a guess dressed as a finding.

On the source address

The upload came from a rented budget VPS in the United States, on a reseller range under ColoCrossing infrastructure. It showed 15 attacks against the sensor across six days in May and has been silent since.

I am not publishing the specific host address, and the reasoning is the point. It identifies a box, not an operator — cheap VPS resellers are the standard disposable launch platform, and the actual party behind it is not determinable from anything this sensor saw. The address has been inactive for three months and has correctly aged off the published blocklist, so naming it adds no defensive value to anyone. What it would do is attach "delivered ransomware" to a specific rented address, permanently and irreversibly, on the basis of six days of activity a quarter of a year ago. Whoever holds that address now is likely not whoever held it then.

The hash above is the useful artifact and it is published in full. If you are investigating an incident that matches this pattern and the source address would help, the contact address on this site works.

Remaining gaps

Not unpacked or unstripped. Go plus garble means recovering meaningful symbols is real work, not a upx -d. Until that happens, the family stays unnamed.

No external lookups performed. The hash was not submitted to any multiscanner. That is a deliberate hold under the handling policy, not an oversight — uploading a sample makes it publicly downloadable, and even a hash query is visible to the platform and to anyone watching it.

Next: RedTail — one dropper, every architecture →