Who pinged? Python Discord ping incident

discord Jul 07, 2020

We recently had an incident on the Python Discord server which resulted in around 51,000 members from our community receiving a set of notifications from our server.

The dreaded ping

Developers is a Discord role which we assign to users when they join the server and accept our rules and privacy policy, it grants users permissions to participate in our channels and send messages.

Discord allows members to mention roles to notify all members within this role, for certain roles it is ideal to have anyone in the server be able to ping them. Take for example the Moderator role, we want anyone to be able to ping the moderators to report issues that happened in the server. For other roles, we do not want them to be mentionable, the Developers role is one instance of that, we don't want people to be able to notify the entire server of something.

Our permissions are configured so that regular users cannot ping the Developers role, it will just send like a regular message with no notification attached. However, staff members can ping this role so if a staff member was to type "@Developers" it would notify the Populous.

Bot permissions

Our administrator team had permission to ping the role, but additionally our Discord bot, which we use to handle management of users and roles in our community, also has administrator permissions.

The roles held by the bot

The "Admin Bots" role had the permission to mention any role since internally the bot may ping us to notify us of a filter trigger or an anti-spam tripping.

The bug

The bug which allowed the bot to ping users was located inside the error handler for the bot.

The error handler triggers when we ask for a certain input to a command, like a Discord member, but instead receive a wrong input, like a Discord channel.

The error handler triggering

The crucial vulnerability here is that the error handler repeats the users input back to them.

This was fine for a while until Discord introduced a change to the way mentions were handled, see the following GitHub issue:

[New Feature] Allowed mentions by jonzlin95 · Pull Request #1396 · discord/discord-api-docs
Allowed Mentions Hey everyone, Jon from Discord here! We’ve been listening to all the comments on this repo about documenting our message sanitization process to help filter out unwanted mentions f...

This change did have a 60 day migration period attached, however this did not benefit us. The Discord library we use, discord.py, has not implemented the allowed_mentions system in the latest version, instead it is still in an alpha version awaiting release, hence we did not have access to this system on our production code yet.

The incident

The incident occurred when someone ran the command: !raw @Developers

The !raw command returns the content of the message in a codeblock to remove any markdown and show the message content in plain-text. It expects a message link to fetch the content from.

The raw command was not at fault here though, as mentioned above it was the error handler.

Discord.py attempted to convert @Developers into a message, which it is obviously not, this led to the error handler being invoked.

Our error handler looks like the following:

elif isinstance(e, errors.BadArgument):
    await ctx.send(f"Bad argument: {e}\n")
    await prepared_help_command
    self.bot.stats.incr("errors.bad_argument")

This sends the erroring argument back to the channel from the bot, which in this case, was the developers ping. This notified all members of the server.

The fix

Obviously we wanted a swift fix to this issue and the easiest fix for us was to upgrade to the alpha version of Discord.py, 1.4.0a.

This version of discord.py allows you to specify what kind of objects you want the bot to mention. When we create the discord.py bot object it now looks like this:

bot = Bot(
    command_prefix=when_mentioned_or(constants.Bot.prefix),
    activity=discord.Game(name="Commands: !help"),
    case_insensitive=True,
    max_messages=10_000,
    allowed_mentions=discord.AllowedMentions(everyone=False, roles=allowed_roles)
)

We completely disabled the functionality for the bot to mention everyone and only allow a subset of roles to be mentioned, like the moderators and admins roles.

There are alternative fixes available, for example, sanitising every output before sending is a valid solution. Also, monkeypatching is a method used for sending to sanitize. However, this has caused issues before where the sanitisation has been broken by Discord changing how user input is treated and how mentions are resolved.

To clarify, we are not blaming this bug on Discord.py, we are responsible for this bug and should have known better to escape the output from the bot. We have taken steps to mitigate it. Discord.py is maintained by a small group of people and is an excellent library, a lot of Discord API wrappers have based their structure off it.

Having suffered from this type of vulnerability, if you are a bot developer we highly recommend you review your code. Ensure that all user input is sanitised, if possible make sure the allowed mentions system is being used as well.

You will find the relevant commits we made to our bot here:

Prevent bot from mentioning roles · python-discord/[email protected]
This was open to abuse when the bot relayed user input.
Allow owners, admins, and mods roles to be pinged · python-discord/[email protected]
The community bot for the Python Discord community - python-discord/bot

If you need any help fixing this with a discord.py bot, come see us in our #discord.py channel in Python Discord.

Joseph Banks

Student studying Computer Science in the UK. I'm an owner of Python Discord, the biggest Python community on Discord and one of the largest in the world, see more at https://pythondiscord.com/!

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.