North Korea's Safari: Poaching for Armadillos
How we discovered POWerful Armadillo
Poaching is bad, let’s start by stating that. But you know what’s worse? Developing malware for a dictatorship.
Of all the fates in the grand scheme of things, you drew the worst one possible: a modern slave trapped in a never-ending loop of defrauding ordinary people, companies, and even yourself. Because at the end of the day, I doubt your inner child ever yearned to become some wacko’s personal vibe coder.
On top of that, a group of weirdos hunts you like day hunts night. And once again, dawn has caught you outside your burrow.
This is the story of how we discovered (and named) POWerful Armadillo, a new DPRK malware.
Thanks to our loyal sponsor, Famous Chollima, for sharing their samples with us.
Armadillos & Quetzals
We’re used to seeing these muppets try every trick in the book, and write most of the new ones that end up in it. They may not be the most technically sophisticated actors out there, but they are definitely creative, and this campaign was no exception.
For this operation, they shifted to using compromised WhatsApp accounts to distribute a fake WebEx installer in DMG format for macOS. Once executed, you’re presented with the typical application window, but labelled “Drag to Terminal to Install.xyz”. Of course, doing so executes the “installer” in your Terminal, with your privileges and the same access level as your user.
But we’re not doing that, you mischievous pony head. You know what time it is?
Time for disassembling. Your malware, your campaign, and your dreams.
The .xyz installer is actually a Bash script that downloads the malware loader via cURL. It is interestingly disguised as a publicly accessible ASPX file, but in reality it is just another Bash script.
For the sake of simplicity, we’ll shorten the component names to four letters plus extension, as the original names are excessively long.
The first component, PNZF, downloads three additional stages: 4NWS, 2KVS and 01HY. The first and the last are piped into Bash, while the second is piped into osascript, indicating that it is in fact JavaScript code.
Let’s reconstruct the infection chain.
The second stage downloads three additional components, all written in JavaScript: T65A, DQEZ and 0CRW. Opening them, we find a rather predictable surprise: they’re obfuscated. But we know exactly which deobfuscator to use.
Does that ring a bell? It’s the exact same one they always use, and the same one we documented in our reports on BeaverTail and OtterCookie. Old habits die hard.
Component T65A prompts the user for their credentials, stores them, and replays them whenever necessary using the native macOS utility dscl.
dscl (Directory Service command line) is normally used to interact with the system’s directory services, including managing user accounts, groups, authentication data, and other attributes stored in the local directory or network directory services such as LDAP or Active Directory.
Coincidentally, DQEZ is virtually the same component, just with different deobfuscation output, but it follows the exact same execution flow.
I told you these weren’t the brightest bulbs in the house.
Moving on, 0CRW is where the real action starts. It defines a C2 server with a specific endpoint, derives a hardware-unique ID for the infected machine, and uses it to identify the host to the C2. It also runs tccutil to reset TCC permissions on the host and dumps both Apple Notes and small files from ~/Desktop, ~/Downloads, and ~/Documents, then zips and ships everything to the C2.
Classic behaviour so far, but there’s a catch: the server won’t just receive the data as-is, it will require a PoW (Proof of Work).
We’ve never seen DPRK malware require Proof of Work to protect their C2 from our spammy, friendly interview requests. That detail stuck with me for a while, so we gave it a name: POWerful Armadillo.
A challenge, then? I’m always up for one. But let me finish kicking over your sandcastle first, then I’ll move on to stomping on your shovel and bucket. We’ll be back here in a minute.
Let’s backtrack a little, as we still have two more components to explore: 2KVS and 01HY, which were loaded in the first stage.
2KVS is the stealer. It targets macOS Keychain, a long list of desktop crypto wallets/configs, browser credentials and cookies (Chromium + Firefox, including extension storage), and Telegram Desktop data, then stages everything under /tmp, zips it, and uploads it to the C2 (again with PoW gating).
01HY establishes persistence by creating a per-host LaunchAgent tied to the machine’s Hardware UUID. It downloads a JXA payload (“52VH”), stores it under Application Support, and ensures it runs at login via osascript, with KeepAlive enabled for automatic relaunch. Let’s see what 52VH does.
After deobfuscation, we’re met with the final piece of the malware. 52VH is the persistent JXA C2 agent: it fingerprints the host, polls the server, solves PoW challenges, executes remote tasks (Bash/AppleScript/JXA), and acknowledges them back to the C2.
And speaking of the devil. I haven’t forgotten about that little Proof of Work challenge the ponies decided to implement.
What do they know about hard work? Parasites in a weird uniform with weird haircuts. Let’s break that toy too.
Cracking open the Armadillo
As we’ve seen before, every interaction with the C2 is gated by a JSON response containing a “challenge” and a “complexity” level. By reading the code, we can see exactly how both values are calculated.
First, the server expects us to calculate a “nonce” (a number used only once). In this case, it’s simply an incrementing integer: the malware starts at zero and keeps increasing the value until it finds one that satisfies the server’s condition, formatted as:
<nonce>-<challenge>It then computes the SHA-256 hash of that string. The server requires the resulting hash to begin with a specific number of leading zeroes in hexadecimal form. The number of required zeroes is determined by the complexity value. For example, a complexity of 1 requires the hash to start with a single “0”, while a complexity of 3 requires three leading zeroes.
Because cryptographic hashes are unpredictable, the only way to find a valid nonce is through brute force. The implant starts at zero and increments upward until the hash output meets the requirement. Once a valid nonce is found, it sends the solution back to the server and receives a token.
Now that we understand how it works, we can grab one of the modules where this calculation is already implemented and “neuter” it, stripping out any harmful functionality and sanitising sensitive information, while keeping the communication logic intact.
This would allow us to arbitrarily upload fake information to their C2, effectively spamming it at will. But you know the saying: new year, new me. This time, I choose to be the bigger person.
Just kidding. Let’s terrorise those invertebrate vermin.
Hardcoding different values as “REGARDS-FROM-THE-QUETZALS” and crafting a specific zip file with fake information about a fake person, we start spamming their C2 relentlessly.
For obvious reasons, we cannot disclose that, in order to arbitrarily upload files to this campaign’s servers, you simply need to run a POST request, followed by a PUT and then another POST to the endpoints depicted in this article, using the PoW resolution method we discovered. Don’t try this at home.
Well. This edition has gone on long enough. Before we jump into the IOCs, let me remind you of the basics.
Stay safe.
Don’t let spies in.
Cyberbully your local threat actor.
Spread love, not malware.
And please: Don’t get rekt.
IOCs
IPv4:62.60.226.225
Domain:hoplokiroute[.]com
Domain:hylb9pbsjaqkl03g75jomhrsitz0msicjttolxo[.]pages[.]dev
URL:hxxps://hoplokiroute[.]com/SifFSYC0iyKGie87L0tjg
URL:hxxps://hoplokiroute[.]com/wuZpFOkrxP7ih1q9wIMVOq5pPVq
URL:hxxps://hoplokiroute[.]com/hicMvddp7NAl80BkWFES19KfRs
URL:hxxps://hylb9pbsjaqkl03g75jomhrsitz0msicjttolxo[.]pages[.]dev/vlorKq3ETMPVqR9phyou88U7bPj7rHcqPft1yMxScQJiAOyV9BAJ8OpKO5Vw6SnG0Ok4nwS[.]aspx
URL:hxxps://hylb9pbsjaqkl03g75jomhrsitz0msicjttolxo[.]pages[.]dev/KqsbHMd9o7Z8AExWh1nD9mL6LXQSL8z1euhGi4PLxvPMujgNCOTbWlCrErzX9NVcwM7X2kVS[.]aspx
URL:hxxps://hylb9pbsjaqkl03g75jomhrsitz0msicjttolxo[.]pages[.]dev/RBVr0mPp65COR4S6tThFCYIiK3ghS7A6BZimoZOHyf4MnmyRTjKxasnaKyZ9OKWIUy6O1hy[.]aspx
URL:hxxps://hylb9pbsjaqkl03g75jomhrsitz0msicjttolxo[.]pages[.]dev/iM7SGHcIcB6ZPZJRUlsu3DRR045tUbWN4mjh2inIyhAa2sXD0U3K1xlMzBjCAAKRrvCCW0q4yS6TT65a[.]aspx
URL:hxxps://hylb9pbsjaqkl03g75jomhrsitz0msicjttolxo[.]pages[.]dev/DYnBfm4yfByVEgqXSq27k67EAsq5hZeZYY8PaTAWok3YlBmi4XZeZUvkdVboEUCm1tUDqeZ[.]aspx
URL:hxxps://hylb9pbsjaqkl03g75jomhrsitz0msicjttolxo[.]pages[.]dev/xFj6OFibJ2V9C2z0g9Aa7FK5vPiUNf17NK9KLs2ZrZaAuy2h8dKomXKTBbwrjtV40crW[.]aspx
URL:hxxps://hylb9pbsjaqkl03g75jomhrsitz0msicjttolxo[.]pages[.]dev/Bc5Tg1EIVZYOgIJVIcV31JZUwXAhc4Hv7DIereUloKjH8Uhnw9vsQY9oq175PbclUjwm6bg52vH[.]aspx
SHA256:f5669d80eb52f8b6fc90f5c5db98182e7d5297073f120a67b22700bf88c17d27
SHA256:318792ed0fcb64059670956468d7e0ef62edb15c967cd1f13fa8774e5e5c28ae
SHA256:66c3d0eaf68dcc97c092ce48ed65df49a8dcb7e1c3e2137f98ad16826ee5ef8d
SHA256:0c47f6db79f5c4db86227b1fba4528ca8c2f8a1e86302863add8fd3f966b1b30
SHA256:e4677a8fb20517393a761c49075f0e4fdfe80f28f6bdeacb246e7d9b3858542f
SHA256:7f78ce8bd2c7e2ccd71fc62bcfb29ce5b0d91efaff4c51f629195efb0293184d
SHA256:ee93c0caa82ed4b362b1f13b230687cf403fea83f7a7e3090c9bbe08955878c4
SHA256:91c47f8ddb5a937c461bee6021569544e6fa009e103e6f34b59b9d342338b76d
FileName:/tmp/exec_throttle.lock
















