How to Use the Discord RCE PoC server.js: Complete Setup Guide

The Discord RCE PoC server.js is an environment-driven HTTP coordination server that manages the Activity-to-native exploit chain by serving pages, issuing protocol leases, generating JavaScript/WebAssembly payloads, and collecting verification telemetry.

The server.js file in the bikini/exploitarium repository serves as the central orchestration hub for the Discord Activity Stock Client RCE proof-of-concept. This Node.js HTTP server handles the complete exploit lifecycle from initial Activity renderer compromise to native code execution, requiring specific environment variables and HTTPS tunneling to function correctly. Researchers analyzing the Discord Activity surface must understand its REST API endpoints and state management to successfully deploy the calculator spawn demonstration.

Architecture and Role in the Exploit Chain

The server coordinates four primary components that execute sequentially:

  • server.js – The core HTTP coordination server that implements the protocol lease system and payload generation.
  • exploit.html – First-stage Activity page that creates the V8 memory primitive and builds the native payload.
  • wasm-module-builder.js – Helper utility that generates the WebAssembly bytecode used by the memory primitive.
  • run.ps1 – PowerShell wrapper that validates target hashes, starts the server, monitors completion, and restores original Discord settings.

According to the source code in discord-activity-stock-client-rce-poc/server.js, the server is entirely environment-driven, with no hard-coded credentials or fixed ports, allowing flexible deployment across different network configurations.

Environment Variable Configuration

Behavior is controlled through the following optional environment variables. All variables default to safe values if unset:

  • ACTIVITY_PORT – TCP port the HTTP server binds to (default: 3077).
  • ACTIVITY_PUBLIC_ORIGIN – Public HTTPS tunnel URL that Discord forwards Activity requests to; must be a valid HTTPS origin.
  • ACTIVITY_RCE_SPAWN_PATH – Absolute filesystem path to the binary spawned upon successful exploitation (default: calc.exe).
  • ACTIVITY_RCE_NATIVE_CANDIDATES – Comma-separated hex IDs of native candidates; server falls back to hard-coded candidate if empty.
  • ACTIVITY_PROTOCOL_LEASE_MS – Lease token lifetime in milliseconds (default: 300000, approximately 5 minutes).
  • ACTIVITY_RCE_NATIVE_DIAGNOSTIC – Set to "leak" to enable extra telemetry from the native execution stage.
  • ACTIVITY_RCE_STAGE1_DIAGNOSTIC – Set to "full" to enable comprehensive stage-1 logging.

The server aborts initialization if the target Discord executable is missing or if the recovery flag indicates an incomplete previous session, as implemented in the startup validation block.

HTTP API Endpoints Explained

The server exposes a REST API for client registration, payload delivery, and telemetry collection. All state mutations are logged to timestamped files under the logs/ directory.

Health and Origin Registration

  • GET /health – Returns server status, chosen native candidate, and current log file path.
    Source: if ("/health" === url.pathname) block【server.js†L20-L21】

  • POST /set-public-origin?origin=<HTTPS-origin> – Loopback-only endpoint that stores the collector URL used for telemetry.
    Source: if ("/set-public-origin" === url.pathname)【server.js†L21-L24】

Protocol Handshake

  • GET /protocol/register?instance=<id>&generation=<gen> – Registers a new client instance and returns a generation token.
    Source: Registration handling begins at if ("/protocol/register"【server.js†L26-L27】

  • GET /protocol/lease?instance=<id>&generation=<gen> – Issues a one-shot lease containing a random hex token and expiry timestamp.
    Source: Lease logic extends through line 68【server.js†L26-L68】

  • GET /protocol/state – Returns the current lease status and associated instance metadata.

Payload Delivery

  • GET /payload?stage=rce-popup-patch&instance=<id>&token=<token>&origin=<collector> – Builds and returns the Stage-1 JavaScript payload that patches Discord's popup policy and injects a CSP nonce.
    Source: Payload generation under if ("/payload"【server.js†L70-L96】

  • GET /rce-stage2?instance=<id>&token=<token>&origin=<collector> – Returns the Stage-2 HTML document containing the embedded Wasm builder and native spawn parameters. This page persists WEBAPP_ENDPOINT to /native-proof and triggers Discord client relaunch.
    Source: Stage-2 generation starts at if ("/rce-stage2"【server.js†L98-L135】

Verification and State Management

  • GET /native-proof – Serves the second-stage verification page. When rceNativePayloadArmed is true, it verifies the spawned native child; when ACTIVITY_RCE_NATIVE_DIAGNOSTIC=leak, it dumps diagnostic data.
    Source: Handling at if ("/native-proof"【server.js†L36-L55】

  • GET /rce/state – Returns JSON object showing mode, stage1Dispatched, nativePayloadArmed, and nativeExitSuccess fields.
    Source: if ("/rce/state"【server.js†L56-L69】

  • POST /rce/retry – Re-arms the native payload for another execution attempt.
    Source: if ("/rce/retry"【server.js†L70-L73】

Telemetry Collection

  • GET /collect?stage=<stage>&m=<msg> – Receives encoded telemetry messages from the client, including native verification results and recovery requests.
    Source: if ("/collect"【server.js†L74-L81】

  • POST /log or GET /log?m=<msg> – Writes arbitrary debug messages to the live log file.
    Source: if ("/log"【server.js†L82-L90】

Step-by-Step Usage Guide

Starting the Server

Use the provided PowerShell wrapper to handle environment setup and HTTPS tunnel registration:


# Create HTTPS tunnel forwarding to localhost:3077

cloudflared tunnel --url http://127.0.0.1:3077

# Launch with public origin

.\run.ps1 -PublicOrigin "https://your-tunnel-here.trycloudflare.com"

The run.ps1 script validates the Discord executable hash, backs up settings.json, and writes a recovery flag. If the server stops unexpectedly, rerun with -RecoverOnly to restore original settings.

Obtaining a Protocol Lease

Generate a lease token manually using curl for testing or automation:


# Register instance

INSTANCE=$(uuidgen)
GEN=$(curl -s "http://127.0.0.1:3077/protocol/register?instance=$INSTANCE" | jq -r .generation)

# Request lease

curl -s "http://127.0.0.1:3077/protocol/lease?instance=$INSTANCE&generation=$GEN" | jq .

A successful response contains:

{
  "status": "granted",
  "token": "a1b2c3d4e5f6...",
  "attempt": 1,
  "expiresAt": 1694155674321
}

Triggering the Exploit Chain

Fetch the Stage-1 popup patch payload:

curl -s "http://127.0.0.1:3077/payload?stage=rce-popup-patch&instance=$INSTANCE&token=$TOKEN&origin=$PUBLIC_ORIGIN"

Once the Activity loads Stage-1, fetch Stage-2 to trigger native IPC:

curl -s "http://127.0.0.1:3077/rce-stage2?instance=$INSTANCE&token=$TOKEN&origin=$PUBLIC_ORIGIN"

This returns an HTML document that the Discord client renders in a second renderer process, embedding the Wasm module builder and spawn configuration.

Monitoring Execution State

Poll the state endpoint to verify successful calculator launch:

curl -s http://127.0.0.1:3077/rce/state | jq .

When nativeExitSuccess returns true, the calc.exe process (or your configured ACTIVITY_RCE_SPAWN_PATH) has executed successfully.

Key Source Files

The following files in the bikini/exploitarium repository comprise the complete PoC:

Summary

  • The Discord RCE PoC server.js coordinates the entire exploit chain through environment variables and a REST API.
  • Protocol leases provide time-bound authorization tokens that gate access to exploit payloads.
  • Two-stage delivery separates the popup policy patch (Stage 1) from the native IPC trigger (Stage 2) to bypass Discord's renderer isolation.
  • HTTPS tunneling is mandatory; the server validates that ACTIVITY_PUBLIC_ORIGIN uses the HTTPS scheme.
  • Automatic recovery via run.ps1 ensures Discord settings are restored after testing, preventing permanent client modification.

Frequently Asked Questions

What is the minimum required setup to run server.js?

You need Node.js installed and an HTTPS tunnel (such as Cloudflare Tunnel or ngrok) pointing to the local ACTIVITY_PORT. Set the ACTIVITY_PUBLIC_ORIGIN environment variable to your HTTPS tunnel URL, then execute node server.js or use run.ps1 for automated setup. The server will fail to start if it cannot locate the Discord executable for hash validation.

How does the protocol lease system prevent unauthorized access?

The server generates cryptographically random tokens bound to specific client instances and generations. Each lease expires after ACTIVITY_PROTOCOL_LEASE_MS milliseconds (default 5 minutes), and the /payload and /rce-stage2 endpoints validate the token before serving exploit code. This ensures that only the Discord Activity instance that registered can retrieve the malicious payloads.

Can I modify the payload to execute binaries other than calc.exe?

Yes. Set the ACTIVITY_RCE_SPAWN_PATH environment variable to the absolute path of your target binary before starting the server. For example, set ACTIVITY_RCE_SPAWN_PATH=C:\Windows\System32\notepad.exe changes the proof-of-concept to launch Notepad instead of Calculator. The path is embedded into the Stage-2 HTML payload and passed to the native execution primitive.

Where are exploit logs stored and what data do they contain?

Logs are written to timestamped files under the logs/ directory (e.g., logs/live-2026-09-07T12-34-56-789Z.log). They contain HTTP request paths, protocol registration events, lease grants, payload delivery confirmations, and native verification status. Enable diagnostic mode by setting ACTIVITY_RCE_NATIVE_DIAGNOSTIC=leak or ACTIVITY_RCE_STAGE1_DIAGNOSTIC=full to capture additional memory layout and execution telemetry.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →