From mark at msapiro.net Fri Aug 19 03:09:53 2016 From: mark at msapiro.net (Mark Sapiro) Date: Fri, 19 Aug 2016 00:09:53 -0700 Subject: [Mailman-Announce] Imminent release of a Mailman security fix. Message-ID: <5aaec549-ad73-5192-8a7a-40eeaf2f4bbe@msapiro.net> There is a CSRF vulnerability associated with the user options page. This could conceivably allow an attacker to obtain a user's password. This is reported at . I have developed a fix which is a small patch to two modules. I plan to release Mailman 2.1.23 with this and other fixes on Saturday, Aug 27 and also to post at the same time the patch which can be applied stand-alone. Neither the bug report nor the fix reveals much detail about the attack, but to allay any concern, I'm delaying the release for a week to allow people to plan for installation of at least the patch at the time of release. -- Mark Sapiro The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 181 bytes Desc: OpenPGP digital signature URL: From mark at msapiro.net Sat Aug 27 09:47:35 2016 From: mark at msapiro.net (Mark Sapiro) Date: Sat, 27 Aug 2016 06:47:35 -0700 Subject: [Mailman-Announce] Mailman 2.1.23 release and fix for CVE-2016-6893 Message-ID: <53fe80e4-ec41-5c51-8f1b-587c6dafda3e@msapiro.net> I am pleased to announce the release of Mailman 2.1.23. Python 2.4 is the minimum supported, but Python 2.7 is strongly recommended. This release contains a fix for CVE-2016-6893 which is also attached here as a patch. It also has new features, a few i18n updates and some bug fixes. See the attached README for details. Mailman is free software for managing email mailing lists and e-newsletters. Mailman is used for all the python.org and SourceForge.net mailing lists, as well as at hundreds of other sites. For more information, please see our web site at one of: http://www.list.org http://www.gnu.org/software/mailman http://mailman.sourceforge.net/ http://mirror.list.org/ Mailman 2.1.23 can be downloaded from https://launchpad.net/mailman/2.1/ http://ftp.gnu.org/gnu/mailman/ https://sourceforge.net/projects/mailman/ -- Mark Sapiro The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan -------------- next part -------------- An embedded and charset-unspecified text was scrubbed... Name: README URL: -------------- next part -------------- Patch for CVE-2016-6893 This will apply with possible minor line number diffs to any Mailman >= 2.1.15 For Mailman < 2.1.15, the required Mailman/CSRFcheck.py module doesn't exist and other CSRF vulnerabilities exist in the admin UI, so upgrade is recommended. === modified file 'Mailman/Cgi/admindb.py' --- Mailman/Cgi/admindb.py 2016-07-14 21:27:49 +0000 +++ Mailman/Cgi/admindb.py 2016-08-23 23:12:05 +0000 @@ -39,6 +39,7 @@ from Mailman.Cgi import Auth from Mailman.htmlformat import * from Mailman.Logging.Syslog import syslog +from Mailman.CSRFcheck import csrf_check EMPTYSTRING = '' NL = '\n' @@ -58,6 +59,9 @@ else: ssort = SSENDER +AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin, + mm_cfg.AuthListModerator) + def helds_by_skey(mlist, ssort=SSENDER): @@ -135,6 +139,18 @@ print doc.Format() return + # CSRF check + safe_params = ['adminpw', 'admlogin', 'msgid', 'sender', 'details'] + params = cgidata.keys() + if set(params) - set(safe_params): + csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token')) + else: + csrf_checked = True + # if password is present, void cookie to force password authentication. + if cgidata.getvalue('adminpw'): + os.environ['HTTP_COOKIE'] = '' + csrf_checked = True + if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin, mm_cfg.AuthListModerator, mm_cfg.AuthSiteAdmin), @@ -212,7 +228,11 @@ elif not details: # This is a form submission doc.SetTitle(_('%(realname)s Administrative Database Results')) - process_form(mlist, doc, cgidata) + if csrf_checked: + process_form(mlist, doc, cgidata) + else: + doc.addError( + _('The form lifetime has expired. (request forgery check)')) # Now print the results and we're done. Short circuit for when there # are no pending requests, but be sure to save the results! admindburl = mlist.GetScriptURL('admindb', absolute=1) @@ -234,7 +254,7 @@ mlist.Save() return - form = Form(admindburl) + form = Form(admindburl, mlist=mlist, contexts=AUTH_CONTEXTS) # Add the instructions template if details == 'instructions': doc.AddItem(Header( === modified file 'Mailman/Cgi/edithtml.py' --- Mailman/Cgi/edithtml.py 2016-07-14 21:27:49 +0000 +++ Mailman/Cgi/edithtml.py 2016-08-23 23:12:05 +0000 @@ -30,9 +30,12 @@ from Mailman.Cgi import Auth from Mailman.Logging.Syslog import syslog from Mailman import i18n +from Mailman.CSRFcheck import csrf_check _ = i18n._ +AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin) + def main(): @@ -104,6 +107,18 @@ print doc.Format() return + # CSRF check + safe_params = ['VARHELP', 'adminpw', 'admlogin'] + params = cgidata.keys() + if set(params) - set(safe_params): + csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token')) + else: + csrf_checked = True + # if password is present, void cookie to force password authentication. + if cgidata.getvalue('adminpw'): + os.environ['HTTP_COOKIE'] = '' + csrf_checked = True + # Editing the html for a list is limited to the list admin and site admin. if not mlist.WebAuthenticate((mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin), @@ -148,7 +163,11 @@ try: if cgidata.keys(): - ChangeHTML(mlist, cgidata, template_name, doc) + if csrf_checked: + ChangeHTML(mlist, cgidata, template_name, doc) + else: + doc.addError( + _('The form lifetime has expired. (request forgery check)')) FormatHTML(mlist, doc, template_name, template_info) finally: doc.AddItem(mlist.GetMailmanFooter()) @@ -167,7 +186,8 @@ doc.AddItem(FontSize("+1", link)) doc.AddItem('

