Here we are again.
The Quetzal Team just returned from DEF CON Las Vegas after delivering talks and workshops about North Korean malware at the Adversary, Red Team, and Malware Villages, and having the honour of conquering the Main Stage.

This edition was meant to recap all that, to be honest, but something (or someone) decided to get in the way… 🐴.
Not even a week after returning, while we were trying to get the slides and recordings ready for publication, a new malware with no track record decided to measure itself against us. And we took that personally.
So this edition has shifted from a happy Hacker Summer Camp recap to a story from the trenches about how we blew up the infrastructure of a new malware component that we didn’t even have the time (or the will) to name yet. So, the DEF CON special will have to wait another week. Never a dull moment on this Team.

Ah, those were peaceful times. So anyway, put on your safety glasses.
We’re about to blow things up.
The Rabid Toy
The sample we’ll review today is in no way comparable to Arlt’s writings. Yet, it behaves like a little rabid toy. It made its way to us through an old classic excuse: a job offer targeting Alex, one of our developers. He played along with the fake job interview and, in the end, was given a repository as part of a coding challenge, which he then shared with us to investigate.
If you’ve been following along, you know where this ends: the coding challenge is laced with malware and will trigger something nasty like InvisibleFerret, BeaverTail, OtterCookie, or one of the many other malware strains from our friends in boring Korea. We’ve reversed these samples many times as part of our talk series, “North Korea’s Zoo” and “North Korea’s Safari”, at DEF CON and BSides Pyongyang.
But this time, it is different!

The infection starts with the “setup” file, which acts as a stager and differs between Windows (PowerShell) and Unix/macOS (Bash script). It creates a hidden folder to camouflage the downloaded binaries as if they were associated with Visual Studio Code.
It downloads the vscode-bootstrap script (.ps1 or .sh, depending on the target system) into the fake .vscode folder. These scripts act as the next stage, surveying system information, checking the installed Node.js version and updating it if needed, and finally downloading the real villain of this story: the malware itself.
Clean but weird code, comments everywhere, highly verbose output (who are you helping debug your malware, the victim?), and an overall blocky structure. Lots of clanker fingerprints, if you ask me. But let’s dig deeper into the final stage downloaded: ghost.js. on Unix machines, or ghost.npl on Windows workstations (0 hits on VirusTotal, by the way).
Oh no! The code is heavily obfuscated. It looks like we may have to call it a day… unless, for some reason, they’re using the same obfuscation technique they’ve been using for the last three years. Worth a try, who knows…
Of course, it worked like a charm. Just like everyone else, I was expecting a sample of OtterCookie or BeaverTail to show up in the right panel, but to my surprise, it was something different: brief, clunky, and sprinkled with a few Base64-encoded strings. Let’s tear this apart.
The first thing we notice is the getSystemInfo() function, which sweeps the system for basic information such as the hostname, release name, platform, IP address, and MAC address. It then builds a string with that information and sends it to the C2 server to register the victim host.
Now we need to know where that server is.
Luckily, decoding Base64 is trivial, as intended by the format. One of the strings reveals the C2 server address and endpoint, while the second translates to “now it time to get everything” (sic) which is used as a key to “speak” to the C2 server.
So now that we know the C2 server’s location, the format and key it expects, and the endpoint to talk to it, maybe it’s time for some crash tests. Or, as a North Korean vibecoder friend would say: “now it time to get everything!”
The Almost Perfect Crime
The original intention for this issue was to share more about the workshops and talks we delivered at DEF CON, because they touched directly on this topic!
This year, we worked hard to release Haetae, our AI agent that automates the takedown of certain DPRK C2 servers, across three villages: Adversary (where we explained how we built the AI agent), Red Team (where we taught a live, hands-on session on how to build this agent from scratch), and last but not least, Malware, where we spoke about the vulnerabilities in malware that are exploited by the agent.
But wait! Vulnerabilities? In malware?
Exactly. The main idea behind this series of talks and workshops was to show that malware is, in fact, software. Malicious software, but that doesn’t matter. As software, it is susceptible to vulnerabilities and flaws.
Over the last few years, we’ve identified lots of them: upload endpoints with no authentication, weak authentication methods such as User-Agent-based ones, weak mathematical implementations, hardcoded Telegram bot keys, and much more.
So, it’s safe to say we know how to spot vulnerable malware… which should give you a hint about what’s going to happen next.
Cyberbullying.

Since we know everything we need to set up a fake dialogue with the C2 server, we could allegedly do exactly that, making their server believe that a new victim called “QuetzalTeam” has been registered.
Multiple times.
Per second.
For maybe a million times.
And with viral music.
It would look like this:
This marks the end of this chapter, and of this malware’s episode. It earned an epitaph before it even got a name.
As usual, take extra care when attending job interviews.
Don’t do malware.
Authenticate and restrict your C2 upload endpoints.
Cyberbully your closest National Cyber Threat.
And don’t get rekt.
IOCs
IPV4:141.94.148.39
URL:ip-checking-psi[.]vercel[.]app // Thanks for the quick takedown, Vercel!
SHA256:fcbcaf9a00757fff176afed4b3b115227b02f11f0cca4a762b0b0f12723daa53 // deobf_ghost.js
SHA256:338f34788c92489b4b9078d10a436df035fcae395f3ec2fd95d1e29d28e67efd // ghost.js
SHA256:338f34788c92489b4b9078d10a436df035fcae395f3ec2fd95d1e29d28e67efd // ghost.npl
SHA256:6effad9fdee81589b37c60bbbae20483200bf53bee3e3c107b1aa47d2ac4ccb3 // package.json
SHA256:6ffedf7ec3b9cea4c99ac7ff5b0e2fb8e68f0a6bfce7110ff6ab267bc485fbb3 // stager_mac.sh
SHA256:db2b3c88d8ab1a55591b21d727812bc855da0e02e85e0bfe77d4335153090ac0 // stager_nix.sh
SHA256:f63c498b9c42612447fdb757a59aa9a1bb2285e77f83af8224577f9478fd8f2d // stager_win.ps1
SHA256:5d036408ac731df4f2909385ca5c3a75766ab528bcd784668a4997cfa2acd947 // vscode-bootstrap.ps1
SHA256:9cb437ca697a7550163c4a66e8b766952d3ddf94f714bafac91132ad37e5ddfa // vscode-bootstrap.sh
MD5:72c3eea054187471a5d00b8d7c721b42 // deobf_ghost.js
MD5:d804d3b1114766fb48b3b369df9d2f47 // ghost.js
MD5:d804d3b1114766fb48b3b369df9d2f47 // ghost.npl
MD5:ea31f66a5164a394ce3a7253db49dd79 // package.json
MD5:4d7d3dfd3d99727f450ee0883e9f60e2 // stager_mac.sh
MD5:38614d05a905eab3a0885b637bfbb8e8 // stager_nix.sh
MD5:2e3fdb5b8befdb9b48cb1a5468dfc164 // stager_win.ps1
MD5:b42652763f78183cf779811dc2cb13bb // vscode-bootstrap.ps1
MD5:d8cc49f56265ca137a73a275379704fa // vscode-bootstrap.sh
URL:http[:]//141.94.148.39[:]1224
URL:http[:]//141.94.148.39[:]1224/api/checkStatus












