RedTail · Chapter 6
The miner that hides its wallet
We unpack RedTail's miner and crack it open in Ghidra hunting for the pool and the wallet. What we find is more interesting than a number: the data is in there, embedded and encrypted — and nobody has published how to crack it.
In Chapter 5 I caught RedTail letting itself in with its own key. Now for the binary: unpack it and open it in Ghidra with a clear goal — to pull out its mining pool and its Monero wallet. Honest spoiler: the answer isn't a number, it's why that number isn't there.
The aim is to find where the money goes and what it depends on, so it can be detected and cut off.
01Peeling off the wrapper
The binaries came packed with UPX and with no section headers — which is what happens to any ELF packed with UPX, not a trick of theirs. upx -d doesn't need them: it decompresses using its own structures.
$ upx -d x86_64 -o x86_64.unpacked
File size Ratio Format Name
-------------------- ------ ----------- -----------
5199952 <- 1989056 38.25% linux/amd64 x86_64.unpacked
Unpacked 1 file.From 2 MB compressed to 5.2 MB in the clear. Wrapper off, time to look inside.
02It's a custom XMRig
The strings in the clean binary leave no doubt: RandomX, cryptonight, donate.v2.xmrig.com… it's a fork of the open-source miner XMRig. But with two additions that give it away as RedTail:
- A library of its own, libredtail, with evbuffer_tls — its TLS network layer for talking to the C2.
- The author's build path, embedded: /var/build/redtail/scripts/x86_64-build/ — with hwloc 2.14.0, snappy 1.2.2, abseil-cpp. A modern, well-kept build environment. The project is called, literally, "redtail".
What they haven't touched is the engine. XMRig's whole core is still in there: the five RandomX variants (rx/0, rx/2, rx/arq, rx/aH, rx/af), thirteen CryptoNight ones and the three argon2 flavours (chukwa, ninja, wrkz), with support for Monero, Graft, Wownero, Zephyr, Townforge, Sumokoin, Arqma and Ravencoin. A miner capable of all that, dedicated to a single coin. Hold on to that detail, because in section 06 it explains rather a lot.
03Hunting the loot — and the wall
With the binary open, I went after the pool and the wallet down every path. One by one, they all hit a wall:
- An embedded encrypted blob? Entropy scan of the 5.2 MB → zero high-entropy regions. There's no hidden encrypted block.
- The pool/wallet in the clear? No. The only plaintext is XMRig's own templates (stratum+ssl://%s) and its default donate domains.
- An IP or domain of its own? Not one. I even searched for the delivery IP (217.60.195.113) as text and as raw bytes in every ordering. Not there.
- Some odd config parser? No: it's the standard XMRig JSON parser. The miner expects to receive a config, it doesn't carry one.
Ghidra confirmed what the strings hinted at: the functions that build the pool URL (Pool::parse, stratum+tcp/ssl) are the same old XMRig ones, fed by a configuration that arrives from outside.
04The verdict: the wallet isn't there, by design
Putting the pieces together — a TLS layer of its own (libredtail), no encrypted blob, no embedded pool/wallet/C2, standard config parser — the conclusion is clear and it's RedTail's signature:
This is what separates RedTail from off-the-shelf malware: the others hid the secret inside and it was enough to read it properly (Chapters 2 and 4). RedTail doesn't hide the secret: it doesn't carry it at all. Knock over its C2 and the miners already deployed are left with nowhere to send the money — but they won't tell you either.
05Where the line is
Actually pulling out the wallet would demand one of two routes, and both are off the table:
- Run it in an isolated lab and watch which C2 it calls and what config it decrypts. That's the route that would give the answer — but it's a live miner, and running it crosses the line of this diary (and puts the machine at risk). Not done.
- A far deeper trace of how libredtail builds the C2 address (probably assembled in memory piece by piece). Hours of reverse engineering, with no guaranteed prize.
I'd rather tell you what we know for sure — that the data isn't there, and why — than force an answer. Acknowledging the wall is part of the craft too.
06I went back — and the wall moved
What the rest of the world says
The first thing I did was what I should have done earlier: check whether anyone had been down this road. The answer surprised me.
Nobody has ever published how to decrypt RedTail's configuration. And it isn't that I searched badly: Akamai say so in writing. They're the reference for this family, and their report explains that they pulled the pools by looking at the miner's memory while it was already running, "avoiding the lengthy process of reverse engineering the decryption". In other words: they hit the same wall and went around it.
There's more. Malpedia, the reference catalogue, doesn't have a single detection rule for RedTail. There's no configuration extractor in any public framework. And the detail that struck me most: in two years not one single Monero wallet from this family has been published. Not one. What is constant across all its known infrastructure is port 2137.
Where I got it wrong
Above I wrote, with great confidence, that RedTail "asks its command server for them on the fly". I now think that's wrong, and the evidence was right in front of me.
XMRig, the legitimate miner RedTail is a modified copy of, is configured by command line or by file. It has keys for everything: url, algo, coin, config… I went looking for them in RedTail's binary and a handful are missing — but not the ones I said. Counted over the raw bytes: nineteen remain, url among them, and six are gone.
And it's that six that tells the story, because it isn't a random amputation. Missing are config, coin, algo, user, cpu and tls: exactly the ones that would let you steer it. Without config it reads no configuration file. Without coin or algo you can't switch its currency. And without user you can't put another wallet on it. The operational ones — threads, huge pages, retries, logging — are all still there. They didn't take away its interface: they took away the steering wheel.
Think about that for a second: this miner has had the ability to be configured from outside surgically removed. And why would anyone do that? If the configuration arrived from the C2, there'd be no need to delete anything — the miner could keep accepting parameters and it wouldn't matter. You amputate when the data is inside and you don't want anyone changing it, reading it, or swapping in their own.
And that matches what Akamai and the other firms hold: the configuration is embedded and encrypted, and gets decrypted in memory at startup. And the clean entropy scan from section 03? No contradiction: that sweep was coarse-grained, built to find big blocks — and a config blob is so small next to 5.2 MB of binary that it never breaks the surface. My conclusion that it came from the C2 rested on a single source that now looks thin.
What I have ruled out
If the data is inside and encrypted, the next question is with what. And here I do bring something nobody had published.
It isn't XOR. Not a single-byte key, not a repeating one, and I don't say that on a hunch. There's a neat statistical test for this: if you take ordinary text and encrypt it with a repeating key, then compare the result against itself shifted by exactly the key length, the top bit of every byte always comes out zero. In random data it comes out zero half the time. So you can sweep a whole file looking for that signature without knowing the key.
I ran it over every data section of the binary, testing key lengths from 1 to 40. Not one region tests positive. The only hits were false ones, and rather charming: conversion tables, lists of numbers… and a chunk of Lorem ipsum that comes from the test suite of one of the libraries it links in.
The map, as far as I got
And here's the ground gained, for whoever comes next — myself included:
0x415b60 main # not the one the decompiler claims: you have to
# read the startup assembly to find it
0x446e8c ... # calls the loader with the config already built
0x4457c0 loader # reads the "pools" key and builds the targets
0x460ba0 Pool # uses "rig-id" and "self-select"
0x459e80 reconnect # handles the pool's "client.reconnect"
??? decryption # <- this is where I stoppedThe missing piece is in the middle: whatever turns those encrypted bytes into the JSON the loader reads. And I don't have it for two concrete reasons. The first is that the loader isn't called directly but through a pointer table, so the trail breaks and has to be rebuilt by hand. The second is brute force: it's 5.2 MB of heavily optimised C++ with OpenSSL and half a dozen libraries inside, and the decompiler chokes — it hands back functions full of blocks it can't resolve. From there on it's reading raw assembly, and that runs at hours per function.
07Why I'm leaving it here (for now)
This wall isn't like the wallet one. That was final: the data doesn't exist in the file, and no amount of reverse engineering extracts what isn't there. This one is different — it's a wall made of cost. The data is in there, I know which way leads to it, and what's missing is hours. Many of them.
And I've chosen to tell you like this, map half-drawn, rather than lock it in a drawer until it's complete. For two reasons. One, because what's been ruled out is useful too: if somebody picks this up, they no longer have to waste a morning trying XOR. And two, because it seems more honest to show an investigation as it really is — open, with ground gained and ground still to gain — than to pretend chapters come out finished on the first pass.
The binary isn't going anywhere. I'll be back.
08Indicators (IOCs)
| Type | Value |
|---|---|
| Family | RedTail · Monero miner (XMRig fork) |
| Packing | UPX with section headers wiped |
| Own library | libredtail (TLS / evbuffer_tls) |
| Build path | /var/build/redtail/scripts/x86_64-build/ |
| Config | embedded and encrypted · NOT XOR (ruled out statistically) |
| Amputated interface | the 6 keys that would let you steer it are gone: config · coin · algo · user · cpu · tls (19 remain, url among them) |
| Engine | XMRig core, complete: RandomX ×5 · CryptoNight ×13 · argon2 ×3 |
| Their pools' port | 2137 (constant across the family's known infrastructure) |
| SHA-256 (miner x86_64) | f0aa83bbbd2c75e2f71ec16029ee5fcfad59f3a8efa30a500b815f0f6c18d987 |
And the honest summary of this chapter is that the wallet isn't there, that the "it isn't XOR" still stands, and that the wall which stopped me was one of cost, not of impossibility — what gets ruled out also serves whoever tries next.
To be continued — the bait's still lit. When the next critter brings something new, there'll be a seventh chapter. 🍯
Comments