Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2021-35042 — Vulnérabilité d'injection SQL dans Django | Kitploit
Outils/GitHubGitHub/luuanhduc/cve-2021-35042
Analyse des VulnérabilitésExploitationExploitation d'Applications WebApprentissage et ÉducationSécurité des Bases de DonnéesLabs et Pratique
GitHubluuanhduc/cve-2021-35042

CVE-2021-35042

Vulnérabilité d'injection SQL dans Django

Voir le dépôt
15il y a 3 ansPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2021-35042: Vulnérabilité d'injection SQL dans Django

I. Aperçu

Django est un framework d'application Web open source, écrit en Python, construit selon le modèle MVC (Model - View - Controller). Il a été initialement conçu pour gérer les sites Web de contenu d'actualités appartenant à la société d'édition Lawrence, un logiciel CMS (Content Management System).

Les versions de Django 3.1.x -> 3.1.13 et 3.2.x -> 3.2.5 présentent une vulnérabilité d'injection SQL.

La cause de cette vulnérabilité est que la fonction de filtrage des données d'entrée contrôlées par l'utilisateur dans QuerySet.order_by() n'est pas suffisante pour se prémunir contre les attaques par injection SQL. Cette vulnérabilité pourrait être exploitée pour permettre à un attaquant d'effectuer des actions non autorisées, entraînant une fuite de données sensibles.

Télécharger l’outil
CVE - IDCVE-2021-35042
Sévérité9.8 - CRITIQUE
CWE - IDCWE-89 : Neutralisation incorrecte d'éléments spéciaux utilisés dans une commande SQL ('Injection SQL')
Date de publication de la vulnérabilité01/07/2021
Logiciels concernés3.1.x < 3.1.13, 3.2.x < 3.2.5
Authentification requiseNon requise

II. Aperçu x2

0x01. Modèle de Django

Dans Django, la création de tables et la définition des champs dans la base de données se font en déclarant une classe de modèle dans le fichier models.py. Dans cet exemple, nous déclarons une table nommée Wolf et un champ nommé name.

0x02. QuerySet et Order_by() dans Django

Le framework ORM intégré à Django est utilisé pour interagir avec la base de données, et le résultat d'une requête est un ensemble, appelé QuerySet.

order_by(fields) Par défaut, order_by() renvoie un QuerySet trié selon un ordre spécifié dans l'option ordering du Meta du modèle. Nous pouvons remplacer la condition order_by dans chaque requête en utilisant la méthode order_by().

Exemple

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

Le résultat de la requête ci-dessus sera trié par ordre décroissant sur le champ name, puis par ordre croissant sur id. Le signe négatif devant le nom du champ name indique un tri décroissant.

L'exemple suivant trie le résultat en fonction du champ reçu de l'utilisateur ; si aucune valeur n'est transmise, il trie par le champ id.

Résultat

Dans les versions 3.1 et 3.2, Django permet de combiner la méthode de requête avec le nom de la table dans la clause order_by. C'est la principale cause de cette vulnérabilité.

Transmettre un nom de table donne le même résultat que transmettre un nom de champ normalement.

cve202135042_wolf est le nom de la table

D'abord, l'application appelle directement la fonction order_by(). Le code gérant la fonction order_by() est défini dans : django/db/models/query.py

La fonction order_by() effectue deux actions :

  1. Supprime tous les tris actuels appelés par order_by() et supprime le paramètre par défaut transmis lorsque order_by reçoit une valeur différente.
  1. Transmet les paramètres à order_by. La fonction add_ordering() exécute cette tâche.
