
تحليل تقني لثغرة أمنية وإثبات مفهوم لـ CVE-2026-47630، وهي استدعاء dlopen تعسفي عبر TRITON_BATCH_STRATEGY_PATH في خادم NVIDIA Triton للاستدلال، مما يتيح تنفيذ كود أصلي، مع إرشادات للكشف والتخفيف.
dlopen عشوائي عبر TRITON_BATCH_STRATEGY_PATHثغرة اجتياز المسار المطلق في محمّل استراتيجية التجميع المخصص داخل
triton-inference-server/core. تُمرَّر معلمة من معلمات إعداد النموذج دون أي تحقق إلى
dlopen(RTLD_NOW | RTLD_LOCAL)، لذا فإن أي شخص قادر على التأثير في
config.pbtxt لنموذج ما يحصل على تنفيذ كود أصلي داخل عملية خادم Triton
وقت تحميل النموذج.
إقرار NVIDIA في النشرة 5865 نصّه الحرفي: CVE-2026-47630: s1ko.
يقبل Triton معلمة TRITON_BATCH_STRATEGY_PATH لكل نموذج من
config.pbtxt الخاص بالنموذج. وحتى الإصدار 26.05 كانت القيمة تُعامل كمسار
نظام ملفات عشوائي وتُمرَّر دون تعديل إلى dlopen. لم يكن هناك أي فحص احتواء،
ولا قائمة سماح، ولا تحقق من التوقيع أو التجزئة. كانت المسارات المطلقة
وتسلسلات الاجتياز والروابط الرمزية جميعها مقبولة.
يحلّل RTLD_NOW كل رمز فورًا وينفّذ إجراءات
__attribute__((constructor)) / .init_array الخاصة بالكائن قبل أن يعود
dlopen. لذلك يكون التنفيذ غير مشروط بنجاح التحميل — لا حاجة لوجود أي من
نقاط الدخول TRITONBACKEND_ModelBatch* لكي تعمل الحمولة، وهي تعمل بصلاحيات
عملية Triton (غالبًا root في حاويات NGC).
src/backend_model.cc — يصل إدخال المهاجم إلى batch_libpath، ولا يُتحقق منه إلا للتأكد من وجوده:
if (model_config.parameters().contains("TRITON_BATCH_STRATEGY_PATH")) {
batch_libpath = model_config.parameters()
.at("TRITON_BATCH_STRATEGY_PATH")
.string_value();
bool exists = false;
RETURN_IF_ERROR(FileExists(batch_libpath, &exists));
if (!exists) {
return Status(
triton::common::Error::Code::NOT_FOUND,
("Batching library path not found: " + batch_libpath).c_str());
}
}
واللافت للانتباه غياب أي استدعاء لـ IsChildPathEscapingParentPath، الذي طبّقه Triton بالفعل على مسارات التسميات ومسارات مكتبات الواجهة الخلفية في أماكن أخرى من الملف نفسه.
src/backend_model.cc — تمرّر SetBatchingStrategy القيمة:
Status TritonModel::SetBatchingStrategy(const std::string& batch_libpath)
{
std::unique_ptr<SharedLibrary> slib;
RETURN_IF_ERROR(SharedLibrary::Acquire(&slib));
RETURN_IF_ERROR(slib->OpenLibraryHandle(batch_libpath, &batch_dlhandle_));
src/shared_library.cc — المصب (sink):
*handle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
ثلاثة مسارات عملية للتحكم في model_config.parameters():
config.pbtxt لنموذجه زرع ملف .so في دليله الخاص وتوجيه المعلمة إليه. تعمل الحمولة بصلاحيات الخادم، مما يتيح الوصول إلى أوزان وأسرار وGPU الخاصة بكل مستأجر آخر.TRITONSERVER_ServerLoadModelWithParameters مع بادئة المعاملات الموثقة file:<rel-path> للمتصل الذي يملك صلاحيات تحميل النماذج إرسال config.pbtxt و.so في طلب واحد وتشغيل التحميل فورًا. لا حاجة لأي وصول إلى نظام الملفات خارج الواجهة.dlopen عند الطلب.يحتوي poc/ على بدائية الاستغلال (primitive) المختزلة إلى ما يفعله OpenLibraryHandle. ملفات مصدر فقط — ابنِها بنفسك:
$ cd poc && ./build.sh
$ gcc -O0 -o test_dlopen test_dlopen.c -ldl
$ ./test_dlopen ./evil.so
dlopen OK, handle=0x55d1779652c0
$ cat /tmp/triton_dlopen_rce_proof.log
[triton-dlopen-rce] constructor fired @ Fri May 1 09:56:56 2026
pid=240157 uid=1000 euid=1000 cwd=/opt/poc
الحمولة خاملة: تضيف سطرًا واحدًا يسجّل pid وuid وeuid وcwd. تم التحقق على Debian 13 مع GCC 14.2 وبنية x86_64.
للتشغيل من جهة Triton، ضع ملف evil.so المبني بجوار poc/config.pbtxt في مستودع النماذج ثم حمّل النموذج — عبر التحميل التلقائي، أو --load-model، أو POST /v2/repository/models/evil_model/load. في الإصدارات المتأثرة، يعمل المُنشئ أثناء SetBatchingStrategy، قبل أي بحث عن رموز الواجهة الخلفية.
يسجّل Triton نفسه التحميل بمستوى INFO:
Loading custom batching strategy library <path> for model <name>
في بناء مُصلَح، تظهر محاولة مرفوضة كرسالة
Batching library path escapes model repository.
أي قيمة TRITON_BATCH_STRATEGY_PATH تكون مطلقة، أو تحتوي على ..، أو
تشير إلى خارج دليل النموذج تستحق إطلاق تنبيه بشأنها بغض النظر عن الإصدار.
توجد قاعدة Sigma تغطي سطر السجل ومعلمة الإعداد معًا في detection/.
إشارات مكمّلة:
config.pbtxt التي تحمل معلمة TRITON_BATCH_STRATEGY_PATH من الأساس — الميزة نادرة عمليًا، لذا فإن وجودها وحده يُعد مرشحًا مفيدًا.openat/mmap لملف .so خارج جذر النموذج من قِبل عملية tritonserver (عبر auditd أو eBPF أو Falco).قم بالترقية إلى Triton Inference Server 26.06 أو أحدث. يضيف الإصلاح فحص الاحتواء الذي كان مفقودًا في المحمّل:
bool escapes{true};
RETURN_IF_ERROR(IsChildPathEscapingParentPath(
batch_libpath, localized_model_dir->Path(), &escapes));
if (escapes) {
return Status(
Status::Code::INVALID_ARG,
"Batching library path escapes model repository.");
}
حيثما لا يمكن الترقية فورًا، ضوابط تعويضية:
TRITON_BATCH_STRATEGY_PATH من كل config.pbtxt قبل وصوله إلى مستودع النماذج.--model-control-mode على قيمة غير explicit، أو فرض تفويضًا على نقاط نهاية المستودع) ما لم يكن مطلوبًا.tritonserver كمستخدم غير مميز مع تركيب مستودع النماذج للقراءة فقط، بحيث يكون نصف قطر الانفجار أصغر ما يمكن عند نجاح التحميل.الربط: MITRE ATT&CK T1574.006 اختطاف تدفق التنفيذ: اختطاف الرابط الديناميكي وT1129 الوحدات المشتركة؛ NIST SP 800-53r5 SI-7, CM-5, SC-18؛ CIS Controls v8 §2, §4.
triton-inference-server/core — https://github.com/triton-inference-server/coreMIT — انظر LICENSE.
| CVE | CVE-2026-47630 |
| نشرة NVIDIA | 5865 (2026-08-18) |
| CWE | CWE-36 (اجتياز المسار المطلق)؛ أُبلغ عنها أيضًا كـ CWE-114 / CWE-829 |
| CVSS v3.1 (NVIDIA) | 5.5 متوسط — AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| المتأثر | Triton Inference Server 0.0 – 26.05 (لينكس) |
| أُصلح في | 26.06 |
| أبلغ به | s1ko (github.com/s1ko، [email protected]) |
| تتبع البائع | تذكرة NVIDIA PSIRT 6139742 |
| التاريخ | الحدث |
|---|
| 2026-04-25 | الإبلاغ عن الاكتشاف الأساسي المتعلق بمعالجة المسارات في Triton؛ تفتح NVIDIA PSIRT التذكرة 6128799 |
| 2026-05-01 | تأكيد هذا الاكتشاف الثانوي والإبلاغ عنه؛ تفتح PSIRT التذكرة 6139742 |
| 2026-05-28 | الاتفاق على إيقاع الكشف المنسق مع PSIRT |
| 2026-06 | يصدر الإصلاح في Triton Inference Server 26.06 |
| 2026-08-18 | تنشر NVIDIA النشرة 5865، وتخصص CVE-2026-47630 وتنسب الفضل إلى s1ko |
| 2026-08-22 | نشر هذا التقرير |