Hi security team,

> python-django: CVE-2018-14574: Open redirect possibility in CommonMiddleware

I've attached the following diff for a proposed 1:1.10.7-2+deb9u2
update for Django:

  Source: python-django
  Version: 1:1.10.7-2+deb9u2
  Distribution: stretch-security
  Urgency: high
  Maintainer: Chris Lamb <la...@debian.org>
  Timestamp: 1533177448
  Date: Thu, 02 Aug 2018 10:37:28 +0800
  Closes: 905216
  Changes:
   python-django (1:1.10.7-2+deb9u2) stretch-security; urgency=high
   .
     * Non-maintainer upload by the Security Team.
     * CVE-2018-14574: Fix an open redirect possibility in CommonMiddleware.
       If the django.middleware.common.CommonMiddleware and the APPEND_SLASH
       setting were both enabled, and if the project has a URL pattern that
       accepted any path ending in a slash then a request to a maliciously 
crafted
       URL of that site could lead to a redirect to another site, enabling
       phishing and other attacks. (Closes: #905216)

       
Let me know if I should go ahead and upload.


Regards,

-- 
      ,''`.
     : :'  :     Chris Lamb
     `. `'`      la...@debian.org / chris-lamb.co.uk
       `-
diff --git a/debian/changelog b/debian/changelog
index 472d500fb..ace826d6c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,15 @@
+python-django (1:1.10.7-2+deb9u2) stretch-security; urgency=high
+
+  * Non-maintainer upload by the Security Team.
+  * CVE-2018-14574: Fix an open redirect possibility in CommonMiddleware.
+    If the django.middleware.common.CommonMiddleware and the APPEND_SLASH
+    setting were both enabled, and if the project has a URL pattern that
+    accepted any path ending in a slash then a request to a maliciously crafted
+    URL of that site could lead to a redirect to another site, enabling
+    phishing and other attacks. (Closes: #905216)
+
+ -- Chris Lamb <la...@debian.org>  Thu, 02 Aug 2018 10:37:28 +0800
+
 python-django (1:1.10.7-2+deb9u1) stretch-security; urgency=high
 
   * Non-maintainer upload by the LTS Team.
diff --git a/debian/patches/0015-CVE-2018-14574.patch 
b/debian/patches/0015-CVE-2018-14574.patch
new file mode 100644
index 000000000..c8bf439e9
--- /dev/null
+++ b/debian/patches/0015-CVE-2018-14574.patch
@@ -0,0 +1,153 @@
+From: Chris Lamb <la...@debian.org>
+Date: Thu, 2 Aug 2018 10:28:56 +0800
+Subject: CVE-2018-14574
+
+Open redirect possibility in CommonMiddleware
+
+If the django.middleware.common.CommonMiddleware and the APPEND_SLASH setting
+are both enabled, and if the project has a URL pattern that accepts any path
+ending in a slash (many content management systems have such a pattern), then a
+request to a maliciously crafted URL of that site could lead to a redirect to
+another site, enabling phishing and other attacks.
+
+Thanks Andreas Hug for reporting this issue.
+
+ -- <https://www.djangoproject.com/weblog/2018/aug/01/security-releases/>
+
+Backported by Chris Lamb <la...@debian.org> from:
+
+  
https://github.com/django/django/commit/d6eaee092709aad477a9894598496c6deec532ff
+---
+ django/middleware/common.py    |  3 +++
+ django/urls/resolvers.py       |  8 ++++----
+ django/utils/http.py           | 11 +++++++++++
+ tests/middleware/tests.py      | 19 +++++++++++++++++++
+ tests/middleware/urls.py       |  2 ++
+ tests/utils_tests/test_http.py | 10 ++++++++++
+ 6 files changed, 49 insertions(+), 4 deletions(-)
+
+diff --git a/django/middleware/common.py b/django/middleware/common.py
+index 4cec6f0..4ac5e01 100644
+--- a/django/middleware/common.py
++++ b/django/middleware/common.py
+@@ -9,6 +9,7 @@ from django.urls import is_valid_path
+ from django.utils.cache import get_conditional_response, set_response_etag
+ from django.utils.deprecation import MiddlewareMixin
+ from django.utils.encoding import force_text
++from django.utils.http import escape_leading_slashes
+ from django.utils.http import unquote_etag
+ from django.utils.six.moves.urllib.parse import urlparse
+ 
+@@ -90,6 +91,8 @@ class CommonMiddleware(MiddlewareMixin):
+         POST, PUT, or PATCH.
+         """
+         new_path = request.get_full_path(force_append_slash=True)
++        # Prevent construction of scheme relative urls.
++        new_path = escape_leading_slashes(new_path)
+         if settings.DEBUG and request.method in ('POST', 'PUT', 'PATCH'):
+             raise RuntimeError(
+                 "You called this URL via %(method)s, but the URL doesn't end "
+diff --git a/django/urls/resolvers.py b/django/urls/resolvers.py
+index cec960d..da82d56 100644
+--- a/django/urls/resolvers.py
++++ b/django/urls/resolvers.py
+@@ -18,7 +18,9 @@ from django.utils import lru_cache, six
+ from django.utils.datastructures import MultiValueDict
+ from django.utils.encoding import force_str, force_text
+ from django.utils.functional import cached_property
+-from django.utils.http import RFC3986_SUBDELIMS, urlquote
++from django.utils.http import (
++    RFC3986_SUBDELIMS, escape_leading_slashes, urlquote,
++)
+ from django.utils.regex_helper import normalize
+ from django.utils.translation import get_language
+ 
+@@ -373,9 +375,7 @@ class RegexURLResolver(LocaleRegexProvider):
+                     # safe characters from `pchar` definition of RFC 3986
+                     url = urlquote(candidate_pat % candidate_subs, 
safe=RFC3986_SUBDELIMS + str('/~:@'))
+                     # Don't allow construction of scheme relative urls.
+-                    if url.startswith('//'):
+-                        url = '/%%2F%s' % url[2:]
+-                    return url
++                    return escape_leading_slashes(url)
+         # lookup_view can be URL name or callable, but callables are not
+         # friendly in error messages.
+         m = getattr(lookup_view, '__module__', None)
+diff --git a/django/utils/http.py b/django/utils/http.py
+index 812ddb2..3898331 100644
+--- a/django/utils/http.py
++++ b/django/utils/http.py
+@@ -437,3 +437,14 @@ def limited_parse_qsl(qs, keep_blank_values=False, 
encoding='utf-8',
+                 value = unquote(nv[1].replace(b'+', b' '))
+             r.append((name, value))
+     return r
++
++
++def escape_leading_slashes(url):
++    """
++    If redirecting to an absolute path (two leading slashes), a slash must be
++    escaped to prevent browsers from handling the path as schemaless and
++    redirecting to another host.
++    """
++    if url.startswith('//'):
++        url = '/%2F{}'.format(url[2:])
++    return url
+diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py
+index f87bb9d..0120529 100644
+--- a/tests/middleware/tests.py
++++ b/tests/middleware/tests.py
+@@ -122,6 +122,25 @@ class CommonMiddlewareTest(SimpleTestCase):
+         self.assertEqual(r.status_code, 301)
+         self.assertEqual(r.url, '/needsquoting%23/')
+ 
++    @override_settings(APPEND_SLASH=True)
++    def test_append_slash_leading_slashes(self):
++        """
++        Paths starting with two slashes are escaped to prevent open redirects.
++        If there's a URL pattern that allows paths to start with two slashes, 
a
++        request with path //evil.com must not redirect to //evil.com/ 
(appended
++        slash) which is a schemaless absolute URL. The browser would navigate
++        to evil.com/.
++        """
++        # Use 4 slashes because of RequestFactory behavior.
++        request = self.rf.get('////evil.com/security')
++        response = HttpResponseNotFound()
++        r = CommonMiddleware().process_request(request)
++        self.assertEqual(r.status_code, 301)
++        self.assertEqual(r.url, '/%2Fevil.com/security/')
++        r = CommonMiddleware().process_response(request, response)
++        self.assertEqual(r.status_code, 301)
++        self.assertEqual(r.url, '/%2Fevil.com/security/')
++
+     @override_settings(APPEND_SLASH=False, PREPEND_WWW=True)
+     def test_prepend_www(self):
+         request = self.rf.get('/path/')
+diff --git a/tests/middleware/urls.py b/tests/middleware/urls.py
+index 8c6621d..d623e7d 100644
+--- a/tests/middleware/urls.py
++++ b/tests/middleware/urls.py
+@@ -6,4 +6,6 @@ urlpatterns = [
+     url(r'^noslash$', views.empty_view),
+     url(r'^slash/$', views.empty_view),
+     url(r'^needsquoting#/$', views.empty_view),
++    # Accepts paths with two leading slashes.
++    url(r'^(.+)/security/$', views.empty_view),
+ ]
+diff --git a/tests/utils_tests/test_http.py b/tests/utils_tests/test_http.py
+index efe6b9a..ed4a099 100644
+--- a/tests/utils_tests/test_http.py
++++ b/tests/utils_tests/test_http.py
+@@ -211,3 +211,13 @@ class HttpDateProcessingTests(unittest.TestCase):
+     def test_parsing_asctime(self):
+         parsed = http.parse_http_date('Sun Nov  6 08:49:37 1994')
+         self.assertEqual(datetime.utcfromtimestamp(parsed), datetime(1994, 
11, 6, 8, 49, 37))
++
++
++class EscapeLeadingSlashesTests(unittest.TestCase):
++    def test(self):
++        tests = (
++            ('//example.com', '/%2Fexample.com'),
++            ('//', '/%2F'),
++        )
++        for url, expected in tests:
++            self.assertEqual(http.escape_leading_slashes(url), expected)
diff --git a/debian/patches/series b/debian/patches/series
index 13daeb8c4..ac9c311cd 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -5,3 +5,4 @@ fix-migration-fake-initial-2.patch
 fix-test-middleware-classes-headers.patch
 0013-CVE-2018-7536.patch
 0014-CVE-2018-7537.patch
+0015-CVE-2018-14574.patch

Reply via email to