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

> Uncover the Discord Activity RCE exploit, detailing V8 memory corruption and Electron IPC abuse for native code execution and launching Windows binaries.

- Repository: [bikini/exploitarium](https://github.com/bikini/exploitarium)
- Tags: deep-dive
- Published: 2026-09-07

---

**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`](https://github.com/bikini/exploitarium/blob/main/server.js) and [`exploit.html`](https://github.com/bikini/exploitarium/blob/main/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`](https://github.com/bikini/exploitarium/blob/main/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`](https://github.com/bikini/exploitarium/blob/main/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:

   ```bash
   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:

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

4. Obtain a lease token for stage 2:

   ```bash
   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:

   ```bash
   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`](https://github.com/bikini/exploitarium/blob/main/server.js)**: Implements the HTTP server, protocol handshake endpoints (`/protocol/register`, `/protocol/lease`), and the `/rce-stage2` payload generation handler.
- **[`exploit.html`](https://github.com/bikini/exploitarium/blob/main/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`](https://github.com/bikini/exploitarium/blob/main/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`](https://github.com/bikini/exploitarium/blob/main/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`](https://github.com/bikini/exploitarium/blob/main/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`](https://github.com/bikini/exploitarium/blob/main/exploit.html) and the stage 2 IPC injector.