
Dimostrazione proof-of-concept a scopo educativo dell'iniezione SQL tramite alias dinamici nei metodi annotate() e alias() di Django (CVE-2025-57833). Include codice vulnerabile, esempio di sfruttamento e analisi della correzione.
Un alias è un nome dato a una colonna o a un risultato di aggregazione in una query SQL.
In Django, gli alias vengono utilizzati con i metodi annotate() o alias() per nominare campi calcolati.
Esempio:
from django.db.models import Count
books = Book.objects.annotate(book_count=Count('id'))
Qui, book_count è un alias per il risultato di Count('id').
Ciò consente di ottenere un nome leggibile per una colonna calcolata.
Per andare veloci, si possono inserire i dati tramite la shell:
python manage.py shell
Comandi da eseguire:
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()
Prima di Django 4.2.23, era possibile iniettare SQL tramite alias dinamici forniti dall'utente.
Questa vulnerabilità è nota come CVE-2025-57833.
Colpiva le funzioni annotate() e alias() quando veniva utilizzato un dizionario con espansione (**kwargs) con chiavi non filtrate provenienti dall'utente.
myapp/views.py)import json
from django.db.models import Count
from django.http import JsonResponse
from .models import Author
def vulnerable_view(request):
# On récupère l'alias depuis l'URL
alias_param = request.GET.get("alias", "{}")
try:
# ⚠️ Vulnérable : évaluation directe des données utilisateurs
alias_dict = json.loads(alias_param)
for key, value in alias_dict.items():
alias_dict[key] = eval(value) # dangereux ! permet l'exécution de code arbitraire
# Création de la queryset avec annotation dynamique
qs = Author.objects.annotate(**alias_dict).values("name", *alias_dict.keys())
# Retour des résultats
return JsonResponse(list(qs), safe=False)
except Exception as e:
return JsonResponse({"error": str(e)})
alias_param = request.GET.get("alias", "{}")
Recupera il valore del parametro alias passato dall'utente.
alias_dict = json.loads(alias_param)
Trasforma la stringa JSON in un dizionario Python.
alias_dict[key] = eval(value)
⚠️ Molto pericoloso: eval() esegue la stringa come codice Python.
L'utente potrebbe iniettare SQL distruttivo.
Author.objects.annotate(**alias_dict)
Applica dinamicamente le annotazioni con gli alias forniti.
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
Spiegazione:
Author e Book sono collegati da una relazione ForeignKey.Author per calcolare informazioni sui suoi libri.http://127.0.0.1:8000/vuln/?alias={"evil); DROP TABLE myapp_book;--":"Count('id')"}
Django ora vieta alcuni caratteri negli alias:
--)Se l'URL contiene questi caratteri, Django solleva un errore:
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() su valori provenienti dall'utente.eval() su dati forniti dall'utente.Autore: Loïc
Data: 06/09/2025