------------------------------------------------------------ revno: 1873 fixes bugs: https://launchpad.net/bugs/1947639 https://launchpad.net/bugs/1947640 committer: Mark Sapiro <m...@msapiro.net> branch nick: 2.1 timestamp: Mon 2021-10-18 16:56:42 -0700 message: Fixes for CVEs 2021-42096 and 2021-42097. modified: Mailman/CSRFcheck.py Mailman/Cgi/options.py Mailman/SecurityManager.py NEWS
-- lp:mailman/2.1 https://code.launchpad.net/~mailman-coders/mailman/2.1 Your team Mailman Checkins is subscribed to branch lp:mailman/2.1. To unsubscribe from this branch go to https://code.launchpad.net/~mailman-coders/mailman/2.1/+edit-subscription
=== modified file 'Mailman/CSRFcheck.py' --- Mailman/CSRFcheck.py 2018-06-17 23:47:34 +0000 +++ Mailman/CSRFcheck.py 2021-10-18 23:56:42 +0000 @@ -18,11 +18,13 @@ """ Cross-Site Request Forgery checker """ import time +import urllib import marshal import binascii from Mailman import mm_cfg -from Mailman.Utils import sha_new +from Mailman.Logging.Syslog import syslog +from Mailman.Utils import UnobscureEmail, sha_new keydict = { 'user': mm_cfg.AuthUser, @@ -37,6 +39,10 @@ def csrf_token(mlist, contexts, user=None): """ create token by mailman cookie generation algorithm """ + if user: + # Unmunge a munged email address. + user = UnobscureEmail(urllib.unquote(user)) + for context in contexts: key, secret = mlist.AuthContextInfo(context, user) if key: @@ -49,9 +55,8 @@ token = binascii.hexlify(marshal.dumps((issued, keymac))) return token -def csrf_check(mlist, token): +def csrf_check(mlist, token, options_user=None): """ check token by mailman cookie validation algorithm """ - try: issued, keymac = marshal.loads(binascii.unhexlify(token)) key, received_mac = keymac.split(':', 1) @@ -62,6 +67,17 @@ key, user = key.split('+', 1) else: user = None + if user: + # This is for CVE-2021-42097. The token is a user token because + # of the fix for CVE-2021-42096 but it must match the user for + # whom the options page is requested. + raw_user = UnobscureEmail(urllib.unquote(user)) + if options_user and options_user != raw_user: + syslog('mischief', + 'Form for user %s submitted with CSRF token ' + 'issued for %s.', + options_user, raw_user) + return False context = keydict.get(key) key, secret = mlist.AuthContextInfo(context, user) assert key === modified file 'Mailman/Cgi/options.py' --- Mailman/Cgi/options.py 2020-05-18 17:01:51 +0000 +++ Mailman/Cgi/options.py 2021-10-18 23:56:42 +0000 @@ -54,9 +54,6 @@ True = 1 False = 0 -AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin, - mm_cfg.AuthListModerator, mm_cfg.AuthUser) - def main(): global _ @@ -124,15 +121,6 @@ print doc.Format() return - if set(params) - set(safe_params): - csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token')) - else: - csrf_checked = True - # if password is present, void cookie to force password authentication. - if cgidata.getfirst('password'): - os.environ['HTTP_COOKIE'] = '' - csrf_checked = True - # Set the language for the page. If we're coming from the listinfo cgi, # we might have a 'language' key in the cgi data. That was an explicit # preference to view the page in, so we should honor that here. If that's @@ -169,6 +157,16 @@ user = user[-1].strip() # Avoid cross-site scripting attacks + if set(params) - set(safe_params): + csrf_checked = csrf_check(mlist, cgidata.getfirst('csrf_token'), + Utils.UnobscureEmail(urllib.unquote(user))) + else: + csrf_checked = True + # if password is present, void cookie to force password authentication. + if cgidata.getfirst('password'): + os.environ['HTTP_COOKIE'] = '' + csrf_checked = True + safeuser = Utils.websafe(user) try: Utils.ValidateEmail(user) @@ -871,8 +869,9 @@ mlist.FormatButton('othersubs', _('List my other subscriptions'))) replacements['<mm-form-start>'] = ( + # Always make the CSRF token for the user. CVE-2021-42096 mlist.FormatFormStart('options', user, mlist=mlist, - contexts=AUTH_CONTEXTS, user=user)) + contexts=[mm_cfg.AuthUser], user=user)) replacements['<mm-user>'] = user replacements['<mm-presentable-user>'] = presentable_user replacements['<mm-email-my-pw>'] = mlist.FormatButton( === modified file 'Mailman/SecurityManager.py' --- Mailman/SecurityManager.py 2018-06-17 23:47:34 +0000 +++ Mailman/SecurityManager.py 2021-10-18 23:56:42 +0000 @@ -104,6 +104,7 @@ if user is None: # A bad system error raise TypeError, 'No user supplied for AuthUser context' + user = Utils.UnobscureEmail(urllib.unquote(user)) secret = self.getMemberPassword(user) userdata = urllib.quote(Utils.ObscureEmail(user), safe='') key += 'user+%s' % userdata === modified file 'NEWS' --- NEWS 2021-06-06 17:55:49 +0000 +++ NEWS 2021-10-18 23:56:42 +0000 @@ -5,7 +5,17 @@ Here is a history of user visible changes to Mailman. -2.1.35 (xx-xxx-xxxx) +2.1.35 (19-Oct-2021) + + Security + + - A potential for for a list member to carry out an off-line brute force + attack to obtain the list admin password has been reported by Andre + Protas, Richard Cloke and Andy Nuttall of Apple. This is fixed. + CVE-2021-42096 (LP:#1947639) + + - A CSRF attack via the user options page could allow takeover of a users + account. This is fixed. CVE-2021-42097 (LP:#1947640) Bug Fixes and other patches
_______________________________________________ Mailman-checkins mailing list -- mailman-checkins@python.org To unsubscribe send an email to mailman-checkins-le...@python.org https://mail.python.org/mailman3/lists/mailman-checkins.python.org/ Member address: arch...@jab.org