CVE-2020-15172 (Python, published 2020-09-15T19:15Z)
Description: value: The Act module for Red Discord Bot before commit 6b9f3b86 is vulnerable to Remote Code Execution. With this exploit, Discord users can use specially crafted messages to perform destructive actions and/or access sensitive information. Unloading the Act module with `unload act` can render this exploit inaccessible.
Severity: MEDIUM (CVSS v3 base score: 8.8)
Weakness: CWE-502 \u2014 Deserialization of Untrusted Data
The product deserializes untrusted data without sufficiently verifying that the resulting data will be valid.
Repository: https://github.com/zephyrkul/FluffyCogs
Files changed:
act/act.py
Commit message: [act] don't trigger on disabled cmds


Vulnerable code:
import itertools
from redbot.core import Config, checks, commands
    def __init__(self, bot):
    async def initialize(self, bot):
    async def act(self, ctx, *, target: Union[discord.Member, str] = None):
            message = message.format(target, user=target)
    async def actset(self, ctx):
    async def customize(self, ctx, command: str.lower, *, response: str = None):
        await ctx.tick()
    async def customize_global(self, ctx, command: str.lower, *, response: str = None):
    async def ignore(self, ctx, command: str.lower):
    async def ignore_global(self, ctx, command: str.lower):
    async def tenorkey(self, ctx):
    async def on_message(self, message):
        if message.author.bot:

        ctx = await self.bot.get_context(message)
        if ctx.prefix is None or not ctx.invoked_with.replace("_", "").isalpha():

        if ctx.valid and ctx.command.enabled:
            try:
                if await ctx.command.can_run(ctx):
                    return
            except commands.errors.CheckFailure:
                return

        ctx.command = self.act
        await self.bot.invoke(ctx)

Fixed code:
import functools
import re
from redbot.core import bot, Config, checks, commands
fmt_re = re.compile(r"{(?:0|user)(?:\.([^\{]+))?}")

    def __init__(self, bot: bot.Red):
    async def initialize(self, bot: bot.Red):
    @staticmethod
    def repl(target: discord.Member, match: re.Match):
        if attr := match.group(1):
            print(attr)
            if attr.startswith("_") or "." in attr:
                return str(target)
            try:
                return str(getattr(target, attr))
            except AttributeError:
                return str(target)
        return str(target)

    async def act(self, ctx: commands.Context, *, target: Union[discord.Member, str] = None):
            assert isinstance(message, str)
            message = fmt_re.sub(functools.partial(self.repl, target), message)
    async def actset(self, ctx: commands.Context):
    async def customize(self, ctx: commands.Context, command: str.lower, *, response: str = None):
            await ctx.tick()
            await ctx.send(
                fmt_re.sub(functools.partial(self.repl, ctx.author), response),
                allowed_mentions=discord.AllowedMentions(users=False),
            )
    async def customize_global(
        self, ctx: commands.Context, command: str.lower, *, response: str = None
    ):
    async def ignore(self, ctx: commands.Context, command: str.lower):
    async def ignore_global(self, ctx: commands.Context, command: str.lower):
    async def tenorkey(self, ctx: commands.Context):
    async def on_command_error(
        self, ctx: commands.Context, error: commands.CommandError, unhandled_by_cog: bool = False
    ):
        if ctx.command == self.act:
        if isinstance(error, commands.UserFeedbackCheckFailure):
            # UserFeedbackCheckFailure inherits from CheckFailure
        elif isinstance(error, (commands.CheckFailure, commands.CommandNotFound)):
            ctx.command = self.act
            await ctx.bot.invoke(ctx)