How the Discord Activity RCE Exploit Works: From V8 Memory Corruption to Native Code Execution

The Discord Activity RCE exploit chains a V8 heap corruption primitive with an Electron IPC abuse vector to execute arbitrary native code, ultimately calling CreateProcessW to launch Windows binaries from within a sandboxed Activity.

The bikini/exploitarium repository demonstrates a proof-of-concept that escapes the Discord Activity sandbox and achieves remote code execution (RCE) on the host system. This exploit targets the Discord desktop client's renderer process, leveraging memory corruption in the V8 JavaScript engine to inject a payload that hijacks Electron's inter-process communication (IPC) mechanism.

Exploit Chain Architecture

The attack consists of nine logical components implemented across server.js and exploit.html. The infrastructure begins with an HTTP tunnel that provisions the malicious Activity, while the client-side code executes a multi-stage memory corruption and IPC injection sequence.

Activity Provisioning and Protocol Handshake

The attacker operates a local HTTP server defined in server.js that listens on 127.0.0.1:3077 by default. This server implements the Discord Activity protocol handshake through two critical endpoints: /protocol/register and /protocol/lease (lines approximately 260-170). These endpoints generate a lease token that authorizes the Activity to request privileged actions later in the exploit chain.

Stage 1 — V8 Memory Primitives and Sandbox Bypass

Inside exploit.html, the buildPrimitive() function (approximately line 360) constructs a WebAssembly-based addrof/fakeobj primitive. This yields arbitrary read/write capabilities (read64, write64) on the V8 heap. The exploit uses these primitives in installElectronIpcPayload (lines approximately 1150-1200) to overwrite the auxiliary-window flag within the renderer's V8 object. Flipping this flag allows the Activity to open a second renderer window that shares the same process, breaking the sandbox isolation.

Stage 2 — Electron IPC Abuse and Native Payload Delivery

With the auxiliary window enabled, the exploit crafts a malicious Electron IPC message. The installElectronIpcNativeInvokePayload function (lines approximately 1270-1360) resolves internal Discord/Electron function addresses (ipcCreate, invoke, etc.) and assembles a Return-Oriented Programming (ROP) chain. This chain culminates in a call to CreateProcessW with the path to the target executable, defaulting to C:\Windows\System32\calc.exe.

Persistent Endpoint Injection

The crafted IPC message also modifies Discord's internal settings. It writes a temporary web-application endpoint (/native-proof) into WEBAPP_ENDPOINT and triggers DISCORD_APP_RELAUNCH. The relaunch logic, handled in the /rce-stage2 endpoint (lines approximately 998-1030), ensures Discord reloads the Activity from the attacker-controlled endpoint, placing the native payload inside the main renderer process.

Native Execution Verification

Once the relaunched renderer executes the native payload at /native-proof (lines approximately 236-260), the code verifies successful execution by checking DiscordNative.processUtils.getLastCrash().rendererCrashExitCode. A value of 0x51a71338 signals successful native execution. The payload itself resolves CreateProcessW via PE parsing in resolveImport (lines approximately 800-820) and invokes it through a memory stub generated at markerAddr (lines approximately 1110-1130).

Step-by-Step Execution Flow

  1. Start the proof-of-concept server:

    export ACTIVITY_RCE_SPAWN_PATH="C:\\Windows\\System32\\calc.exe"
    node server.js
  2. Configure the Discord Activity in the Developer Portal to point to your tunnel URL (e.g., https://<APP_ID>.discordsays.com).

  3. Register a protocol instance:

    curl -s "http://127.0.0.1:3077/protocol/register?generation=abcd1234&instance=myinst"
  4. Obtain a lease token for stage 2:

    curl -s "http://127.0.0.1:3077/protocol/lease?generation=abcd1234&instance=myinst"

    This returns a JSON object: {"status":"granted","token":"<random-hex>","attempt":1,"expiresAt":...}

  5. Trigger the stage 2 payload delivery:

    curl -G "http://127.0.0.1:3077/rce-stage2" \
        --data-urlencode "instance=myinst" \
        --data-urlencode "token=<token>" \
        --data-urlencode "origin=https://myapp.discordsays.com"
  6. Discord relaunches, executes the native proof, and spawns the configured process.

Key Source Files and Functions

  • server.js: Implements the HTTP server, protocol handshake endpoints (/protocol/register, /protocol/lease), and the /rce-stage2 payload generation handler.
  • exploit.html: Contains the V8 heap exploitation logic, buildPrimitive(), and the auxiliary window bypass via installElectronIpcPayload.
  • installElectronIpcNativeInvokePayload: Constructs the ROP chain and IPC message for native code execution in the main Discord process.
  • resolveImport: Parses PE headers to locate CreateProcessW or ExitProcess in the host module.

Environment Configuration

The proof-of-concept supports several environment variables to customize the attack:

  • ACTIVITY_RCE_SPAWN_PATH: Path to the executable spawned by the payload (default: calc.exe).
  • ACTIVITY_PROTOCOL_LEASE_MS: Duration of the protocol lease token validity.
  • ACTIVITY_PUBLIC_ORIGIN: Collector origin for verification callbacks reporting the 0x51a71338 success code.

Summary

  • The Discord Activity RCE exploit combines V8 memory corruption with Electron IPC abuse to escape the sandbox and execute native code.
  • Arbitrary read/write primitives in exploit.html enable the auxiliary-window policy bypass required to access the IPC channel.
  • The installElectronIpcNativeInvokePayload function crafts a ROP chain that ultimately calls CreateProcessW with attacker-controlled arguments.
  • Environment variables like ACTIVITY_RCE_SPAWN_PATH control the final payload execution without recompiling the exploit.
  • Success is verified by checking for the specific exit code 0x51a71338 in the renderer crash log returned by DiscordNative.processUtils.getLastCrash().

Frequently Asked Questions

What makes the Discord Activity RCE exploit possible?

The exploit exists because Discord Activities run in a V8 renderer process that can corrupt memory to access the same-process IPC channel used by the main Electron application. By using the buildPrimitive() addrof/fakeobj gadget to enable auxiliary windows, the attacker gains access to unsandboxed native code execution through the CreateProcessW Windows API call, as implemented in the bikini/exploitarium repository.

How does the V8 memory primitive work in this exploit?

The buildPrimitive() function in exploit.html (line ~360) uses WebAssembly to construct an addrof/fakeobj gadget pair. This provides read64 and write64 operations that can modify internal V8 flags. Specifically, the installElectronIpcPayload function (lines ~1150-1200) targets the auxiliary-window policy flag located at a calculable offset from heap objects, enabling the second renderer window required for IPC access.

What is the purpose of the 0x51a71338 exit code?

According to the source code in server.js (lines ~236-260), the value 0x51a71338 serves as a magic number indicating successful native payload execution. The /native-proof page checks DiscordNative.processUtils.getLastCrash().rendererCrashExitCode for this specific value to confirm that the ROP chain successfully invoked CreateProcessW before reporting RCE_VERIFY_SUCCESS to the attacker's collector endpoint.

Can this exploit work without user interaction in Discord?

No, the exploit requires the victim to actively launch the malicious Activity from a Discord channel by clicking the Activity's Launch button. However, once launched, the entire chain—from V8 corruption to native code execution—proceeds automatically without additional user consent, leveraging the trusted nature of the Discord client to execute the staged payloads in exploit.html and the stage 2 IPC injector.

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 →