CVE-2025-13372 Overview
CVE-2025-13372 is a SQL injection vulnerability in the Django web framework affecting the FilteredRelation query construct. Attackers can inject SQL through column aliases when a suitably crafted dictionary is expanded as **kwargs into QuerySet.annotate() or QuerySet.alias() on PostgreSQL backends. The flaw is classified under CWE-89 (Improper Neutralization of Special Elements used in an SQL Command). Django maintainers credit Stackered for reporting the issue and released fixes on December 2, 2025.
Critical Impact
Applications that pass user-controlled dictionary keys into QuerySet.annotate() or QuerySet.alias() with FilteredRelation on PostgreSQL can leak database contents through injected SQL fragments in column aliases.
Affected Products
- Django 5.2 before 5.2.9
- Django 5.1 before 5.1.15
- Django 4.2 before 4.2.27
- Unsupported series (5.0.x, 4.1.x, 3.2.x) may also be affected but were not evaluated
Discovery Timeline
- 2025-12-02 - Django releases security fixes and publishes advisory, crediting Stackered for reporting the issue
- 2025-12-02 - CVE-2025-13372 published to NVD
- 2026-06-17 - Last updated in NVD database
Technical Details for CVE-2025-13372
Vulnerability Analysis
The vulnerability resides in Django's ORM handling of FilteredRelation, an API used to create conditional joins in querysets. When a developer expands a dictionary as keyword arguments into QuerySet.annotate(**kwargs) or QuerySet.alias(**kwargs), the dictionary keys become column aliases in the generated SQL. Django did not adequately neutralize these alias identifiers before embedding them into the PostgreSQL query text.
An attacker who controls the keys of that dictionary can inject arbitrary SQL fragments into the alias position of a SELECT clause. Because the injection point sits inside the query rendered against PostgreSQL, the resulting payload executes with the privileges of the application database user. Impact is limited to confidentiality of database data based on the CVSS scoring, and exploitation requires user interaction, typically a request that triggers the vulnerable code path.
Root Cause
The root cause is missing identifier quoting and validation on FilteredRelation column aliases prior to SQL generation. Django's ORM treats developer-supplied kwargs as trusted identifiers, but applications frequently forward client-supplied names (for example, sort keys, filter names, or dynamic field selectors) directly into annotate() or alias() without sanitization.
Attack Vector
Exploitation requires an application that dynamically builds FilteredRelation annotations from external input on a PostgreSQL database. A typical pattern is a request handler that accepts a JSON body or query parameters and expands them into QuerySet.annotate(**request_data). The attacker supplies a key containing SQL metacharacters, which Django then emits verbatim into the alias portion of the generated statement. See the Django Weblog security release notes for the vendor description of the attack path.
Detection Methods for CVE-2025-13372
Indicators of Compromise
- PostgreSQL query logs containing unusual SQL tokens (quotes, comments, UNION, ;) inside column alias positions of SELECT statements originating from Django worker processes
- Application error logs showing django.db.utils.ProgrammingError or PostgreSQL syntax errors near FilteredRelation usage
- HTTP request payloads with JSON keys or query parameter names containing SQL metacharacters targeting endpoints backed by annotate() or alias()
Detection Strategies
- Audit source code for calls to QuerySet.annotate(**data) or QuerySet.alias(**data) where data originates from request input, and flag any that combine with FilteredRelation
- Enable PostgreSQL statement logging (log_statement = 'all') in staging environments and grep for anomalous alias syntax
- Deploy web application firewall rules that block request parameter names containing quotes, semicolons, or SQL keywords
Monitoring Recommendations
- Monitor application logs for spikes in ORM-generated query failures on endpoints that expose dynamic annotations
- Track Django version inventory across production services and alert when instances fall below 5.2.9, 5.1.15, or 4.2.27
- Correlate WAF alerts with database error rates to identify probing attempts against FilteredRelation code paths
How to Mitigate CVE-2025-13372
Immediate Actions Required
- Upgrade Django to 5.2.9, 5.1.15, or 4.2.27 depending on the deployed series
- Audit all uses of QuerySet.annotate() and QuerySet.alias() and confirm that dictionary keys are not derived from untrusted input
- Rotate database credentials if PostgreSQL logs show evidence of injected alias payloads
Patch Information
The Django project released patched versions 5.2.9, 5.1.15, and 4.2.27 on December 2, 2025. Details are available in the Django Security Release Documentation and the Django Weblog security announcement. Users on unsupported series (5.0.x, 4.1.x, 3.2.x) should migrate to a supported release, as those branches were not evaluated but may share the defect.
Workarounds
- Validate and allowlist dictionary keys before passing them to annotate() or alias(), restricting them to a fixed set of identifier characters (^[A-Za-z_][A-Za-z0-9_]*$)
- Avoid dictionary unpacking of untrusted data into ORM methods; construct keyword arguments explicitly from vetted values
- Apply least-privilege database roles so that the Django application user cannot read tables outside its functional scope
# Upgrade Django to a patched release matching your series
pip install --upgrade 'Django>=5.2.9,<5.3' # 5.2.x series
pip install --upgrade 'Django>=5.1.15,<5.2' # 5.1.x series
pip install --upgrade 'Django>=4.2.27,<4.3' # 4.2.x LTS series
# Verify installed version
python -c "import django; print(django.get_version())"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

