Skip to content

Instantly share code, notes, and snippets.

@almostSouji
Last active December 24, 2025 13:29
Show Gist options
  • Select an option

  • Save almostSouji/2e9707739d1480062b7a314fc8c90cba to your computer and use it in GitHub Desktop.

Select an option

Save almostSouji/2e9707739d1480062b7a314fc8c90cba to your computer and use it in GitHub Desktop.
2022 Discord Privilege Escalation Report

Note

Timeline of events

  • 2022/08/22 22:23 (UTC): Private disclosure through hackerone with high severity
  • 2022/08/23 22:08 (UTC): Acknowledgement by Discord
  • 2022/08/24 19:57 (UTC): Remediation by Discord + escalation of severity to critical
  • 2022/08/24 21:11 (UTC): Confirmed remediation
  • 2022/08/24 21:30 (UTC): Resolution of the incident report and bounty reward
  • 2022/08/24 21:38 (UTC): Request for disclosure
  • 2022/09/23 21:39 (UTC): Report disclosure

Exploit Description

Execution of guild-based application commands that are not deployed to target server causing unredeemable privilege escalation.

Pretext

Discord is organized into sub-communities (called "servers" or "guilds"). Developers can use a native surface for automated applications "commands" to integrate their various apps with Discord communities. Commands can either be deployed to one specific guild or globally. The latter means the command can be used across the platform, if the app is installed on this specific community.

One of my bots was used in a personal server (with a fun command specific to this community) and as an anti-spam measure in the server of a large content creator (without the fun command).

Discovery

TL;DR: I can use the command in the server it is not deployed to.

Discord introduces command mentions. Using the </command_name:command_id> syntax, you can embed app commands in regular Discord messages. Users interacting with this embedded element, will then open up the slash command UI and be prompted to enter options.

I added the command mention for the "fun" command in my app's global description to add a discovery venue and flavour to its "personality". The app's profile can be prompted by clicking on the app name in the member list or a message it sent.

Curious about the rejection behaviour I clicked on the command mention in the app's profile description while in the server without the "fun" command.

The command went through and I got a response. That was unexpected.

Note

While command mentions lead to the discovery, The vulnerability I discovered was not connected to this update at all, meaning it has been present much longer than I originally anticipated. According to sources familiar with the code base, there were no updates to the affected code paths for months at the time.

Finding the root cause and thinking creatively

TL;DR: Discord does not validate the command origin

So i can use a command in a server it is not deployed to. Not a big deal for this "fun" command, which doesn't even have a public response message. Besides, if the app is added to the server, of course its commands can be used...

Well, no.

As mentioned above, the "scope" a command can be used in is determined by the developer (and server staff through application settings). It can be deployed locally or globally. Some Discord bots use this for a modular approach to commands.

Consider a community uses a bot just for "fun" commands (memes, virtual pets, card collecting, etc.) while another community uses its "moderation" module (kick, ban, prune, etc.).

Now what If i can use the "moderation" module in the server with just "fun" enabled?
And how would it even determine if I have the permission to do that?

Caution

Not ever Discord bot is just fun and games. Apps are used as powerful moderation tools with a lot of permissions. It's a common (and really bad) practice to give all apps in a server a "Bot" role, to make handling permissions more comfortable for the community administrators. Unfortunately many communities just enable the ADMINISTRATOR permission for this role following the "This bot is so popular, surely the bot dev is not a danger" credo. Thoughts like "besides, this bot can just do fun silly things, what's the harm in giving it more permissions than it really needs" also dominate the space.

To be clear: I recommend not giving ANY app this permission and employ you to be very careful with the set of permission you give every app. Least privilege should be the default! Everything should precisely have the permission it needs to do what it's here for. Not a single bit more.

Whenever a Discord user uses an application command, it's really just an authenticated HTTP POST request to the /interactions endpoint. It looks something like this:

{
  "type": 2,
  "application_id": "",
  "guild_id": "guild1",
  "channel_id": "guild1-channel",
  "session_id": "",
  "data": {
    "version": "",
    "id": "",
    "guild_id": "guild2",
    "name": "",
    "type": 1,
    "options": [],
    "application_command": {
      "id": "",
      "application_id": "",
      "version": "",
      "default_permission": true,
      "default_member_permissions": null,
      "type": 1,
      "name": "",
      "description": "",
      "guild_id": "guild3"
    },
    "attachments": []
  },
  "nonce": ""
}

