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

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

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

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

دليل الأدوات

الفئات

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

CVE-2021-35042

تحليل أساسي حول CVE-2021-35942. حقن SQL في Django.

عرض المستودع
2منذ 4 سنواتلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2021-35042: ثغرة حقن SQL في Django

أولا: نظرة عامة

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

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

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

CVE - IDCVE-2021-35042
الخطورة9.8 - حرجة
CWE - IDCWE-89: تحييد غير صحيح للعناصر الخاصة المستخدمة في أمر SQL ('حقن SQL')
تاريخ نشر الثغرة1/7/2021
البرامج المتأثرة3.1.x < 3.1.13, 3.2.x < 3.2.5
يتطلب مصادقةغير مطلوب

ثانيا: نظرة عامة x2

0x01. نموذج Django

في Django، يتم إنشاء الجداول وتحديد الحقول في قاعدة البيانات عن طريق تعريف فئة نموذج في ملف 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: يتحقق مما إذا كان يحتوي على تعليق توضيحي أم لا، وإذا كان كذلك، فسيتم continue.
  5. if self.extra and item in self.extra: يحدد ما إذا كانت هناك إضافات، وإذا كان كذلك، فسيتم continue.

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

ثالثا: تحليل ثغرة حقن SQL في Django

0x31. السبب

يقوم ORM الخاص بـ Django بتصفية البيانات المُدخلة في الاستعلام بشكل صارم للغاية، لكن التغيير في الكود المصدري هذه المرة الذي أدى إلى حقن SQL هو أن المؤلف افترض أنه إذا كان اسم العمود هو عمود UUID (المعرف الفريد العام)، فلن يمكن تنفيذ استعلام 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.

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 إلى إصدار غير متأثر.

رابعا: العرض التوضيحي

0x41. البيئة

Docker & Docker-compose

0x42. الإعداد

  1. git clone https://github.com/WynSon/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

خامسا: المراجع

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

تنزيل الأداة