') doc.AddItem('


') - form = Form(mlist.GetScriptURL('edithtml') + '/' + template_name) + form = Form(mlist.GetScriptURL('edithtml') + '/' + template_name, + mlist=mlist, contexts=AUTH_CONTEXTS) text = Utils.maketext(template_name, raw=1, mlist=mlist) # MAS: Don't websafe twice. TextArea does it. form.AddItem(TextArea('html_code', text, rows=40, cols=75)) === modified file 'Mailman/Cgi/options.py' --- Mailman/Cgi/options.py 2016-07-14 21:27:49 +0000 +++ Mailman/Cgi/options.py 2016-08-23 23:12:05 +0000 @@ -33,6 +33,7 @@ from Mailman import i18n from Mailman.htmlformat import * from Mailman.Logging.Syslog import syslog +from Mailman.CSRFcheck import csrf_check OR = '|' SLASH = '/' @@ -51,6 +52,8 @@ True = 1 False = 0 +AUTH_CONTEXTS = (mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin, + mm_cfg.AuthListModerator, mm_cfg.AuthUser) def main(): @@ -104,6 +107,19 @@ # The total contents of the user's response cgidata = cgi.FieldStorage(keep_blank_values=1) + # CSRF check + safe_params = ['displang-button', 'language', 'email', 'password', 'login', + 'login-unsub', 'login-remind', 'VARHELP', 'UserOptions'] + params = cgidata.keys() + if set(params) - set(safe_params): + csrf_checked = csrf_check(mlist, cgidata.getvalue('csrf_token')) + else: + csrf_checked = True + # if password is present, void cookie to force password authentication. + if cgidata.getvalue('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 @@ -315,6 +331,15 @@ print doc.Format() return + # Before going further, get the result of CSRF check and do nothing + # if it has failed. + if csrf_checked == False: + doc.addError( + _('The form lifetime has expired. (request forgery check)')) + options_page(mlist, doc, user, cpuser, userlang) + print doc.Format() + return + if cgidata.has_key('logout'): print mlist.ZapCookie(mm_cfg.AuthUser, user) loginpage(mlist, doc, user, language) @@ -832,7 +857,8 @@ mlist.FormatButton('othersubs', _('List my other subscriptions'))) replacements[''] = ( - mlist.FormatFormStart('options', user)) + mlist.FormatFormStart('options', user, mlist=mlist, + contexts=AUTH_CONTEXTS, user=user)) replacements[''] = user replacements[''] = presentable_user replacements[''] = mlist.FormatButton( === modified file 'Mailman/HTMLFormatter.py' --- Mailman/HTMLFormatter.py 2015-02-13 18:41:28 +0000 +++ Mailman/HTMLFormatter.py 2016-08-23 23:04:58 +0000 @@ -28,6 +28,8 @@ from Mailman.i18n import _ +from Mailman.CSRFcheck import csrf_token + EMPTYSTRING = '' BR = '
' @@ -317,12 +319,17 @@ container.AddItem("") return container - def FormatFormStart(self, name, extra=''): + def FormatFormStart(self, name, extra='', + mlist=None, contexts=None, user=None): base_url = self.GetScriptURL(name) if extra: full_url = "%s/%s" % (base_url, extra) else: full_url = base_url + if mlist: + return ("""
+""" + % (full_url, csrf_token(mlist, contexts, user))) return ('' % full_url) def FormatArchiveAnchor(self): === modified file 'Mailman/htmlformat.py' --- Mailman/htmlformat.py 2016-07-15 02:10:24 +0000 +++ Mailman/htmlformat.py 2016-08-23 23:20:30 +0000 @@ -407,13 +407,14 @@ class Form(Container): def __init__(self, action='', method='POST', encoding=None, - mlist=None, contexts=None, *items): + mlist=None, contexts=None, user=None, *items): apply(Container.__init__, (self,) + items) self.action = action self.method = method self.encoding = encoding self.mlist = mlist self.contexts = contexts + self.user = user def set_action(self, action): self.action = action @@ -428,7 +429,7 @@ if self.mlist: output = output + \ '\n' \ - % csrf_token(self.mlist, self.contexts) + % csrf_token(self.mlist, self.contexts, self.user) output = output + Container.Format(self, indent+2) output = '%s\n%s
\n' % (output, spaces) return output -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 181 bytes Desc: OpenPGP digital signature URL: From mark at msapiro.net Sat Aug 27 10:21:36 2016 From: mark at msapiro.net (Mark Sapiro) Date: Sat, 27 Aug 2016 07:21:36 -0700 Subject: [Mailman-Announce] [Mailman-Users] Mailman 2.1.23 release and fix for CVE-2016-6893 In-Reply-To: References: <53fe80e4-ec41-5c51-8f1b-587c6dafda3e@msapiro.net> Message-ID: On 08/27/2016 07:03 AM, Jim Popovitch wrote: > I'm not seeing the CVE-2016-6893 fix in bazzar at > https://code.launchpad.net/~mailman-coders/mailman/2.1, shouldn't it > be there now too? It's there now. Thanks for the reminder. -- Mark Sapiro The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan