Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2021-35042 — ثغرة حقن SQL في Django | Kitploit
أدوات/GitHubGitHub/luuanhduc/cve-2021-35042
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويبالتعلم والتعليمأمن قواعد البياناتمختبرات وتدريب عملي
GitHubluuanhduc/cve-2021-35042

CVE-2021-35042

ثغرة حقن SQL في Django

عرض المستودع
15منذ 3 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2021-35042: Django SQL injection vulnerability

I. نظرة عامة

Django هو إطار تطبيق ويب مفتوح المصدر، مكتوب بلغة Python، مبني على نموذج MVC (Model - View - Controller). تم بناؤه في البداية لإدارة مواقع الويب لمحتوى الأخبار المملوكة لمجموعة النشر Lawrence، وهو نظام إدارة محتوى (CMS).

توجد ثغرة SQL injection في Django الإصدارات 3.1.x -> 3.1.13 والإصدارات 3.2.x -> 3.2.5.

سبب هذه الثغرة هو أن وظيفة تصفية البيانات المدخلة التي يتحكم فيها المستخدم في QuerySet.order_by() غير كافية لمنع هجمات SQL injection. يمكن استغلال هذه الثغرة للسماح للمهاجم بتنفيذ إجراءات غير مصرح بها تؤدي إلى تسريب بيانات حساسة.

CVE - IDCVE-2021-35042
Severity9.8 - CRITICAL
CWE - IDCWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
تاريخ نشر الثغرة1/7/2021
البرامج المتأثرة3.1.x < 3.1.13, 3.2.x < 3.2.5
مطلوب مصادقةغير مطلوب

II. نظرة عامة (2)

0x01. نموذج Django

في Django، يتم إنشاء الجداول وتعريف الحقول في قاعدة البيانات عن طريق تعريف فئة نموذج (model) في ملف models.py. في هذا المثال، نعلن عن جدول باسم Wolf وحقل باسم name.

0x02. QuerySet و Order_by() في Django

يُستخدم إطار عمل ORM المدمج في Django للتعامل مع قاعدة البيانات، ونتيجة الاستعلام هي مجموعة تسمى QuerySet.

order_by(fields) افتراضيًا، ترجع order_by() QuerySet مرتبة حسب الترتيب المحدد في خيار ordering في Meta الخاص بالنموذج. يمكننا تجاوز شرط order_by في كل استعلام باستخدام الطريقة order_by().

مثال

wolves = Wolf.objects.order_by('-name', 'id')

سيتم ترتيب نتيجة الاستعلام أعلاه تنازليًا حسب الحقل name، ثم تصاعديًا حسب id. تشير علامة السالب قبل اسم الحقل name إلى ترتيب النتيجة تنازليًا.

المثال التالي سيرتب النتيجة حسب الحقل المستلم من المستخدم، وإذا لم يتم تمرير أي قيمة فسيتم الترتيب حسب الحقل id.

النتيجة

في الإصدارين 3.1 و 3.2، يسمح Django بدمج طريقة الاستعلام مع اسم الجدول في استعلام order_by. وهذا هو السبب الرئيسي لهذه الثغرة.

تمرير اسم جدول يعطينا نفس النتيجة كتمرير اسم الحقل العادي

cve202135042_wolf هو اسم الجدول

أولاً، يستدعي التطبيق مباشرة دالة order_by()، ويتم تعريف الكود الذي يعالج دالة order_by() في: django/db/models/query.py

تقوم دالة order_by() بأمرين

  1. تحذف جميع الترتيبات الحالية التي يتم استدعاؤها بواسطة order_by() وتحذف المعامل الافتراضي الذي يمرر عندما يستقبل order_by قيمة مختلفة.
  1. تمرر المعاملات إلى order_by. تقوم الدالة add_ordering() بهذا الأمر.
