VYPR
Moderate severityNVD Advisory· Published Oct 28, 2020· Updated Aug 4, 2024

Unauthorized privilege escalation in Mod module

CVE-2020-15278

Description

CVE-2020-15278 is a privilege escalation in Red Discord Bot's Mod module prior to 3.4.1, allowing high-privilege users to bypass hierarchy checks and perform destructive actions.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2020-15278 is a privilege escalation in Red Discord Bot's Mod module prior to 3.4.1, allowing high-privilege users to bypass hierarchy checks and perform destructive actions.

Vulnerability

Overview

CVE-2020-15278 is an unauthorized privilege escalation vulnerability in the Mod module of Red Discord Bot, a self-hosted Discord bot framework [3]. The vulnerability resides in the way the bot handles the massban command, specifically when processing user IDs to ban. Prior to version 3.4.1, the bot's code did not adequately verify that the user executing the command had proper hierarchy over the target users, potentially allowing a high-privilege user (e.g., a server administrator) to bypass Discord's built-in role hierarchy checks under certain conditions [1][4].

Exploitation

To exploit this flaw, an attacker must already have a high privilege level (such as the "administrator" permission) within the Discord guild (server) [1]. The vulnerability is triggered when the Mod module's massban command is used in a specific state that is beyond the attacker's control, likely tied to how the bot caches member data. The root cause was that the bot failed to query the guild for all members when the guild was not fully "chunked" (i.e., member list not fully loaded), leading to the bypass of hierarchy checks for members not initially in the cache [4]. An attacker could then ban users who are higher in role hierarchy than themselves, which Discord normally prevents.

Impact

Successful exploitation allows the attacker to perform destructive actions within the guild, such as banning users who outrank them, thereby disrupting the server's moderation and potentially causing loss of members or administrative control [1]. The impact is limited to actions within the guild where the attacker has high privileges, but could lead to serious disruption of community management.

Mitigation

