Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2021-35042 — Un análisis básico sobre CVE-2021-35942. Inyección SQL en Django. | Kitploit
Herramientas/GitHubGitHub/zer0qs/cve-2021-35042
Análisis de VulnerabilidadesExplotaciónSeguridad WebPapers e InvestigaciónAprendizaje y Educación
GitHubzer0qs/cve-2021-35042

CVE-2021-35042

Un análisis básico sobre CVE-2021-35942. Inyección SQL en Django.

Ver Repositorio
21hace 4 añosAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2021-35042: Vulnerabilidad de inyección SQL en Django

I. Resumen

Django es un framework de aplicaciones web de código abierto, escrito en Python y construido según el modelo MVC (Model - View - Controller). Originalmente fue creado para gestionar sitios web de contenido de noticias propiedad de la corporación editorial Lawrence, como software CMS (Content Management System).

Las versiones de Django 3.1.x -> 3.1.13 y 3.2.x -> 3.2.5 presentan una vulnerabilidad de inyección SQL.

La causa de esta vulnerabilidad es que la función de filtrado de datos de entrada controlados por el usuario en QuerySet.order_by() no es suficiente para prevenir ataques de inyección SQL. Esta vulnerabilidad puede ser explotada para permitir que un atacante realice acciones no autorizadas que conduzcan a la filtración de datos sensibles.

CVE - IDCVE-2021-35042
Severidad9.8 - CRÍTICA
CWE - IDCWE-89: Neutralización incorrecta de elementos especiales utilizados en un comando SQL ('Inyección SQL')
Fecha de publicación de la vulnerabilidad1/7/2021
Software afectado3.1.x < 3.1.13, 3.2.x < 3.2.5
Requiere autenticaciónNo requerida

II. Resumen x2

0x01. Modelo de Django

En Django, la creación de tablas y la definición de campos en la base de datos se realiza declarando una clase de modelo en el archivo models.py. En este ejemplo, declaramos una tabla llamada Wolf y un campo llamado name.

0x02. QuerySet y Order_by() en Django

El framework ORM integrado en Django se utiliza para operar con la base de datos, y el resultado de la consulta es un conjunto, este conjunto es un QuerySet.

order_by(fields) Por defecto, order_by() devuelve un QuerySet ordenado según un orden especificado en la opción ordering dentro del Meta del Modelo. Podemos sobrescribir la condición order_by en cada consulta utilizando el método order_by().

Ejemplo

wolves = Wolf.objects.order_by('-name', 'id')

El resultado de la consulta anterior se ordenará de forma descendente por el campo name, y luego de forma ascendente por id. El signo negativo delante del nombre del campo name indica que el resultado se ordena de forma descendente.

El siguiente ejemplo ordenará el resultado devuelto según el campo recibido del usuario; si no se pasa ningún valor, se ordenará por el campo id.

Resultado

En las versiones 3.1 y 3.2, Django permite combinar el método de consulta con el nombre de la tabla en la consulta order_by. Esta es también la causa principal de esta vulnerabilidad.

Pasar un nombre de tabla nos da el mismo resultado que pasar un nombre de campo normal

cve202135042_wolf es el nombre de la tabla

Primero, la aplicación llama directamente a la función order_by(); el código que procesa la función order_by() está definido en: django/db/models/query.py

La función order_by() realiza dos tareas

  1. Elimina todos los métodos actuales que están siendo llamados por order_by() y elimina el parámetro predeterminado que se pasa cuando order_by recibe un valor diferente.
  1. Pasa el parámetro a order_by. La función add_ordering() realiza esta tarea.
