About this post: Thanks to MalwareHunterTeam for flagging this new payload. This analysis and write-up were produced by Claude (Opus 5.5), working as an agent in Claude Code on a REMnux analysis VM. Tools used: Malcat, msitools, osslsigncode, Python (pefile, capstone, pycryptodome), js-beautify, FLOSS, capa, ANY.RUN and Deception Pro.
TL;DR
A file called PuTTY.msi installs the real PuTTY SSH client, and it carries a valid Extended Validation (EV) signature from a Canadian company called "Code Blue Staffing Inc." Alongside PuTTY it drops a genuine, re-signed copy of Node.js with a hidden script baked into it. That script waits five minutes, checks which security product is watching, and then downloads its next stage from a Cloudflare Workers address. The next stage only ever lives in memory.
- Lure: a signed
PuTTY.msithat really does open PuTTY 0.85. - Loader: official Node.js 26.7.0 turned into a "single executable application" with an encrypted config and an extra hidden module.
- Evasion: a 5-minute sleep, a config key that changes every calendar year, its own DNS lookups, ETW/AMSI patching and indirect system calls.
- C2:
machinegirl[.]carvelpolman[.]workers[.]devhands out a module calledget_module_lunaover an encrypted key-exchange protocol. In our sandbox it did, and that module then called home to*.normanitavo[.]workers[.]dev. - Signer: Code Blue Staffing Inc., Certum EV, serial
71ec769262102d63e053d47269e7e635.

But it’s signed!
Code signing is how Windows tells you who published a file. An Extended Validation certificate is the premium version: the certificate authority checks that the company is real before issuing it. That’s why so many people treat a valid signature as a seal of approval.
Here the MSI, node.exe and the helper koffi.node are all signed by "Code Blue Staffing Inc." in Toronto, using a certificate Certum issued on 22 September 2026. Four weeks later it signed an installer that claims to be PuTTY by Simon Tatham. A staffing agency has no reason to ship an SSH client. A brand-new certificate that is immediately used to sign someone else’s product is a pattern we see again and again with signed malware. The certificate has been reported to the CA and added to the CertGraveyard.
The bundled putty.exe is the genuine one, still carrying Simon Tatham’s own signature. The installer opens it at the end through a small helper, decoy_launch.exe, so the victim sees exactly what they expected.
What the installer actually does
MSI files are small databases. You can read their tables to see what an installer will do before running it. This one copies four files to C:\ProgramData\NodeJS\, writes a few marker values under HKCU\Software\NodeJS and HKCU\Software\PuTTY, and has two "custom actions":
- RunAgent starts
node.exewith no arguments, in the background, as the logged-in user. - RunDecoy starts
decoy_launch.exe putty.exe.
There is no Run key, shortcut or scheduled task, so the installer itself doesn’t set up persistence. Two details still matter. The Node.js files are marked permanent, so they stay behind after you uninstall "PuTTY". And the same custom action fires again on any repair or reinstall.
Node.js, but with a passenger
Node.js can be packaged as a single executable application (SEA). The developer bakes a script into node.exe as a resource, and when that exe starts it runs the script instead of waiting for input. This is a legitimate feature, and it is what makes the trick work: the file is the real Node.js 26.7.0. We compared it with the official download, and no machine code had been changed. The only differences are the injected script, the switch that turns SEA mode on, and three text strings where "Node.js" was renamed to "AppCore".
The hidden script (about 38 KB of minified JavaScript) brings along koffi, a popular library that lets JavaScript call Windows functions directly. The copy is byte-identical to the official koffi 3.0.2 package. With koffi, the script can allocate memory, flip it to executable and jump into it, all from JavaScript.
Settings that expire every New Year
The script’s settings are stored encrypted. The key is the SHA-256 hash of nm-cfg- plus the current year. So the config only decrypts on a machine whose clock says 2026; in 2027 the stager would look for a plaintext config.json next to itself and crash without one. This gives the operator a built-in expiry date and a way to re-arm the stager later. It also means anyone analysing it after New Year needs to set the clock back.
Decrypted, the config is short:
- Link:
hxxps[://]machinegirl[.]carvelpolman[.]workers[.]dev/ - Request:
get_module_luna, POST - Wait first: 300 seconds
- User-Agent: a fake Chrome string claiming "Windows NT 11.0" (no such version exists)
The five-minute wait is aimed at sandboxes. Our ANY.RUN run lasted one minute, so it saw node.exe start and then nothing, and the verdict came from the PuTTY binary rather than from the loader.
Two ways to phone home
After the wait, the script looks at the DLLs loaded into its own process. If it spots SentinelOne, Bitdefender or Sophos, it plays it safe: it uses Node’s normal HTTPS code and does its own DNS lookups against public resolvers (Cloudflare, Google, Quad9, OpenDNS) or Cloudflare DNS-over-HTTPS, so the system resolver never sees the domain.
On every other machine it gets more aggressive. It decrypts a second hidden module (key: nm-httpc- plus the year) and loads it straight into memory. That module switches off two Windows monitoring features. It patches ETW (the event stream many security tools read) with a single "return" instruction, and it patches AMSI (the interface that lets antivirus scan scripts) so every scan answers "invalid argument". Its hidden strings name Windows’ WinINet and BCrypt libraries, including the elliptic-curve key-exchange functions, so it almost certainly performs the same encrypted exchange natively (we didn’t see it on the wire).

Either way the conversation looks the same. The client and server each make a fresh key pair and agree on a shared secret (an "ECDH key exchange"). The request is then encrypted with AES-GCM, so even someone who can read the HTTPS traffic only sees random bytes. The server answers with the next stage.
Loading without touching disk
Whatever comes back is never written to a file. If it’s a Windows program (a PE file), the script maps it into memory itself, fixes up addresses, and overwrites its header with junk so memory scanners can’t spot the familiar "MZ" signature. It then calls the module’s start function on a background thread-pool worker. If it’s raw shellcode, the script just runs it.
To allocate and protect that memory, the script avoids the normal Windows functions that security tools watch. It builds its own small system-call stubs: tiny bits of code that ask the kernel directly, jumping through a syscall instruction inside ntdll so the call still appears to come from ntdll. Think of it as walking in through the staff entrance rather than the front door with the guard.
What happened when we let it run
We let it run in a long-lived sandbox (Deception Pro) with a simulated corporate user. Exactly as the code predicts, node.exe sat quietly for about five minutes, then contacted the carvelpolman worker. Eight seconds later the same process started talking to two new Workers addresses, bluepeak[.]normanitavo[.]workers[.]dev and cedar-stone[.]normanitavo[.]workers[.]dev, with a different network fingerprint. That fingerprint matches Windows’ WinHTTP library, which the loader itself never uses. In other words, the "luna" module arrived, started running inside node.exe, and opened its own command channel. The download was about 240 KB. From then on the module checked in every four to four and a half minutes, rotating between five subdomains of the same worker account (bluepeak, cedar-stone, harbor-atlas, common and fieldnote), a simple trick to make the traffic look less repetitive. The Node process stayed alive afterwards, and it had loaded Windows libraries for data protection (DPAPI), crash dumping and key storage. That could point to credential theft, but those libraries can also load for ordinary reasons, so we can’t say yet.
What we couldn’t see
The second stage itself is still a black box. It travels inside the key-exchange encryption on top of HTTPS, and the keys exist only in the memory of that one node.exe process, so even a perfect network capture doesn’t let us decrypt it. Getting the module would mean dumping that process’s memory while it runs. We also don’t yet know what the "luna" module does on its new channel. The sandbox run is still going and we’ll update this post if the traffic tells us more.
Advice
For regular users: download PuTTY only from its official page at chiark.greenend.org.uk/~sgtatham/putty/, or through winget. PuTTY is signed by Simon Tatham, so if the installer is signed by anyone else, stop. If you ran this file, check for C:\ProgramData\NodeJS\node.exe. Uninstalling "PuTTY" does not remove it.
For defenders:
- Hunt for
node.exeoutside normal install paths, especially when it is signed by someone other than the OpenJS Foundation, and forHKCU\Software\NodeJS\Agent. - Flag Node processes that load
koffi.nodeand then create private executable memory or thread-pool work. - Block or alert on
*.carvelpolman[.]workers[.]devand*.normanitavo[.]workers[.]dev. Look for direct DNS to public resolvers or DoH from non-browser processes. - Watch for MSI custom actions launching
node.exefromProgramData.
IOCs
| Type | Value |
|---|---|
| MSI SHA256 | 73ab77da60d7196bce61405c615f8cd03a3157abffaf4a2447bd79fb8082da9f |
| node.exe (SEA) | 823d9e0d1086a5894e126dbff272259731208bd17dc47a0753849ee98818339e |
| koffi.node (re-signed) | f384e2419f200e4eb8b49657be41a0e4327b044e7d571303b68463d417a2d544 |
| decoy_launch.exe | 2214430d1611a20f60cd656025b031d9476c5f2d9bdb421ec5d4e87551167453 |
| httpc module (in memory) | 7409a74bb808d35d03e4aac7e05991b3b2263a30bc225144d6f520ad264be636 |
| C2 (loader) | hxxps[://]machinegirl[.]carvelpolman[.]workers[.]dev/ |
| C2 (stage 2) | bluepeak, cedar-stone, harbor-atlas, common, fieldnote .normanitavo[.]workers[.]dev |
| Request string | get_module_luna |
| Install path | C:\ProgramData\NodeJS\ |
| Registry | HKCU\Software\NodeJS (Agent, Koffi), HKCU\Software\PuTTY (Installed, Decoy, Launch) |
| Signer | Code Blue Staffing Inc., Certum EV Code Signing 2021 CA, serial 71ec769262102d63e053d47269e7e635 |
| Build path | C:\Users\user\Desktop\working\res5\fang\sub\no-mistakes\nodejs\dist\bundle.cjs |
Wrapping up
Turning a real, untouched Node.js into a loader is a clever way to dodge "unknown binary" alarms: the big executable is genuine, and everything malicious is a few kilobytes of encrypted data riding inside it. Add an EV signature and a real PuTTY window, and very little looks out of place. If you’ve seen get_module_luna or the carvelpolman / normanitavo workers elsewhere, we’d love to hear about it. Stay safe, and check who signed your installers.