root@kitploit:~
def add_ordering(self, *ordering):
        """
        Add items from the 'ordering' sequence to the query's "order by"
        clause. These items are either field names (not column names) --
        possibly with a direction prefix ('-' or '?') -- or OrderBy
        expressions.

        If 'ordering' is empty, clear all ordering from the query.
        """
        errors = []
        for item in ordering:
            if isinstance(item, str):
                if '.' in item:
                    warnings.warn(
                        'Passing column raw column aliases to order_by() is '
                        'deprecated. Wrap %r in a RawSQL expression before '
                        'passing it to order_by().' % item,
                        category=RemovedInDjango40Warning,
                        stacklevel=3,
                    )
                    continue
                if item == '?':
                    continue
                if item.startswith('-'):
                    item = item[1:]
                if item in self.annotations:
                    continue
                if self.extra and item in self.extra:
                    continue
                # names_to_path() validates the lookup. A descriptive
                # FieldError will be raise if it's not.
                self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
            elif not hasattr(item, 'resolve_expression'):
                errors.append(item)
            if getattr(item, 'contains_aggregate', False):
                raise FieldError(
                    'Using an aggregate in order_by() without also including '
                    'it in annotate() is not allowed: %s' % item
                )
        if errors:
            raise FieldError('Invalid order_by arguments: %s' % errors)
        if ordering:
            self.order_by += ordering
        else:
            self.default_ordering = False
            

المعامل الذي يُمرر إلى add_ordering() هو مصفوفة.

مثال عندما يُمرر المعامل كالتالي: wolves = Wolf.objects.order_by( 'name' , 'id' ) عندها، سيقوم التطبيق بتحويله إلى استعلام في قاعدة البيانات كالتالي:

root@kitploit:~
SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY "cve202135042_wolf"."name" ASC, "cve202135042_wolf"."id" ASC

عند تمرير القيم، ستقوم دالة add_ordering بفحص كل عنصر في المصفوفة، إذا كان string، فسيتم فحصه في 5 حالات:

  1. if '.' in item: تتحقق مما إذا كان استعلامًا يحتوي على اسم عمود مع اسم جدول محدد في جملة SQL. إذا كان كذلك، فسيصدر تحذيرًا ويستمر continue.
  2. if item == '?': إذا كانت قيمة العنصر هي علامة '?'، فسيتم ترتيب النتيجة عشوائيًا، continue.
  3. if item.startswith('-'): إذا بدأ العنصر بالحرف '-', فسيتم ترتيب نتيجة الاستعلام تنازليًا (DESC).
  4. if item in self.annotations: يتحقق مما إذا كان يحتوي على تعليق (comment)، إذا كان كذلك، continue.
  5. if self.extra and item in self.extra: يحدد ما إذا كان هناك إضافات (extra) وإذا كان كذلك، continue.

بعد 5 فحوصات، يستمر تمرير المعامل إلى الدالة self.names_to_path(item.split(LOOKUP_SEP), self.model._meta) للتحقق مما إذا كان اسم عمود صالحًا، ثم إذا كان صالحًا، سيتم إضافته إلى self.ordering في الفئة Query لمعالجته لاحقًا.

III. تحليل ثغرة SQL injection في Django

0x31. السبب

يقوم ORM الخاص بـ Django بتصفية البيانات المدخلة في الاستعلام بشكل صارم، لكن التغيير في الكود المصدري هذه المرة أدى إلى SQL injection بسبب افتراض المؤلف أن اسم العمود إذا كان UUID (Universal Unique Identifier) فلن يتمكن استعلام order_by من التنفيذ.

بمعنى أنه إذا كانت البيانات المدخلة هي xxx-xxx-xxx-xxx (تنسيق UUID)، فلن يتمكن الاستعلام من التنفيذ.

الكود قبل التغيير

root@kitploit:~
# django/db/models/sql/constants.py 
ORDER_PATTERN  =  _lazy_re_compile ( r '\?|[-+]?[.\w]+$' )

# django/db/models/sql/query.py 
def  add_ordering ( self ,  * ordering ): 
        errors  =  [] 
        for  item  in  ordering : 
            if  isinstance ( item ,  str )  and  ORDER_PATTERN . match ( item ): 
                if  '.'  in  item : 
                    warnings . warn ( 
                        'Passing column raw column aliases to order_by() is ' 
                        'deprecated. Wrap %r in a RawSQL expression before '
                        'passing it to order_by().'  %  item , 
                        category = RemovedInDjango40Warning , 
                        stacklevel = 3 , 
                    ) 
            elif  not  hasattr ( item ,  'resolve_expression' ): 
                errors . append ( item ) 
            if  getattr ( item ,  'contains_aggregate' ,  False ): 
                raise  FieldError ( 
                    'Using an aggregate in order_by() without also including ' 
                    'it in annotate() is not allowed: %s ' %  item 
                ) 
        if  errors : 
            raise  FieldError ( 'Invalid order_by arguments: %s '  %  errors ) 
        if  ordering : 
            self . order_by  +=  ordering 
        else : 
            self . default_ordering  =  False

من الكود أعلاه، يمكننا أن نرى أنه إذا تطابق المعامل مع ? أو بدأ بـ - يليه أحرف عادية أو نقاط .، فإن الاستعلام سيتم تنفيذه فقط.

لذلك، عندما يكون اسم العمود UUID، ستكون قيمة غير صالحة ولا يمكن إدخالها في order_by. تم قبول تغيير الكود لهذه المعالجة، وتغير كالتالي: https://github.com/charettes/django/commit/513948735b799239f3ef8c89397592445e1a0cd5

استخدمت الدالة self.name_to_path للتحقق من صحة البيانات المدخلة.

لكن بعد فحص ما إذا كان . موجودًا في العنصر، سيعتبره استعلامًا مع اسم جدول، ويتم تنفيذ الأمر continue، مما يؤدي إلى تخطي استخدام الدالة self.name_to_path للتحقق من صحة البيانات.

الكود الذي يعالج النقطة . في الدالة get_order_by هو كما يلي django/db/models/sql/compiler.py

root@kitploit:~
if  '.'  in  field : 
    table ,  col  =  col . split ( '.' ,  1 ) 
    order_by . append (( 
            OrderBy ( 
                RawSQL ( ' %s . %s '  %  ( 
                self . quote_name_unless_alias ( table ),  col ),  [ ]), 
                descending = descending 
            ),  False )) 
    continue

تقوم الدالة self.quote_name_unless_alias بمعالجة اسم الجدول، وتصفية أسماء الجداول الصالحة وتتجاهل تصفية اسم العمود، لذلك يمكننا حقن جملة SQL injection.

0x32. التصحيح

في إصدار Django 4.0 الحالي، تمت إزالة الاستعلام حسب اسم الجدول باستخدام النقطة . ولم يعد مدعومًا، وتم إصدار التصحيح للإصدارين 3.1 و 3.2. الإصدارات المتأثرة هي 3.2 -> 3.2.4 و 3.1 -> 3.1.12. 3.2.x Fixed CVE-2021-35042 -- Prevented SQL injection in QuerySet.o…

3.1.x Fixed CVE-2021-35042 -- Prevented SQL injection in QuerySet.o…

التعديل بسيط للغاية، فقد تم إعادة فحص البيانات باستخدام ReGex القديم.

0x33. الإصلاح

قم بتحديث Django إلى إصدار غير متأثر.

IV. العرض التوضيحي

0x41. البيئة

Docker & Docker-compose

0x42. الإعداد

  1. git clone https://github.com/LUUANHDUC/CVE-2021-35042.git
  2. قم بتشغيل ./setup.sh للإعداد المبدئي
  3. sudo docker-compose up --build
  4. sudo docker exec -it cve-2021-35042_web_1 python manage.py makemigrations cve202135042
  5. sudo docker exec -it cve-2021-35042_web_1 python manage.py migrate
  6. قم بزيارة http://localhost:8000/load_example_data لتحميل بيانات مثال:
  7. المسار الذي يحتوي على المعامل الضعيف: http://localhost:8000/wolves/ http://localhost:8000/wolves/?order_by=name

الشاشة بعد التثبيت

0x43. الاستغلال

الشرط: للاستغلال، يجب علينا معرفة اسم الجدول بطريقة ما :))

عند حقن الأمر، يجب أن نعرف اسم الجدول لتتمكن من تنفيذ جملة SQLi.

عند إدخال اسم جدول خاطئ

عند إدخال اسم جدول صحيح، يعمل استعلام orderby بشكل طبيعي.

سيكون الأمر في هذه الحالة SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name") ASC

في هذه اللحظة، يمكننا إنهاء أمر order_by السابق وحقن جملة SQL للاستغلال.

SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY ("cve202135042_wolf"."name"); SELECT * from cve202135042_wolf where id =1; --) ASC

V. المراجع

https://www.djangoproject.com/weblog/2021/jul/01/security-releases/ https://xz.aliyun.com/t/9834 https://www.bugxss.com/vulnerability-report/3095.html https://blankheart.top/2022/04/07/cve-2021-35042/ https://itcn.blog/p/1648921763575859.html https://github.com/YouGina/CVE-2021-35042

تنزيل الأداة