Cloudanix VulnDB

CVE-2025-13372

Medium · 4.3

Django is vulnerable to SQL injection in column aliases

CVSS

4.3

EPSS

0.9%

Published

Dec 2, 2025

CISA due date

—

Cloudanix analysis

Next patch cycle SSVC: Track

A bug in a popular web framework can let someone tamper with database queries on PostgreSQL when an app unsafely names certain query shortcuts, which may expose or change stored data.

Exploitation
Remote Authenticated
Attacker needs
Low Privilege
Complexity
Moderate
Impact
Data Exposure
Component
Web Framework
Class
SQL Injection
Remediation
Upgrade
Effort
Low
Patch risk
None

What it actually does

On PostgreSQL, Django FilteredRelation mishandles column aliases when a crafted dictionary is expanded as **kwargs into QuerySet.annotate() or QuerySet.alias(). Alias names can be interpolated into generated SQL, so attacker-controlled identifiers become part of the query executed as the application database role. Impact is SQL injection in that ORM path only (not RCE by itself) and requires the unsafe annotate/alias pattern on a vulnerable Django release.

Why it matters to the business

If an app feeds untrusted input into FilteredRelation annotate/alias kwargs, attackers may read or change data the database user can access, harming confidentiality and integrity. Django is widely used, so any remaining 5.2 < 5.2.9, 5.1 < 5.1.15, or 4.2 < 4.2.27 (and unevaluated 5.0.x, 4.1.x, 3.2.x) is in scope. Likelihood is low: CVSS 4.3, EPSS ~0.009 and flat over 30 days, not in CISA KEV, no ransomware use, exploit maturity none. Public for ~300 days, so unpatched production is lingering patch debt rather than a new crisis.

How an attack unfolds

An attacker finds a Django+PostgreSQL app that builds FilteredRelation and calls annotate() or alias() with a dictionary expanded as keyword arguments, where keys (aliases) can be influenced by the client. They submit alias names that break out of the intended identifier context. Django interpolates those aliases into SQL sent to PostgreSQL without adequate handling for this construction. PostgreSQL parses the injected fragments as part of the query, changing semantics (extra clauses, broader results, or other SQL) under the app's DB role. This needs a reachable unsafe coding pattern; no named threat actors or KEV campaigns are associated.

How to fix it

Upgrade Django to 5.2.9, 5.1.15, or 4.2.27 (or newer supported). Upstream fix: git 6ef1f6fbe33e9882f82dae026b527800ecb78c36. Move unsupported 5.0.x, 4.1.x, and 3.2.x onto a patched supported line. Never pass request/API/untrusted dicts as **kwargs to annotate() or alias(), especially with FilteredRelation; allowlist dynamic annotation names. Redeploy all workers and rebuild images so old Django packages are gone.

How to confirm the fix worked

In each runtime, confirm django.get_version() is at least 5.2.9, 5.1.15, or 4.2.27. Search code for FilteredRelation used with annotate( or alias( and ** expansion. Check requirements.txt, poetry.lock, Pipfile.lock, and container images for django below those versions. In staging, exercise FilteredRelation annotation paths and confirm aliases are treated as identifiers (unexpected alias characters rejected, not interpolated). Do not use production data or live exploit tests.

Typical attack path

  1. Locate a Django app on PostgreSQL that uses FilteredRelation with QuerySet.annotate() or QuerySet.alias()
  2. Confirm alias names come from a dictionary expanded as **kwargs and can be influenced by the client
  3. Submit crafted alias keys intended to break out of the column-alias context
  4. Cause the ORM to include those aliases in SQL sent to PostgreSQL
  5. Influence query results or other SQL under the application's database role

How to detect it

No in-the-wild exploitation or named actors are reported. Hunt whether it was already abused by combining code exposure with data-store logs: find FilteredRelation used with annotate()/alias() and user-influenced kwargs; pull PostgreSQL logs (csvlog/stderr/pgaudit) around those endpoints for syntax errors near SELECT aliases or unexpected JOIN/ON SQL; review Django/app logs for ProgrammingError/DataError on annotate/alias; check DB audit for anomalous SELECTs from the app role. Correlate web request IDs with DB timestamps. Lack of those errors does not prove absence of successful (non-erroring) injection.

  • django.db.utils.ProgrammingError
  • django.db.utils.DataError
  • psycopg2.errors.SyntaxError
  • psycopg.errors.SyntaxError
  • FilteredRelation
  • QuerySet.annotate
  • QuerySet.alias
  • ERROR: syntax error at or near

What your security team should do

  • Inventory Django+PostgreSQL apps and lockfiles; treat 5.2<5.2.9, 5.1<5.1.15, 4.2<4.2.27 and unsupported 5.0/4.1/3.2 as exposed
  • Patch to 5.2.9, 5.1.15, or 4.2.27 (or later) and redeploy so no process still loads old Django
  • Code-review FilteredRelation + annotate()/alias(**kwargs); stop passing untrusted dict keys as aliases
  • After patching, review PostgreSQL and app error logs for historical alias-related SQL syntax failures
  • Keep standard SQL-injection detections; this CVE is not KEV-listed and has no public exploit maturity, so prioritize by exposure of the unsafe ORM pattern

The record

An issue was discovered in 5.2 before 5.2.9, 5.1 before 5.1.15, and 4.2 before 4.2.27. `FilteredRelation` is subject to SQL injection in column aliases, using a suitably crafted dictionary, with dictionary expansion, as the `**kwargs` passed to `QuerySet.annotate()` or `QuerySet.alias()` on PostgreSQL. Earlier, unsupported Django series (such as 5.0.x, 4.1.x, and 3.2.x) were not evaluated and may also be affected. Django would like to thank Stackered for reporting this issue.

Affected packages

Ecosystem Package Introduced Fixed
PyPI django 4.2a1 4.2.27
PyPI django 5.1a1 5.1.15
PyPI django 5.2a1 5.2.9
Bitnami django 4.2.0 4.2.27
Chainguard authentik 0 2025.10.2-r2
Chainguard authentik-go-server 0 2025.10.2-r2
Chainguard awx 0 24.6.1-r20
Mageia:9 python-django 0 4.1.13-1.9.mga9
SUSE:Linux Enterprise Module for Package Hub 15 SP6 python-Django 0 4.2.11-150600.3.44.1
SUSE:Linux Enterprise Module for Package Hub 15 SP7 python-Django 0 4.2.11-150600.3.44.1
openSUSE:Leap 15.6 python-Django 0 4.2.11-150600.3.44.1
openSUSE:Leap 16.0 python-Django 0 5.2.4-bp160.4.1
openSUSE:Tumbleweed python-Django 0 5.2.9-1.1
openSUSE:Tumbleweed python-Django4 0 4.2.27-1.1
openSUSE:Tumbleweed python-Django6 0 6.0-1.1
pypi Django 4.2a1 No fix
pypi Django 5.1a1 No fix
pypi Django 5.2a1 No fix

Related vulnerabilities