CVE-2025-59681
High · 7.1Django vulnerable to SQL injection in column aliases
CVSS
7.1
EPSS
0.6%
Published
Oct 1, 2025
CISA due date
—
Cloudanix analysis
An older version of a widely used web toolkit can let someone slip extra database commands through certain query features if the app uses MySQL and lets outsiders pick names for those queries, so the toolkit should be updated.
- Exploitation
- Remote Unauthenticated
- Attacker needs
- None
- Complexity
- Moderate
- Impact
- Data Exposure
- Component
- Web Framework
- Class
- SQL Injection
- Remediation
- Upgrade
- Effort
- Low
- Patch risk
- Low
What it actually does
On MySQL and MariaDB backends, Django QuerySet.annotate(), QuerySet.alias(), QuerySet.aggregate(), and QuerySet.extra() did not safely treat column aliases when those aliases were supplied as dictionary keys via **kwargs expansion. A crafted key can leave the alias context and change the generated SQL, so the database executes attacker-influenced statements with the application's DB credentials. Impact is unauthorized read or modification of data the app role can access; it is not a Django-wide RCE by itself and does not affect backends that already quoted aliases safely.
Why it matters to the business
If an application passes untrusted names into those QuerySet APIs while using MySQL or MariaDB, customer or operational records can be stolen or changed, creating breach, integrity, and audit exposure. Django is widely deployed, so unpatched 4.2, 5.1, and 5.2 services remain a material residual risk even though this issue is not in CISA KEV, is not tied to ransomware, has exploit maturity None, and has a low EPSS (~0.006, only a small 30-day rise). The CVE is about a year old, so remaining unpatched versions are an avoidable governance and incident-cost problem rather than a zero-day emergency.
How an attack unfolds
An attacker looks for Django apps still below 4.2.25, 5.1.13, or 5.2.7 that talk to MySQL or MariaDB and that build a dictionary from request or other untrusted input, then expand it into annotate(), alias(), aggregate(), or extra(). Those keys become column aliases; on the affected backends the alias was not fully constrained, so a malicious key can alter query meaning instead of remaining a quoted identifier. The injected SQL runs as the web app's database user, which is typically enough to read or change application data. Reachability depends on that specific coding pattern—apps that only pass developer-controlled aliases are not exploitable. No public exploit code or named threat-actor campaigns are associated with this CVE.
How to fix it
Upgrade Django to 4.2.25, 5.1.13, or 5.2.7 or later on the matching major line (upstream fix 467aeeb569da17a7573e8dfc4932434c84e70642). After upgrade, keep aliases developer-controlled: do not expand untrusted dictionaries into annotate(), alias(), aggregate(), or extra(). If you cannot patch immediately, inventory those call sites, block user-derived keys, and run the app DB user at least privilege (no extra file or admin rights). Treat remaining 4.2/5.1/5.2 releases below those floors as out of date regardless of compensating controls.
How to confirm the fix worked
Confirm the runtime and lockfile Django version is >= 4.2.25, 5.1.13, or 5.2.7. Check DATABASES ENGINE for mysql/mariadb. Search code and templates of QuerySet usage for annotate/alias/aggregate/extra combined with ** expansion of non-constant dicts. After patching, regression-test alias-heavy query paths and confirm they still return expected columns without SQL errors. Re-run SCA/pip-audit/poetry/uv checks until the patched release is the only Django artifact deployed.
Typical attack path
- Identify an unpatched Django 4.2, 5.1, or 5.2 deployment whose ORM uses MySQL or MariaDB.
- Locate a feature that expands a dictionary derived from untrusted input into QuerySet.annotate(), alias(), aggregate(), or extra().
- Supply input that becomes a dictionary key used as a column alias so the ORM emits attacker-influenced SQL.
- Abuse the application's database privileges to read or modify in-scope data.
- Collect results through normal responses or follow-on queries; no public in-the-wild playbook is documented.
How to detect it
There are no public exploits, KEV listings, or named campaigns, so hunt for suspicious use of the vulnerable APIs rather than a known payload pack. Review Django application logs and MySQL/MariaDB error, general, and audit logs around annotate/alias/aggregate/extra for ProgrammingError or syntax failures in the alias position; correlate with requests that send unusual field or key names. Look for sudden wide SELECT shapes, unexpected UNION-style result sets, or mass data reads from the app DB user originating at web workers. Code-deploy and package inventories showing Django 4.2.<25, 5.1.<13, or 5.2.<7 remain the primary exposure signal; absence of matching SQL errors does not prove non-exploitation if query logging was off.
- django.db.utils.ProgrammingError
- django.db.utils.OperationalError
- You have an error in your SQL syntax
- QuerySet.annotate
- QuerySet.alias
- QuerySet.aggregate
- QuerySet.extra
What your security team should do
- Patch Django to 4.2.25, 5.1.13, or 5.2.7 (or newer) on all MySQL/MariaDB-backed apps first; this is the complete fix.
- Inventory QuerySet.annotate/alias/aggregate/extra call sites that expand dictionaries and ensure keys cannot come from request or other untrusted input.
- Confirm production DATABASES backends and prioritize internet-facing services still on vulnerable Django lines, even though EPSS is low and KEV is false.
- Constrain application DB accounts to least privilege and disable verbose SQL debug in production.
- Track dependency lockfiles and CI gates so Django cannot regress below the fixed versions.
The record
An issue was discovered in Django 4.2 before 4.2.25, 5.1 before 5.1.13, and 5.2 before 5.2.7. QuerySet.annotate(), QuerySet.alias(), QuerySet.aggregate(), and QuerySet.extra() are subject to SQL injection in column aliases, when using a suitably crafted dictionary, with dictionary expansion, as the **kwargs passed to these methods (on MySQL and MariaDB).
Affected packages
| Ecosystem | Package | Introduced | Fixed |
|---|---|---|---|
| PyPI | django | 4.2 | 4.2.25 |
| PyPI | django | 5.1 | 5.1.13 |
| PyPI | django | 5.2 | 5.2.7 |
| Bitnami | django | 4.2.0 | 4.2.25 |
| Chainguard | awx | 0 | 24.6.1-r17 |
| Mageia:9 | python-django | 0 | 4.1.13-1.7.mga9 |
| SUSE:Linux Enterprise Module for Package Hub 15 SP6 | python-Django | 0 | 4.2.11-150600.3.33.1 |
| SUSE:Linux Enterprise Module for Package Hub 15 SP7 | python-Django | 0 | 4.2.11-150600.3.33.1 |
| openSUSE:Leap 15.6 | python-Django | 0 | 4.2.11-150600.3.33.1 |
| openSUSE:Leap 16.0 | python-Django | 0 | 5.2.4-bp160.3.1 |
| openSUSE:Tumbleweed | python-Django | 0 | 5.2.7-1.1 |
| openSUSE:Tumbleweed | python-Django4 | 0 | 4.2.25-1.1 |
| openSUSE:Tumbleweed | python-Django6 | 0 | 6.0-1.1 |
| pypi | Django | 4.2 | No fix |
| pypi | Django | 5.1 | No fix |
| pypi | Django | 5.2 | No fix |
Related vulnerabilities
- GHSA-8p8v-wh79-9r56 Django vulnerable to Uncontrolled Resource Consumption Also affects django
- GHSA-mjgh-79qc-68w3 Django has a Race Condition vulnerability Also affects django
- CVE-2026-25673 An issue was discovered in 6.0 before 6.0.3, 5.2 before 5.2.12, and 4.2 before 4.2.29. Also affects django
- CVE-2026-25674 An issue was discovered in 6.0 before 6.0.3, 5.2 before 5.2.12, and 4.2 before 4.2.29. Also affects django
- GHSA-6426-9fv3-65x8 Django has an SQL Injection issue Also affects django
- GHSA-gvg8-93h5-g6qq Django has an SQL Injection issue Also affects django
- GHSA-2mcm-79hx-8fxw Django has Observable Timing Discrepancy Also affects django
- GHSA-33mw-q7rj-mjwj Django has Inefficient Algorithmic Complexity Also affects django