root@kitploit:~
def add_ordering(self, *ordering):
        """
        Add items from the 'ordering' sequence to the query's "order by"
        clause. These items are either field names (not column names) --
        possibly with a direction prefix ('-' or '?') -- or OrderBy
        expressions.

        If 'ordering' is empty, clear all ordering from the query.
        """
        errors = []
        for item in ordering:
            if isinstance(item, str):
                if '.' in item:
                    warnings.warn(
                        'Passing column raw column aliases to order_by() is '
                        'deprecated. Wrap %r in a RawSQL expression before '
                        'passing it to order_by().' % item,
                        category=RemovedInDjango40Warning,
                        stacklevel=3,
                    )
                    continue
                if item == '?':
                    continue
                if item.startswith('-'):
                    item = item[1:]
                if item in self.annotations:
                    continue
                if self.extra and item in self.extra:
                    continue
                # names_to_path() validates the lookup. A descriptive
                # FieldError will be raise if it's not.
                self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
            elif not hasattr(item, 'resolve_expression'):
                errors.append(item)
            if getattr(item, 'contains_aggregate', False):
                raise FieldError(
                    'Using an aggregate in order_by() without also including '
                    'it in annotate() is not allowed: %s' % item
                )
        if errors:
            raise FieldError('Invalid order_by arguments: %s' % errors)
        if ordering:
            self.order_by += ordering
        else:
            self.default_ordering = False
            

El parámetro pasado a add_ordering() es un array.

Por ejemplo, cuando el parámetro se pasa de la siguiente manera: wolves = Wolf.objects.order_by( 'name' , 'id' ) Entonces, la aplicación convertirá esto en la siguiente consulta en la base de datos:

root@kitploit:~
SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY "cve202135042_wolf"."name" ASC, "cve202135042_wolf"."id" ASC

Cuando se pasa, la función add_ordering verificará cada elemento del array; si es un string, se comprobarán los siguientes 5 casos:

  1. if '.' in item: Comprueba si es una consulta con un nombre de columna y si esa columna tiene un nombre de tabla especificado en la sentencia SQL. Si es así, se emite una advertencia y se ejecuta continue.
  2. if item == '?': Si el valor del elemento es el signo '?', el resultado de salida se ordenará aleatoriamente, continue.
  3. if item.startswith('-'): Si el elemento comienza con el carácter '-', el resultado de la consulta se ordenará DESC (descendente).
  4. if item in self.annotations: Comprueba si contiene un comentario; si es así, continue.
  5. if self.extra and item in self.extra: Determina si hay adiciones y, si es así, continue.

Después de las 5 comprobaciones, el parámetro se pasa a la función self.names_to_path(item.split(LOOKUP_SEP), self.model._meta) para continuar verificando si es un nombre de columna válido; luego, si es válido, se añade a self.ordering de la clase Query para su posterior procesamiento.

III. Análisis de la vulnerabilidad de inyección SQL en Django

0x31. Causa

El ORM de Django filtra los datos introducidos en la consulta de forma muy estricta, pero este cambio en el código fuente que conduce a la inyección SQL se debe a que el autor planteó la hipótesis de que si el nombre de la columna es una columna UUID (Identificador Único Universal), la consulta order_by no podría ejecutarse.

Es decir, si los datos introducidos son xxx-xxx-xxx-xxx (formato UUID), la consulta no podría ejecutarse.

Código antes del cambio

root@kitploit:~
# django/db/models/sql/constants.py 
ORDER_PATTERN  =  _lazy_re_compile ( r '\?|[-+]?[.\w]+$' )

# django/db/models/sql/query.py 
def  add_ordering ( self ,  * ordering ): 
        errors  =  [] 
        for  item  in  ordering : 
            if  isinstance ( item ,  str )  and  ORDER_PATTERN . match ( item ): 
                if  '.'  in  item : 
                    warnings . warn ( 
                        'Passing column raw column aliases to order_by() is ' 
                        'deprecated. Wrap %r in a RawSQL expression before '
                        'passing it to order_by().'  %  item , 
                        category = RemovedInDjango40Warning , 
                        stacklevel = 3 , 
                    ) 
            elif  not  hasattr ( item ,  'resolve_expression' ): 
                errors . append ( item ) 
            if  getattr ( item ,  'contains_aggregate' ,  False ): 
                raise  FieldError ( 
                    'Using an aggregate in order_by() without also including ' 
                    'it in annotate() is not allowed: %s ' %  item 
                ) 
        if  errors : 
            raise  FieldError ( 'Invalid order_by arguments: %s '  %  errors ) 
        if  ordering : 
            self . order_by  +=  ordering 
        else : 
            self . default_ordering  =  False

