Anatomy of a Failed (Nation-State?) Attack
Analysis of a Botched (State-Sponsored?) Cyber Attack
This report details a sophisticated attempt to compromise my system via a fraudulent recruitment scheme.
Disclaimer: This narrative was written by a human. However, I utilized Claude to accelerate the analysis of the Remote Access Trojan (RAT) and to develop a script for detecting Indicators of Compromise (IoCs).
Because I am based in Canada, I have forwarded all relevant findings to the Canadian Centre for Cyber Security (CCCS) and other pertinent authorities.
🚩 Initial Observations
- AV Evasion: The malicious image payload is completely invisible to all engines on VirusTotal.
- Identity: The attacker used a fake persona. I have removed names of innocent individuals who share the same name as the alias to avoid confusion.
- Scope: Other members of the Rust community on Reddit reported similar targeting.
🎣 The Hook: Social Engineering
The attack began with a "fake-interview" scam. Given the context, I suspect I was targeted because of my contributions to crates.io.
The Persona
I was contacted by an individual claiming to be "D█████ S████" from Lua Ventures.
- The Firm: A Singapore-based VC focusing on DeFi (which I later discovered was actually defunct).
- The Profile: The attacker provided a link to a professional-looking, albeit generic, LinkedIn profile.
- The Lure: They mentioned specific portfolio companies—Lyrasing and Roadpay—that were seeking advisory expertise.
I initially thought this was a standard lead. While these companies had a minimal web presence, it wasn't an immediate red flag, as early-stage startups often have sparse websites.
The Interaction
After coordinating via email, we scheduled a call. The person on the line had a German accent and was somewhat difficult to understand. He claimed to be traveling, which was slightly unusual but not inherently suspicious.
🔍 The Technical Discovery
The "interview" involved analyzing a repository for a project called "Ticket Harbor" (a ferry-ticketing application).
The Audit Process
Upon cloning the repo, I noticed the instructions felt more like a TypeScript coding test than a high-level architecture review. Out of a mix of caution and laziness, I zipped the repo and fed it to Claude for a preliminary scan.
The Audit Checklist:
- Check root
package.jsonfor lifecycle hooks. - Audit
patch-packageimplementation. - Scan
patches/directories for anomalies. - Analyze binary blobs in assets.
Claude flagged a discrepancy: the root package.json lacked postinstall or preinstall hooks, yet the project utilized patch-package (which typically requires such hooks).
Finding the Needle
I discovered an excessive number of files in the patches/ directory. Most were "noise" designed to distract analysts:
packages/electron-benchmarks/patches/sumchecker+3.0.1.patchpackages/electron-benchmarks/patches/@electron+get+2.0.3.patchpackages/electron-benchmarks/patches/extract-zip+2.0.1.patch
However, inside typescript+5.9.2.patch, I found a malicious obfuscation stub injected into _tsc.js and typescript.js.
The Payload Logic
The stub uses a base64-encoded blob and a XOR cipher. The mathematical operation for decryption can be represented as:
;;( function ( r , k ){
const d = Buffer.from(..., 'base64');
for ( let i = 0; i < d.length; i++ ) d [ i ] ^= k;
return new Function ( ' require ' , ' Buffer ' , ' WebAssembly ' , ' process ' , ' __dirname ' , d.toString ( ' utf8 ' ))( require , Buffer , WebAssembly , process , __dirname )
})( " YWFg... " , 73 )
This code executes every time the TypeScript compiler (tsc) or any module importing typescript.js is invoked. At this point, I moved the analysis to a secure sandbox.
⚙️ The Infection Chain
The attack is a multi-stage pipeline designed for stealth and persistence.
Summary of Components
| Component | Role | Stealth Mechanism |
|---|---|---|
patch-package | Delivery | Injects code into node_modules |
git skip-worktree | Persistence | Hides modified patch files from git status |
operators/3.png | Storage | Hides payload in a custom WASM chunk within an image |
new Function() | Execution | Avoids eval() to bypass some security scanners |
🦠 The Payload: PinpinRAT
The final stage is a 1.68 MB obfuscated payload I've dubbed "PinpinRAT".
The Unwrapping Process:
- Layer 1:
obfuscator.io(which ironically claims to protect against LLMs). - Layer 2: Base64 encoding.
- Layer 3: Additional Base64 encoding.
The malware is designed to be highly ephemeral. It employs a three-tier cleanup strategy:
- The
gittrick hides the initial entry point. - The dropper modifies the patch file to remove the injected lines after the first successful run.
- The second-stage temporary directory is deleted immediately upon execution.
I utilized Claude's sandbox capabilities to reverse engineer the trojan's internals, as I refused to risk detonating it on my own hardware.