The vulnerability is fixed in Red Discord Bot version 3.4.1, released on October 28, 2020 [1][2]. Users are strongly recommended to update to this version or later. As a temporary workaround, administrators can unload the Mod module using unload mod or disable the massban command with command disable global massban to prevent exploitation until the update can be applied [1]. The fix involved ensuring that the bot properly queries for all guild members and uses its ban_user() method with hierarchy checks for every target, rather than bypassing them for members not initially in the cache [4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
Red-DiscordBotPyPI
< 3.4.13.4.1

Affected products

2

Patches

1
726bfd38adfd

Merge pull request from GHSA-mp9m-g7qj-6vqr

2 files changed · +47 24
  • docs/changelog_3_4_0.rst+13 3 modified
    @@ -9,16 +9,26 @@ Redbot 3.4.1 (2020-10-27)
     Read before updating
     --------------------
     
    -1. This Red update bumps discord.py to version 1.5.1, which explicitly requests Discord intents. Red requires all Prvileged Intents to be enabled. More information can be found at :ref:`enabling-privileged-intents`.
    -2. Mutes functionality has been moved from the Mod cog to a new separate cog (Mutes) featuring timed and role-based mutes. If you were using it (or want to start now), you can load the new cog with ``[p]load mutes``. You can see the full `Mutes changelog below <important-341-1>`.
    -3. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point):
    +1. This release fixes a security issue in Mod cog. See `Security changelog below <important-341-2>` for more information.
    +2. This Red update bumps discord.py to version 1.5.1, which explicitly requests Discord intents. Red requires all Prvileged Intents to be enabled. More information can be found at :ref:`enabling-privileged-intents`.
    +3. Mutes functionality has been moved from the Mod cog to a new separate cog (Mutes) featuring timed and role-based mutes. If you were using it (or want to start now), you can load the new cog with ``[p]load mutes``. You can see the full `Mutes changelog below <important-341-1>`.
    +4. Information for Audio users that are using an external Lavalink instance (if you don't know what that is, you should skip this point):
     
        We've updated our `application.yml file <https://github.com/Cog-Creators/Red-DiscordBot/blob/3.4.1/redbot/cogs/audio/data/application.yml>`_ and you should update your instance's ``application.yml`` appropriately.
        Please ensure that the WS port in Audio's settings (``[p]llset wsport``) is set to the port from the ``application.yml``.
     
     End-user changelog
     ------------------
     
    +.. _important-341-2:
    +
    +Security
    +********
    +
    +**NOTE:** If you can't update immediately, we recommend globally disabling the affected command until you can.
    +
    +- **Mod** - Fixed unauthorized privilege escalation exploit in ``[p]massban`` (also called ``[p]hackban``) command. Full security advisory `can be found on our GitHub <https://github.com/Cog-Creators/Red-DiscordBot/security/advisories/GHSA-mp9m-g7qj-6vqr>`_.
    +
     Core Bot
     ********
     
    
  • redbot/cogs/mod/kickban.py+34 21 modified
    @@ -2,7 +2,7 @@
     import contextlib
     import logging
     from datetime import datetime, timedelta, timezone
    -from typing import Optional, Tuple, Union
    +from typing import Dict, List, Optional, Tuple, Union
     
     import discord
     from redbot.core import commands, i18n, checks, modlog
    @@ -440,28 +440,41 @@ def remove_processed(ids):
                 await show_results()
                 return
     
    +        # We need to check here, if any of the users isn't a member and if they are,
    +        # we need to use our `ban_user()` method to do hierarchy checks.
    +        members: Dict[int, discord.Member] = {}
    +        to_query: List[int] = []
    +
             for user_id in user_ids:
    -            user = guild.get_member(user_id)
    -            if user is not None:
    -                if user_id in tempbans:
    -                    # We need to check if a user is tempbanned here because otherwise they won't be processed later on.
    -                    continue
    +            member = guild.get_member(user_id)
    +            if member is not None:
    +                members[user_id] = member
    +            elif not guild.chunked:
    +                to_query.append(user_id)
    +
    +        # If guild isn't chunked, we might possibly be missing the member from cache,
    +        # so we need to make sure that isn't the case by querying the user IDs for such guilds.
    +        while to_query:
    +            queried_members = await guild.query_members(user_ids=to_query[:100], limit=100)
    +            members.update((member.id, member) for member in queried_members)
    +            to_query = to_query[100:]
    +
    +        # Call `ban_user()` method for all users that turned out to be guild members.
    +        for member in members:
    +            try:
    +                success, reason = await self.ban_user(
    +                    user=member, ctx=ctx, days=days, reason=reason, create_modlog_case=True
    +                )
    +                if success:
    +                    banned.append(user_id)
                     else:
    -                    # Instead of replicating all that handling... gets attr from decorator
    -                    try:
    -                        success, reason = await self.ban_user(
    -                            user=user, ctx=ctx, days=days, reason=reason, create_modlog_case=True
    -                        )
    -                        if success:
    -                            banned.append(user_id)
    -                        else:
    -                            errors[user_id] = _("Failed to ban user {user_id}: {reason}").format(
    -                                user_id=user_id, reason=reason
    -                            )
    -                    except Exception as e:
    -                        errors[user_id] = _("Failed to ban user {user_id}: {reason}").format(
    -                            user_id=user_id, reason=e
    -                        )
    +                    errors[user_id] = _("Failed to ban user {user_id}: {reason}").format(
    +                        user_id=user_id, reason=reason
    +                    )
    +            except Exception as e:
    +                errors[user_id] = _("Failed to ban user {user_id}: {reason}").format(
    +                    user_id=user_id, reason=e
    +                )
     
             user_ids = remove_processed(user_ids)
     
    

Vulnerability mechanics

Root cause

"Missing authorization check when banning users who are not in the bot's member cache due to an unchunked guild, allowing bypass of Discord role hierarchy enforcement."

Attack vector

An attacker who already has a high privilege level (e.g., a moderator role) within a Discord guild can exploit the `massban` command when the guild is not fully "chunked" (i.e., the bot's member cache is incomplete). In this state, `guild.get_member()` returns `None` for users who are actually guild members, causing the original code to skip hierarchy checks and ban those users directly. This allows the attacker to ban users who are equal or higher in the role hierarchy than themselves, including other moderators or administrators, without the intended authorization enforcement [CWE-285]. The condition (guild not chunked) is outside the attacker's control, making this a privilege escalation that can be triggered opportunistically.

Affected code

The vulnerability resides in the `massban` (also called `hackban`) command within the Mod module, specifically in `redbot/cogs/mod/kickban.py`. The original code called `guild.get_member(user_id)` and, if the user was not found in the local cache, proceeded to ban the user directly via `guild.ban()` without performing Discord hierarchy checks. The patch adds logic to query uncached members for unchunked guilds and then routes all member bans through the `ban_user()` method, which enforces proper authorization checks.

What the fix does

The patch modifies the `massban` command in `kickban.py` to first collect all user IDs that are not found in the local cache but belong to an unchunked guild, then query those members via `guild.query_members()` to ensure the bot has accurate membership data. Once all members are resolved, the code iterates over the resolved `members` dict and calls `self.ban_user()` for each one, which performs the proper Discord hierarchy checks before executing the ban. This closes the bypass by ensuring that no guild member can be banned without authorization verification, regardless of the guild's chunked state [patch_id=1697687].

Preconditions

  • authThe attacker must have a high privilege level (e.g., moderator role) within the Discord guild that allows use of the massban command.
  • configThe Discord guild must be in an unchunked state (bot's member cache incomplete), a condition outside the attacker's control.
  • configThe Mod module must be loaded and the massban command must not be globally disabled.

Generated on May 23, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.