Discord Activity Stock Client RCE PoC: Full Attack Chain Analysis

The discord-activity-stock-client-rce-poc vulnerability uses a six-stage exploit chain combining V8 type confusion, memory patching, and Electron IPC abuse to achieve native code execution in Discord 1.0.9245 from a sandboxed Activity.

The discord-activity-stock-client-rce-poc in the bikini/exploitarium repository demonstrates how a malicious Discord Activity can escape the renderer sandbox and execute arbitrary Windows commands. This proof-of-concept targets the stock Discord client version 1.0.9245, exploiting the V8 JavaScript engine and Electron's IPC bridge to transition from sandboxed JavaScript to native process creation.

Stage 1: Activity Sandbox Loading

Discord loads third-party Activities inside a tightly sandboxed iframe served from https://<APP_ID>.discordsays.com. In the vulnerable 1.0.9245 client, the Activity page (exploit.html) executes within the renderer process using the standard sandbox configuration. This initial context provides the attack surface for the V8 exploit, though the renderer itself is restricted from accessing native APIs or spawning processes directly.

Stage 2: V8 Type-Confusion Primitive

The attack gains memory access by driving the V8 optimizer into a shape-feedback mismatch. Using a WebAssembly callback to trigger a confused store, the exploit plants a fake double-array map inside V8's pointer-compression cage. This corruption yields an arbitrary read/write primitive confined within the cage's virtual address space.

According to the "Renderer Memory Primitive" section in the repository's README, the exploit defines helper functions to manipulate 64-bit values through the corrupted array object:

// After a confused store, we obtain a fake double‑array object
// and use it to read/write arbitrary 64‑bit values.
function read64(T) { … }
function write64(T, v) { … }

These primitives allow the Activity to read and write arbitrary memory addresses within the V8 heap, providing the foundation for the next stage of the attack.

Stage 3: Arbitrary Memory Writes and Policy Patching

With read/write access established, the exploit recovers the V8 cage base and locates generated-code dispatch entries in the stock Discord binary. It then overwrites the popup-policy instruction page, specifically targeting the conditional branch that blocks auxiliary windows and the sandbox-flag propagation branch. After patching, the exploit restores the original memory protection flags and flushes the instruction cache to ensure CPU coherence.

This memory corruption is coordinated through validation logic in server.js, which verifies the collector origin before issuing the popup-policy worker:

// Verify the request host, then issue the popup‑policy worker
const allowedHosts = new Set([req.headers.host, …]);
if (!allowedHosts.has(parsed.host.toLowerCase()) && !isDiscordActivityProxy)
    throw new Error("collector origin does not match the request host");

Stage 4: Auxiliary Window Creation

Once the popup-policy patch is active, calls to window.open() succeed where they previously would have been blocked. The exploit creates an auxiliary Discord window that hosts a second-stage renderer process. This new window operates outside the strict sandbox policies applied to the initial Activity iframe, providing the necessary context to access Electron's privileged IPC mechanisms.

Stage 5: Electron IPC Abuse

The second-stage renderer locates Discord's Electron IPC bridge and constructs V8 strings and arrays to invoke three privileged calls, as documented in the "Electron IPC Boundary" section:

// Build arguments for the IPC bridge
DISCORD_SETTINGS_SET("WEBAPP_ENDPOINT", activityProxyOrigin);
DISCORD_SETTINGS_SET("WEBAPP_PATH", "/native-proof");
DISCORD_APP_RELAUNCH();   // triggers a relaunch of the main renderer

The DISCORD_SETTINGS_SET calls persist a temporary endpoint (WEBAPP_ENDPOINT) and path (WEBAPP_PATH) to the Activity proxy origin. The DISCORD_APP_RELAUNCH() call then forces Discord to restart its main web-application renderer, loading the attacker-controlled /native-proof endpoint on relaunch.

Stage 6: Native Payload Execution

The relaunched renderer loads the second-stage page (/native-proof) and resolves Windows API imports directly from the stock Discord executable image. The exploit locates CreateProcessW, Sleep, and ExitProcess in memory, then writes a small Win64 payload that invokes:

// Resolve CreateProcessW from the stock Discord image and invoke it
CreateProcessW("C:\\Windows\\System32\\calc.exe", …);

The payload spawns calc.exe while keeping the renderer alive for approximately ten seconds via Sleep, then exits with a sentinel value using ExitProcess. The server.js component verifies this sentinel to confirm successful execution, completing the full chain from sandboxed Activity to native Windows process creation.

Supporting Infrastructure

The discord-activity-stock-client-rce-poc relies on several coordinated components:

  • exploit.html – Contains the renderer-side exploit logic, including the V8 primitive construction, popup-policy patching routines, and native payload generation.
  • server.js – Implements a one-shot Activity server that issues single-use leases, validates collector origins against request hosts, and maintains protocol-state logs throughout the exploit chain.
  • run.ps1 – PowerShell launcher that validates the stock Discord client version, backs up existing Discord settings, orchestrates the server startup, and restores settings after proof completion.
  • wasm-module-builder.js – Utility for constructing the specific WebAssembly bytecode sequences used to trigger the V8 type-confusion vulnerability.

Summary

  • The discord-activity-stock-client-rce-poc targets Discord 1.0.9245 through a malicious Activity that abuses V8 internals.
  • A type-confusion primitive in the V8 pointer-compression cage provides arbitrary read/write capabilities within the renderer process.
  • Memory patching of the popup-policy instruction page disables checks that prevent auxiliary window creation.
  • Electron IPC privilege escalation (DISCORD_SETTINGS_SET and DISCORD_APP_RELAUNCH) allows the attacker to redirect the main application renderer to attacker-controlled content.
  • The final payload resolves Windows API imports (CreateProcessW) from the Discord binary itself to spawn native processes like calc.exe.

Frequently Asked Questions

What specific Discord version is vulnerable to this RCE vector?

The discord-activity-stock-client-rce-poc specifically targets Discord version 1.0.9245, referred to as the "stock" client in the source analysis. The exploit relies on specific memory layouts and instruction sequences present in this version's binary, particularly the popup-policy branch instructions and V8 heap structure.

How does the exploit bypass Discord's Activity sandbox?

Rather than escaping the V8 sandbox directly, the exploit uses a fake double-array map to gain arbitrary memory read/write within the pointer-compression cage. It then patches the binary's popup-policy instruction page in-memory to allow window.open() calls, creating an auxiliary window that operates with different sandbox privileges and can access Electron's privileged IPC bridge.

Why does the attack require creating an auxiliary window?

The auxiliary window is necessary because the initial Activity iframe operates under strict sandbox policies that block access to Electron's ipcRenderer or privileged context bridges. By patching the popup-policy and opening an auxiliary window, the exploit obtains a second-stage renderer context capable of invoking the DISCORD_SETTINGS_SET and DISCORD_APP_RELAUNCH IPC calls required to redirect the main application and persist the malicious configuration.

What Windows APIs does the final payload utilize?

The native payload resolves three specific Windows API imports from the Discord executable: CreateProcessW (to spawn calc.exe), Sleep (to maintain the renderer connection for approximately 10 seconds during payload execution), and ExitProcess (to terminate with a sentinel value that the coordinating server verifies as proof of successful exploitation).

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 →