diff --git a/docs/changelog_3_4_0.rst b/docs/changelog_3_4_0.rst index 66d38fe9b..2f83b87e2 100644 --- a/docs/changelog_3_4_0.rst +++ b/docs/changelog_3_4_0.rst @@ -9,9 +9,10 @@ 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 `. -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 ` 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 `. +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 `_ 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``. @@ -19,6 +20,15 @@ Read before updating 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 `_. + Core Bot ******** diff --git a/redbot/cogs/mod/kickban.py b/redbot/cogs/mod/kickban.py index 88b9e0537..56370a478 100644 --- a/redbot/cogs/mod/kickban.py +++ b/redbot/cogs/mod/kickban.py @@ -2,7 +2,7 @@ import asyncio 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 @@ class KickBanMixin(MixinMeta): 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)