CVE-2021-21337 (published 2021-03-08T21:15Z)

Products.PluggableAuthService is a pluggable Zope authentication and authorization framework. In Products.PluggableAuthService before version 2.6.0 there is an open redirect vulnerability. A maliciously crafted link to the login form and login functionality could redirect the browser to a different website. The problem has been fixed in version 2.6.1. Depending on how you have installed Products.PluggableAuthService, you should change the buildout version pin to `2.6.1` and re-run the buildout, or if you used `pip` simply do `pip install "Products.PluggableAuthService>=2.6.1".

Severity: MEDIUM
CVSS v2 base score: 5.8 (AV:N/AC:M/Au:N/C:P/I:P/A:N)
CVSS v3 base score: 6.1, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N)

Weakness information:
cwe_id: CWE-601
cwe_name: URL Redirection to Untrusted Site ('Open Redirect')
description: A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This simplifies phishing attacks.
extended_description: An http parameter may contain a URL value and could cause the web application to redirect the request to the specified URL. By modifying the URL value to a malicious site, an attacker may successfully launch a phishing scam and steal user credentials. Because the server name in the modified link is identical to the original site, phishing attempts have a more trustworthy appearance. Whether this issue poses a vulnerability will be subject to the intended behavior of the application. For example, a search engine might intentionally provide redirects to arbitrary URLs.
         

Fix information:
repo_url: https://github.com/zopefoundation/Products.PluggableAuthService
commit_details: repo_url: https://github.com/zopefoundation/Products.PluggableAuthService
author: Jens Vagelpohl
author_date: 2021-02-23 12:23:37+01:00
msg: - Fix open redirect issue in  redirect handling
repository: repo_url: https://github.com/zopefoundation/Products.PluggableAuthService
repo_name: zopefoundation/Products.PluggableAuthService
description: Pluggable Zope authentication / authorization framework
repo_language: Python
owner: zopefoundation
file_changes: filename: CHANGES.rst
change_type: ModificationType.MODIFY
diff: @@ -4,6 +4,8 @@ Change Log
 2.6.0 (unreleased)
 ------------------
 
+- Fix open redirect issue in `Cookie Auth Helper` redirect handling
+
 - Add support for Python 3.9.
 
 

programming_language: Markdown
filename: CookieAuthHelper.py
change_type: ModificationType.MODIFY
diff: @@ -28,6 +28,8 @@ from binascii import hexlify
 import six
 from six.moves.urllib.parse import quote
 from six.moves.urllib.parse import unquote
+from six.moves.urllib.parse import urlparse
+from six.moves.urllib.parse import urlunparse
 
 from AccessControl.class_init import InitializeClass
 from AccessControl.Permissions import view
@@ -225,6 +227,12 @@ class CookieAuthHelper(Folder, BasePlugin):
                     # in an endless redirect loop.
                     return 0
 
+            # Sanitize the return URL ``came_from`` and only allow local URLs
+            # to prevent an open exploitable redirect issue
+            if came_from:
+                parsed = urlparse(came_from)
+                came_from = urlunparse(('', '') + parsed[2:])
+
             if '?' in url:
                 sep = '&'
             else:

programming_language: Python
filename: test_CookieAuthHelper.py
change_type: ModificationType.MODIFY
diff: @@ -128,7 +128,8 @@ class CookieAuthHelperTests(unittest.TestCase,
     def test_challenge(self):
         rc, root, folder, object = self._makeTree()
         response = FauxCookieResponse()
-        testURL = 'http://test'
+        testPath = '/some/path'
+        testURL = 'http://test' + testPath
         request = FauxRequest(RESPONSE=response, URL=testURL,
                               ACTUAL_URL=testURL)
         root.REQUEST = request
@@ -138,7 +139,9 @@ class CookieAuthHelperTests(unittest.TestCase,
         helper.challenge(request, response)
         self.assertEqual(response.status, 302)
         self.assertEqual(len(response.headers), 3)
-        self.assertTrue(response.headers['Location'].endswith(quote(testURL)))
+        loc = response.headers['Location']
+        self.assertTrue(loc.endswith(quote(testPath)))
+        self.assertNotIn(testURL, loc)
         self.assertEqual(response.headers['Cache-Control'], 'no-cache')
         self.assertEqual(response.headers['Expires'],
                          'Sat, 01 Jan 2000 00:00:00 GMT')
@@ -159,8 +162,9 @@ class CookieAuthHelperTests(unittest.TestCase,
         self.assertEqual(response.status, 302)
         self.assertEqual(len(response.headers), 3)
         loc = response.headers['Location']
-        self.assertTrue(loc.endswith(quote(actualURL)))
+        self.assertTrue(loc.endswith(quote('/xxx')))
         self.assertFalse(loc.endswith(quote(vhm)))
+        self.assertNotIn(actualURL, loc)
         self.assertEqual(response.headers['Cache-Control'], 'no-cache')
         self.assertEqual(response.headers['Expires'],
                          'Sat, 01 Jan 2000 00:00:00 GMT')

programming_language: Python