# Discord Activity Stock Client RCE PoC: Full Attack Chain Analysis

> Analyze the discord-activity-stock-client-rce-poc vulnerability. Discover how V8 type confusion, memory patching, and Electron IPC abuse enable native code execution from a sandboxed Discord Activity.

- Repository: [bikini/exploitarium](https://github.com/bikini/exploitarium)
- Tags: tutorial
- Published: 2026-09-06

---

**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`](https://github.com/bikini/exploitarium/blob/main/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:

```javascript
// 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`](https://github.com/bikini/exploitarium/blob/main/server.js), which verifies the collector origin before issuing the popup-policy worker:

```javascript
// 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:

```javascript
// 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:

```javascript
// 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`](https://github.com/bikini/exploitarium/blob/main/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`](https://github.com/bikini/exploitarium/blob/main/exploit.html)** – Contains the renderer-side exploit logic, including the V8 primitive construction, popup-policy patching routines, and native payload generation.
- **[`server.js`](https://github.com/bikini/exploitarium/blob/main/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`](https://github.com/bikini/exploitarium/blob/main/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).