Tip

I'm using the command payload from my original report. The structure has changed a bit since, if you look at command execution today!

Now, one would expect that the guild_id "guild1" and data.guild_id "guild2" and data.application_command.guild_id "guild3" would always be the same. After all, you should only be able to use a command in the guild it is deployed in.
However in my case, guild_id "guild1" was different. It was the content creator's server, the one without the command! This is an issue.

Commands can also have permission restrictions. The developer can define "default permissions" and server staff can then configure who can and cannot use specific commands via the community server's settings.
But I could use a command in a server it wasn't even deployed to. So where exactly was that permission check? Well... In the server the command is in.

Proof of Concept

TL;DR: I can ban you from a server the bot is deployed to, even if that server doesn't have a /ban command, given the app has the command in a server i can use it in.

To proof impact i thought about (and reported on) the following setup:

  1. Create 2 test guilds, EVIL and REGULAR
  2. Setup EVIL: grant the user the ADMINISTRATOR permission (for simplicity)
  3. Setup REGULAR: users have the permission to use slash commands (crucially, no elevated permissions, they are regular users and can use benign, fun commands)
  4. The test application needs to be in both guilds, EVIL and REGULAR
  5. Deploy a slash command to EVIL only, copy the ID of the command
  6. Post a message in the format </exploit:id>
  7. Click the embedded command (optionally fill in options) and execute it despite it not being deployed to the server.

Note

As briefly mentioned above, this really has nothing to do with the command mention, it just serves as a simple interface to interact with and demonstrate the exploit. This can be done without ever interacting with the server (sending the message with the command interaction) by crafting a payload. So steps 6 and 7 can be skipped. Such a payload can be seen below. I have omitted all keys that are irrelevant for understanding what's happening.

{
  "guild_id": "REGULAR",
  "channel_id": "REGULAR_CHANNEL",
  "data": {
    "guild_id": "EVIL",
    "application_command": {
      "guild_id": "EVIL"
    },
  },
}

Discord does not validate that the various guild_id keys are the same. As long as the app is in REGULAR and has permission, i can use any commands that i deployed to EVIL (to come back to the previous example, choosing to opt into all the moderation features) in the context of REGULAR.

Note

While I didn't notice it right away, this also implies that I can redirect the permission check of commands into a server i control!
The permission check uses data.guild_id. Which I can control and can differ from the top level guild_id.

Estimating the exact impact of this is difficult, but it seems to apply to all apps with a command that does something that can be abused and have the permission to do so. I never tried exploiting this, nor have i told anyone what's going on before Discord fixed the issue. I just executed the proof of concept on a willing participant in two empty servers.
Suffice to say, Discord staff found it had enough impact to warrant raising the severity to "Critical" and awarding me with a 3,500$ bug bounty as well as the golden bug hunter badge.

Takeaway Lesson

TL;DR: Principle of least privilege!

I mentioned it above already. Principle of least privilege is king. Falling victim to the "not deployed variant" of this or similar exploits can easily be prevented by not giving apps (or users) permissions they do not need.
If the app does not do moderation tasks, it does not need moderation privileges.

Keep in mind that this is not just true for exploits in the platform API, as I have talked about here.
Anyone with the bot token to any bot on your server can do anything on your server that that bot can theoretically do on your server.
Tokens can be compromised.
Bot owners and their compliance may be bought.

If you are working at a high enough risk level, maybe paying a developer to build and maintain bespoke tools in-house and making the apps private is a thing you should consider.

Tip

I want to clarify again, that any app with permissions and a command to use it could've been exploited by this.
I mention private, bespoke apps here since that would prevent anyone from inviting the app to another server to redirect the permission checks to!

Think twice about who you give permissions to and if they really need them.

Closing

Thanks Discord and all involved staff for the quick acknowledgement, remediation and frictionless bounty reward! With how much bad i hear about disclosures taking ages, this was all said and done in 2 days!

PS: If you asked me about how I got my badge, this article existing is your fault.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment