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 — Une analyse de base sur CVE-2021-35942. Injection SQL dans Django. | Kitploit
Outils/GitHubGitHub/zer0qs/cve-2021-35042
Analyse des VulnérabilitésExploitationSécurité WebArticles et RechercheApprentissage et Éducation
GitHubzer0qs/cve-2021-35042

CVE-2021-35042

Une analyse de base sur CVE-2021-35942. Injection SQL dans Django.

Voir le dépôt
2il y a 4 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 général

Django est un framework de développement d'applications web open source, écrit en Python et 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 au groupe de presse Lawrence, en tant que 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é peut être exploitée pour permettre à un attaquant d'effectuer des actions non autorisées, conduisant à la fuite de données sensibles.

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 général x2

0x01. Le modèle de Django

Dans Django, la création de tables et la définition des champs dans la base de données s'effectuent 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 selon le champ name, puis par ordre croissant selon id. Le signe négatif devant le nom du champ name indique que le résultat est trié par ordre décroissant.

L'exemple suivant trie le résultat renvoyé selon le champ reçu de l'utilisateur ; si aucune valeur n'est transmise, le tri s'effectue selon 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 requête order_by. C'est également la cause principale de cette vulnérabilité.

Le fait de transmettre un nom de table nous donne le même résultat que la transmission d'un nom de champ normalement

cve202135042_wolf est le nom de la table

Tout d'abord, l'application appelle directement la fonction order_by() ; le code traitant la fonction order_by() est défini à : django/db/models/query.py

La fonction order_by() effectue deux opérations

  1. Supprime toutes les méthodes actuellement appelées par order_by() et supprime le paramètre par défaut transmis lorsque order_by reçoit une valeur différente.
  1. Transmet le paramètre à order_by. La fonction add_ordering() effectue cette opération.
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
            

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

Par exemple, lorsque le paramètre est transmis comme suit : wolves = Wolf.objects.order_by( 'name' , 'id' ) L'application convertit 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 ; s'il s'agit d'une string, elle est vérifiée selon les 5 cas suivants :

  1. if '.' in item: Elle vérifie s'il s'agit d'une requête avec un nom de colonne et si cette colonne possède un nom de table spécifié dans la commande 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 signe '?', le résultat de sortie est trié aléatoirement, puis 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: Elle vérifie s'il contient un commentaire ; si c'est le cas, continue.
  5. if self.extra and item in self.extra: Détermine s'il y a des ajouts supplémentaires et, si c'est le cas, 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 filtre très strictement les données insérées dans la requête, mais cette modification du code source conduisant à l'injection SQL est due au fait que l'auteur a émis l'hypothèse que 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 au format xxx-xxx-xxx-xxx (format 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

À partir du code ci-dessus, nous pouvons voir que si le paramètre correspond à ? ou commence par - suivi de caractères ou de points . ordinaires, la requête est alors exécutée.

Par conséquent, lorsque le nom de colonne est un UUID, il s'agit d'une valeur invalide qui ne peut pas être insérée dans order_by. La modification du code de ce traitement a été acceptée et modifiée comme suit : https://github.com/charettes/django/commit/513948735b799239f3ef8c89397592445e1a0cd5

Elle utilise 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 ; la commande continue est exécutée, ce qui conduit à ignorer 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 ; nous pouvons donc insérer une commande d'injection SQL.

0x32. Correctif

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 ; le correctif a été publié 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 vérification des données par l'ancienne ReGex a été rétablie.

0x33. Remédiation

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

IV. Démonstration

0x41. Environnement

Docker & Docker-compose

0x42. Installation

  1. git clone https://github.com/WynSon/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. Le chemin contenant le paramètre vulnérable : http://localhost:8000/wolves/ http://localhost:8000/wolves/?order_by=name

Écran après l'installation terminée

0x43. Exploitation

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

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

Lorsque le nom de la table est saisi incorrectement

Lorsque le nom de la table est correct, la requête orderby s'exécute normalement.

La commande devient alors SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name") ASC

À ce stade, nous pouvons terminer la commande order_by précédente et insérer une commande SQL pour exploiter la vulnérabilité.

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

Télécharger l’outil