Del código anterior, podemos ver que si el parámetro coincide con ? o comienza con - seguido de caracteres normales o un punto ., la consulta se ejecutará.

Por lo tanto, cuando el nombre de la columna es un UUID, sería un valor no válido y no podría introducirse en order_by. El cambio en el código de procesamiento fue aceptado y se modificó de la siguiente manera: https://github.com/charettes/django/commit/513948735b799239f3ef8c89397592445e1a0cd5

Se utilizó la función self.name_to_path para validar los datos de entrada.

Pero después de comprobar si hay un . en el elemento, se considera una consulta con nombre de tabla, se ejecuta el comando continue, lo que lleva a omitir directamente el uso de la función self.name_to_path para verificar la validez de los datos.

El código que procesa el punto . en la función get_order_by es el siguiente: django/db/models/sql/compiler.py

root@kitploit:~
if  '.'  in  field : 
    table ,  col  =  col . split ( '.' ,  1 ) 
    order_by . append (( 
            OrderBy ( 
                RawSQL ( ' %s . %s '  %  ( 
                self . quote_name_unless_alias ( table ),  col ),  [ ]), 
                descending = descending 
            ),  False )) 
    continue

La función self.quote_name_unless_alias procesa el nombre de la tabla, filtra los nombres de tabla válidos y omite el filtrado del nombre de la columna, por lo que podemos insertar una sentencia de inyección SQL.

0x32. Parche

En la versión actual de Django 4.0, la consulta por nombre de tabla mediante el punto . se ha eliminado y ya no se admite; el parche se publicó para las versiones 3.1 y 3.2. Las versiones 3.2 -> 3.2.4 y 3.1 -> 3.1.12 están afectadas. 3.2.x Fixed CVE-2021-35042 -- Prevented SQL injection in QuerySet.o…

3.1.x Fixed CVE-2021-35042 -- Prevented SQL injection in QuerySet.o…

La modificación es muy sencilla: se restauró la validación de datos con la ReGex anterior.

0x33. Mitigación

Actualizar Django a una versión no afectada.

IV. Demo

0x41. Entorno

Docker & Docker-compose

0x42. Configuración

  1. git clone https://github.com/WynSon/CVE-2021-35042.git
  2. Ejecutar ./setup.sh para la configuración inicial
  3. sudo docker-compose up --build
  4. sudo docker exec -it cve-2021-35042_web_1 python manage.py makemigrations cve202135042
  5. sudo docker exec -it cve-2021-35042_web_1 python manage.py migrate
  6. Acceder a http://localhost:8000/load_example_data para cargar los datos de ejemplo:
  7. La ruta que contiene el parámetro vulnerable: http://localhost:8000/wolves/ http://localhost:8000/wolves/?order_by=name

Pantalla después de completar la instalación

0x43. Explotación

Condición: Para poder explotar la vulnerabilidad, debemos conocer el nombre de la tabla de alguna manera :))

Al inyectar la sentencia, debemos conocer el nombre de la tabla para poder ejecutar la sentencia SQLi.

Cuando se introduce un nombre de tabla incorrecto

Cuando se introduce el nombre de tabla correcto, la consulta orderby se ejecuta normalmente.

La sentencia en este momento será SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name") ASC

En este punto, podemos cerrar la sentencia order_by anterior e insertar una sentencia SQL para explotar la vulnerabilidad.

SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name"); SELECT * from cve202135042_wolf where id =1; --) ASC

V. Referencias

https://www.djangoproject.com/weblog/2021/jul/01/security-releases/ https://xz.aliyun.com/t/9834 https://www.bugxss.com/vulnerability-report/3095.html https://blankheart.top/2022/04/07/cve-2021-35042/ https://itcn.blog/p/1648921763575859.html https://github.com/YouGina/CVE-2021-35042

Descargar herramienta