
Eine grundlegende Analyse zu CVE-2021-35942. SQL-Injection in Django.
Django ist ein Open-Source-Webanwendungs-Framework, das in Python geschrieben und nach dem MVC-Modell (Model - View - Controller) aufgebaut ist. Ursprünglich wurde es entwickelt, um Nachrichteninhalts-Websites der Lawrence-Verlagsgruppe zu verwalten, eine CMS-Software (Content Management System).
In Django Version 3.1.x -> 3.1.13 und Version 3.2.x -> 3.2.5 existiert eine SQL-Injection-Schwachstelle.
Die Ursache für diese Schwachstelle liegt darin, dass die Funktion zur Filterung von benutzergesteuerten Eingabedaten bei QuerySet.order_by() nicht ausreicht, um SQL-Injection-Angriffe zu verhindern. Diese Schwachstelle kann ausgenutzt werden, um einem Angreifer unbefugte Aktionen zu ermöglichen, die zur Offenlegung sensibler Daten führen.
| CVE - ID | CVE-2021-35042 |
|---|---|
| Schweregrad | 9.8 - KRITISCH |
| CWE - ID | CWE-89: Fehlerhafte Neutralisierung spezieller Elemente, die in einem SQL-Befehl verwendet werden ('SQL Injection') |
| Veröffentlichungsdatum der Schwachstelle | 1.7.2021 |
| Betroffene Software | 3.1.x < 3.1.13, 3.2.x < 3.2.5 |
| Authentifizierung erforderlich | Nicht erforderlich |
In Django erfolgt das Erstellen von Tabellen und das Definieren von Feldern in der Datenbank durch die Deklaration einer Model-Klasse in der Datei models.py. In diesem Beispiel deklarieren wir eine Tabelle mit dem Namen Wolf und ein Feld mit dem Namen name.


Das in Django integrierte ORM-Framework wird verwendet, um mit der Datenbank zu arbeiten, und das Ergebnis der Abfrage ist eine Sammlung, diese Sammlung ist ein QuerySet.
order_by(fields)
Standardmäßig gibt order_by() ein QuerySet zurück, das in einer Reihenfolge sortiert ist, die in der Option ordering im Meta des Models festgelegt ist. Wir können die order_by-Bedingung in jeder Abfrage überschreiben, indem wir die Methode order_by() verwenden.
Beispiel
wolves = Wolf.objects.order_by('-name', 'id')
Das Ergebnis der obigen Abfrage wird absteigend nach dem Feld name und dann aufsteigend nach id sortiert. Das Minuszeichen vor dem Feldnamen name zeigt an, dass das Ergebnis absteigend sortiert wird.
Das folgende Beispiel sortiert das zurückgegebene Ergebnis nach dem vom Benutzer empfangenen Feld; wenn kein Wert übergeben wird, wird nach dem Feld
idsortiert.
Ergebnis

In den Versionen 3.1 und 3.2 erlaubt Django die Kombination der Abfragemethode mit dem Tabellennamen in der order_by-Abfrage. Dies ist auch die Hauptursache für diese Schwachstelle.
Das Übergeben eines Tabellennamens liefert uns dasselbe Ergebnis wie das Übergeben eines Feldnamens auf normale Weise
cve202135042_wolf ist der Tabellenname
Zuerst ruft die Anwendung direkt die Funktion order_by() auf; der Code, der die Funktion order_by() verarbeitet, ist definiert unter:
django/db/models/query.py

Die Funktion order_by() führt zwei Aufgaben aus
- Löscht alle aktuellen Methoden, die von order_by() aufgerufen werden, und löscht den Standardparameter, der übergeben wird, wenn order_by einen anderen Wert erhält.
- Übergibt den Parameter an order_by. Die Funktion
add_ordering()führt diese Aufgabe aus.
def add_ordering(self, *ordering):
"""
Fügt Elemente aus der 'ordering'-Sequenz zur "order by"-Klausel
der Abfrage hinzu. Diese Elemente sind entweder Feldnamen (nicht
Spaltennamen) -- möglicherweise mit einem Richtungspräfix ('-' oder
'?') -- oder OrderBy-Ausdrücke.
Wenn 'ordering' leer ist, wird die gesamte Sortierung aus der Abfrage
entfernt.
"""
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() validiert die Suche. Ein beschreibender
# FieldError wird ausgelöst, wenn dies nicht der Fall ist.
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
Der an add_ordering() übergebene Parameter ist ein Array.
Beispiel, wenn der Parameter wie folgt übergeben wird:
wolves = Wolf.objects.order_by( 'name' , 'id' )Dann konvertiert die Anwendung dies in die folgende Datenbankabfrage:
SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY "cve202135042_wolf"."name" ASC, "cve202135042_wolf"."id" ASC