CVE-2012-4520 (Python, published 2012-11-18T23:55Z)
Description: value: The django.http.HttpRequest.get_host function in Django 1.3.x before 1.3.4 and 1.4.x before 1.4.2 allows remote attackers to generate and display arbitrary URLs via crafted username and password Host header values.
Severity: MEDIUM (CVSS v3 base score: )
Weakness: CWE-20 \u2014 Improper Input Validation
The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Repository: https://github.com/django/django
Files changed:
django/contrib/auth/tests/urls.py
django/contrib/auth/tests/views.py
django/contrib/auth/views.py
django/http/__init__.py
Commit message: Fixed a security issue related to password resets

Full disclosure and new release are forthcoming

Vulnerable code:
                opts = dict(opts, domain_override=request.META['HTTP_HOST'])

Fixed code:
    (r'^admin_password_reset/$', 'django.contrib.auth.views.password_reset', dict(is_admin_site=True)),
from django.core.exceptions import SuspiciousOperation
    def test_admin_reset(self):
        "If the reset view is marked as being for admin, the HTTP_HOST header is used for a domain override."
        response = self.client.post('/admin_password_reset/',
            {'email': 'staffmember@example.com'},
            HTTP_HOST='adminsite.com'
        )
        self.assertEqual(response.status_code, 302)
        self.assertEqual(len(mail.outbox), 1)
        self.assertTrue("http://adminsite.com" in mail.outbox[0].body)
        self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email)

    def test_poisoned_http_host(self):
        "Poisoned HTTP_HOST headers can't be used for reset emails"
        # This attack is based on the way browsers handle URLs. The colon
        # should be used to separate the port, but if the URL contains an @,
        # the colon is interpreted as part of a username for login purposes,
        # making 'evil.com' the request domain. Since HTTP_HOST is used to
        # produce a meaningful reset URL, we need to be certain that the
        # HTTP_HOST header isn't poisoned. This is done as a check when get_host()
        # is invoked, but we check here as a practical consequence.
        with self.assertRaises(SuspiciousOperation):
            self.client.post('/password_reset/',
                {'email': 'staffmember@example.com'},
                HTTP_HOST='www.example:dr.frankenstein@evil.tld'
            )
        self.assertEqual(len(mail.outbox), 0)

    def test_poisoned_http_host_admin_site(self):
        "Poisoned HTTP_HOST headers can't be used for reset emails on admin views"
        with self.assertRaises(SuspiciousOperation):
            self.client.post('/admin_password_reset/',
                {'email': 'staffmember@example.com'},
                HTTP_HOST='www.example:dr.frankenstein@evil.tld'
            )
        self.assertEqual(len(mail.outbox), 0)

                opts = dict(opts, domain_override=request.get_host())

        # Disallow potentially poisoned hostnames.
        if set(';/?@&=+$,').intersection(host):
            raise SuspiciousOperation('Invalid HTTP_HOST header: %s' % host)