VYPR
High severityNVD Advisory· Published Apr 9, 2021· Updated Aug 3, 2024

Improper Input Validation in sopel-plugins.channelmgnt

CVE-2021-21431

Description

In sopel-channelmgnt prior to 2.0.1, an IRC bot operator could bypass kick/ban restrictions by using a comma or # to target multiple users, potentially removing the bot or users from other channels.

AI Insight

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

In sopel-channelmgnt prior to 2.0.1, an IRC bot operator could bypass kick/ban restrictions by using a comma or # to target multiple users, potentially removing the bot or users from other channels.

Vulnerability

In the sopel-channelmgnt plugin for the Sopel IRC bot, versions prior to 2.0.1, the kick and ban commands did not properly validate user input. An attacker could include a comma (,) or a hash (#) in the target argument, causing the bot to attempt to kick or ban multiple users at once or target a different channel. This bypasses the intended restriction that prevents the bot from being removed via these commands. The issue is triggered only on IRC networks where the TARGMAX parameter is greater than 1, as stated in [1].

Exploitation

To exploit this vulnerability, an attacker must have operator privileges (flag +o) in the channel where the bot resides. The attacker issues a command such as .kick <bot_nick>,<other_nick> or .ban # to the bot. The bot's code in the vulnerable versions accepted these malformed arguments without checking for commas or hashes, and wrote the corresponding IRC KICK or BAN command to the server. This is evident from the fix commit [2], which adds checks to reject such inputs. No race condition or additional user interaction is required beyond the attacker having operator status.

Impact

Successful exploitation allows an attacker to remove the bot from the channel (e.g., by kicking itself) and potentially remove other users. The advisory [1] also notes the possibility, without a proof of concept, of removing users from other channels by including a channel name in the target. This could lead to disruption of channel management and abuse of operator privileges. Freenode IRC network was not affected [1].

Mitigation

The vulnerability is fixed in version 2.0.1 of sopel-channelmgnt, released on April 9, 2021 [1]. The fix adds input validation to reject target strings containing commas or hashes [2]. As a workaround, the plugin should not be used on IRC networks where TARGMAX > 1 [1]. The repository has since been archived [3], but users should update to the patched version or apply the workaround.

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
sopel-plugins.channelmgntPyPI
< 2.0.12.0.1

Affected products

2

Patches

2
7c96d4003582

[SECURITY] Release 2.0.1 (#65)

3 files changed · +19 3
  • dev-requirements.txt+1 1 modified
    @@ -15,7 +15,7 @@ flake8-fixme==1.1.1
     flake8-multiline-containers==0.0.17
     flake8-print==4.0.0
     flake8-pytest-style==1.3.0
    -flake8-return==1.1.2
    +#flake8-return==1.1.2
     flake8-quotes==3.2.0
     flake8-simplify==0.13.0
     flake8-pytest==1.3
    
  • setup.py+2 2 modified
    @@ -10,12 +10,12 @@
     
     setup(
         name='sopel_plugins.channelmgnt',
    -    version='2.0',
    +    version='2.0.1',
         description='Channelmgnt plugin for Sopel',
         long_description=readme,
         long_description_content_type='text/markdown',  # This is important!
         author='MirahezeBot Contributors',
    -    author_email='bots@miraheze.org',
    +    author_email='staff@mirahezebots.org',
         url='https://github.com/MirahezeBots/sopel-channelmgnt',
         packages=find_packages('.'),
         include_package_data=True,
    
  • sopel_channelmgnt/channelmgnt/__init__.py+16 0 modified
    @@ -216,6 +216,10 @@ def kick(bot, trigger):
                 return
             nick = Identifier(text[1])
             reason = ' '.join(text[2:])
    +        if ',' in str(nick):
    +            return bot.reply('Unable to kick. Kicking multiple users is not allowed.')
    +        if '#' in str(nick):
    +            return bot.reply('Unable to kick. Use of # when kicking is not expected.')
             if nick != bot.config.core.nick and trigger.account in chanops:
                 bot.write(['KICK', trigger.sender, nick, ':' + reason])
                 if dodeop:
    @@ -263,6 +267,10 @@ def parse_host_mask(text):
     @example('.ban Zppix')
     def ban(bot, trigger):
         """Ban a user from the channel. The bot must be a channel operator for this command to work."""
    +    if ',' in str(parse_host_mask(trigger.group().split())):
    +        return bot.reply('Unable to ban. Banning multiple users is not allowed.')
    +    if '#' in str(parse_host_mask(trigger.group().split())):
    +        return bot.reply('Unable to ban. Use of # when banning is not expected.')
         makemodechange(bot, trigger, '+b', isbqmode=True)
     
     
    @@ -271,6 +279,10 @@ def ban(bot, trigger):
     @example('.unban Zppix')
     def unban(bot, trigger):
         """Unban a user from the channel. The bot must be a channel operator for this command to work."""
    +    if ',' in str(parse_host_mask(trigger.group().split())):
    +        return bot.reply('Unable to ban. Banning multiple users is not allowed.')
    +    if '#' in str(parse_host_mask(trigger.group().split())):
    +        return bot.reply('Unable to ban. Use of # when banning is not expected.')
         makemodechange(bot, trigger, '-b', isbqmode=True)
     
     
    @@ -312,6 +324,10 @@ def kickban(bot, trigger):
                     deopbot(trigger.sender, bot)
                 return
             nick = Identifier(text[1])
    +        if ',' in str(nick):
    +            return bot.reply('Unable to kickban. Kickbanning multiple users is not allowed.')
    +        if '#' in str(nick):
    +            return bot.reply('Unable to kickban. Use of # when kickbanning is not expected.')
             mask = text[2] if any(s in text[2] for s in '!@*') else ''
             reasonidx = 3 if mask != '' else 2
             reason = ' '.join(text[reasonidx:])
    
643388365f28

Merge pull request from GHSA-23c7-6444-399m

1 file changed · +12 0
  • sopel_channelmgnt/channelmgnt/__init__.py+12 0 modified
    @@ -216,6 +216,10 @@ def kick(bot, trigger):
                 return
             nick = Identifier(text[1])
             reason = ' '.join(text[2:])
    +        if ',' in str(nick):
    +            return bot.reply('Unable to kick. Kicking multiple users is not allowed.') 
    +        if '#' in str(nick):
    +            return bot.reply('Unable to kick. Use of # when kicking is not expected.')
             if nick != bot.config.core.nick and trigger.account in chanops:
                 bot.write(['KICK', trigger.sender, nick, ':' + reason])
                 if dodeop:
    @@ -263,6 +267,10 @@ def parse_host_mask(text):
     @example('.ban nick')
     def ban(bot, trigger):
         """Ban a user from the channel. The bot must be a channel operator for this command to work."""
    +    if ',' in str(nick):
    +        return bot.reply('Unable to ban. Banning multiple users is not allowed.') 
    +    if '#' in str(nick):
    +        return bot.reply('Unable to ban. Use of # when banning is not expected.')
         makemodechange(bot, trigger, '+b', isbqmode=True)
     
     
    @@ -312,6 +320,10 @@ def kickban(bot, trigger):
                     deopbot(trigger.sender, bot)
                 return
             nick = Identifier(text[1])
    +        if ',' in str(nick):
    +            return bot.reply('Unable to kickban. Kickbanning multiple users is not allowed.') 
    +        if '#' in str(nick):
    +            return bot.reply('Unable to kickban. Use of # when kickbanning is not expected.')
             mask = text[2] if any(s in text[2] for s in '!@*') else ''
             reasonidx = 3 if mask != '' else 2
             reason = ' '.join(text[reasonidx:])
    

Vulnerability mechanics

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

References

8

News mentions

0

No linked articles in our index yet.