
Une analyse de base sur CVE-2021-35942. Injection SQL dans Django.
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 - ID | CVE-2021-35042 |
|---|---|
| Sévérité | 9.8 - CRITIQUE |
| CWE - ID | CWE-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és | 3.1.x < 3.1.13, 3.2.x < 3.2.5 |
| Authentification requise | Non requise |
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.


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
- 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.
- Transmet le paramètre à order_by. La fonction
add_ordering()effectue cette opération.
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 :
SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY "cve202135042_wolf"."name" ASC, "cve202135042_wolf"."id" ASC