root@kitploit:~
def add_ordering(self, *ordering):
        """
        Ajoute les éléments de la séquence 'ordering' à la clause "order by"
        de la requête. Ces éléments sont soit des noms de champs (pas des noms de colonnes) --
        éventuellement avec un préfixe de direction ('-' ou '?') -- soit des expressions OrderBy.

        Si 'ordering' est vide, supprime tout tri de la requête.
        """
        errors = []
        for item in ordering:
            if isinstance(item, str):
                if '.' in item:
                    warnings.warn(
                        'Passer des alias bruts de colonnes à order_by() est '
                        'obsolète. Enveloppez %r dans une expression RawSQL avant '
                        'de le passer à 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() valide la recherche. Un FieldError descriptif
                # sera levé si ce n'est pas le cas.
                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(
                    'Utiliser une fonction d'agrégation dans order_by() sans l\'inclure '
                    'également dans annotate() n\'est pas autorisé : %s' % item
                )
        if errors:
            raise FieldError('Arguments order_by invalides : %s' % errors)
        if ordering:
            self.order_by += ordering
        else:
            self.default_ordering = False
            

Le paramètre transmis à add_ordering() est un tableau.

Exemple : lorsque le paramètre est transmis comme suit : wolves = Wolf.objects.order_by( 'name' , 'id' ) L'application transforme alors cela en une requête dans la base de données comme suit :

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

Lorsqu'il est transmis, la fonction add_ordering vérifie chaque élément du tableau ; si c'est une string, elle est vérifiée selon 5 cas :

  1. if '.' in item: Vérifie s'il s'agit d'une requête avec un nom de colonne et que cette colonne a un nom de table spécifié dans l'instruction SQL. Si c'est le cas, un avertissement est émis et continue est exécuté.
  2. if item == '?': Si la valeur de l'élément est '?', le résultat de sortie est trié aléatoirement, continue.
  3. if item.startswith('-'): Si l'élément commence par le caractère '-', le résultat de la requête est trié par ordre décroissant (DESC).
  4. if item in self.annotations: Vérifie s'il contient un commentaire ; si oui, continue.
  5. if self.extra and item in self.extra: Détermine s'il y a un supplément et si oui, continue.

Après ces 5 vérifications, le paramètre est ensuite transmis à la fonction self.names_to_path(item.split(LOOKUP_SEP), self.model._meta) pour continuer à vérifier s'il s'agit d'un nom de colonne valide. Ensuite, s'il est valide, il est ajouté à self.ordering de la classe Query pour un traitement ultérieur.

III. Analyse de la vulnérabilité d'injection SQL dans Django

0x31. Cause

L'ORM de Django effectue un filtrage très strict des données insérées dans la requête, mais cette modification du code source conduisant à l'injection SQL est due à l'hypothèse de l'auteur selon laquelle si le nom de colonne est une colonne UUID (Universal Unique Identifier), la requête order_by ne pourrait pas être exécutée.

Cela signifie que si les données transmises sont xxx-xxx-xxx-xxx (format d'un UUID), la requête ne peut pas être exécutée.

Code avant la modification

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

D'après le code ci-dessus, on voit que si le paramètre correspond à ? ou commence par - suivi de caractères ou de points ordinaires, alors la requête est exécutée.

Ainsi, lorsque le nom de colonne est un UUID, c'est une valeur invalide et ne peut pas être utilisée dans order_by. La modification du code de traitement a été acceptée et a été modifiée comme suit : https://github.com/charettes/django/commit/513948735b799239f3ef8c89397592445e1a0cd5

Il a utilisé la fonction self.name_to_path pour valider les données d'entrée.

Mais après vérification, si . est présent dans l'élément, il est considéré comme une requête avec un nom de table ; l'instruction continue est exécutée, ce qui ignore directement l'utilisation de la fonction self.name_to_path pour vérifier la validité des données.

Le code traitant le point . dans la fonction get_order_by est le suivant : 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 fonction self.quote_name_unless_alias traite le nom de la table, filtre les noms de tables valides et ignore le filtrage du nom de colonne, permettant ainsi d'injecter une instruction SQL.

0x32. Correctif (Patch)

Dans la version actuelle de Django 4.0, la requête par nom de table utilisant le point . a été supprimée et n'est plus prise en charge. Des correctifs ont été publiés pour les versions 3.1 et 3.2. Les versions 3.2 -> 3.2.4 et 3.1 -> 3.1.12 sont concernées. 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 modification est très simple : la validation des données par l'ancienne expression régulière a été réintroduite.

0x33. Correction

Mettre à jour Django vers une version non affectée.

IV. Démonstration

0x41. Environnement

Docker & Docker-compose

0x42. Installation

  1. git clone https://github.com/LUUANHDUC/CVE-2021-35042.git
  2. Exécutez ./setup.sh pour l'installation initiale
  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. Accédez à http://localhost:8000/load_example_data pour charger les données d'exemple :
  7. Chemin contenant le paramètre vulnérable : http://localhost:8000/wolves/ http://localhost:8000/wolves/?order_by=name

Écran après installation terminée

0x43. Exploitation

Condition : Pour pouvoir exploiter, nous devons impérativement connaître le nom de la table d'une manière ou d'une autre :))

Lors de l'injection de l'instruction, nous devons connaître le nom de la table pour pouvoir exécuter l'instruction SQLi.

En cas de nom de table incorrect

En cas de nom de table correct, la requête orderby s'exécute normalement.

L'instruction devient alors : SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name") ASC

À ce stade, nous pouvons terminer la clause order_by précédente et insérer une instruction SQL pour l'exploitation.

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

V. Références

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