
مختبر Docker يعيد إنتاج سلسلة RCE قبل المصادقة في FOSSBilling (تجاوز المصادقة CVE-2026-27604 + Twig SSTI CVE-2026-28496) مع PoC بلغة Python وهدف مقارنة مُرقَّع.
يحتوي هذا المستودع على مختبر Docker محلي لإعادة إنتاج والتحقق من سلسلة RCE قبل المصادقة في FOSSBilling، والمكوّنة من ثغرتين متسلسلتين:
| CVE | النوع | CVSS v4 | GHSA | الوصف |
|---|---|---|---|---|
| CVE-2026-27604 | تجاوز المصادقة | 10.0 | GHSA-78x5-c8gw-8279 | غياب throw في مدقّق أدوار API يعرّض نقاط نهاية المسؤول للمتصلين غير المصادق عليهم |
| CVE-2026-28496 | SSTI | 9.4 | GHSA-57mv-jm88-66jc | عرض قوالب Twig بدون sandbox عبر واجهة string_render API |
FOSSBilling هي منصة مجانية ومفتوحة المصدر للفواتير وإدارة العملاء. الإصدارات من 0.5.4 حتى 0.7.2 متأثرة. الإصدار FOSSBilling 0.8.0 يعالج كلا الثغرتين.
يقارن هذا المختبر بين إصدارين من FOSSBilling:
| الخدمة | إصدار FOSSBilling | الغرض | الرابط |
|---|---|---|---|
| vuln | 0.7.2 | الهدف المصاب | http://localhost:8081 |
| patched | 0.8.0 | الهدف المُرقّع | http://localhost:8082 |
السلسلة المُتحقق منها في هذا المختبر المحلي هي:```text Unauthenticated HTTP POST → /api/system/system/string_render → Role "system" resolves to cron admin identity (CVE-2026-27604) → _tpl={{ 7*7 }} passed into unsandboxed Twig rendering (CVE-2026-28496) → Server evaluates the template expression → Returns {"result":"49","error":null}
الهدف المُرقَّع (0.8.0) يُرجع:```json
{"result":null,"error":{"message":"Unknown API call system/system/string_render","code":879}}
المختبر مُحدَّد النطاق عمدًا لخدمات Docker المحلية. لا يستهدف أنظمة خارجية ولا يتضمن web shells أو برمجيات خبيثة أو استمرارية أو callbacks خارجية أو تفريغ قواعد البيانات أو payloads مدمّرة.
يتطلب Pre-Auth RCE كلا الثغرتين للعمل معًا:```text ┌──────────────────────────────────────────────────────────────┐ │ STEP 1: Auth Bypass (CVE-2026-27604) │ │ │ │ URL path: /api/system/system/string_render │ │ Role "system" → cron admin identity │ │ Exception instantiated but never thrown │ │ → Unauthenticated caller gets admin API access │ ├──────────────────────────────────────────────────────────────┤ │ STEP 2: SSTI (CVE-2026-28496) │ │ │ │ Admin API method: System\Api\Admin::string_render() │ │ _tpl parameter → Twig createTemplate() → render() │ │ No sandbox enforcement │ │ → Server-side template evaluation │ ├──────────────────────────────────────────────────────────────┤ │ COMBINED: Pre-Auth RCE │ │ │ │ One unauthenticated HTTP POST │ │ → Admin access (auth bypass) │ │ → Template injection (SSTI) │ │ → getDi() exposes Pimple DI container │ │ → PDO, cache, extension manager, 40+ services reachable │ │ → Remote Code Execution │ └──────────────────────────────────────────────────────────────┘
يوضح هذا المختبر السلسلة باستخدام إثبات حسابي آمن `{{ 7*7 }}`. لا يتم توضيح مسار RCE الكامل عبر `getDi()`.
## حقائق موثّقة
| الادعاء | الدليل | كيفية التحقق |
| ----- | -------- | ------------- |
| CVE-2026-27604 هو تجاوز للمصادقة في معالجة أدوار FOSSBilling API. | GHSA-78x5-c8gw-8279: غياب throw في مدقق الأدوار يسمح لـ /api/system/ بالتحلل كمسؤول. | شغّل PoC: مسار الضيف مرفوض، ومسار النظام يعيد نتيجة المسؤول. |
| CVE-2026-28496 هو SSTI في عرض Twig في FOSSBilling. | GHSA-57mv-jm88-66jc: string_render يمرر _tpl إلى Twig createTemplate() بدون sandbox. | شغّل PoC: يقيّم الخادم {{ 7*7 }} ويعيد 49. |
| تؤثر كلتا الثغرتين على FOSSBilling 0.5.4 حتى 0.7.2. | تحدد الاستشارات العامة نطاق الإصدارات المتأثرة. | قارن بين الأهداف المصابة (0.7.2) والمرقّعة (0.8.0). |
| FOSSBilling 0.8.0 يرقّع كلتا الثغرتين. | الهدف المرقّع يعيد "Unknown API call" لنقطة النهاية المختبرة. | شغّل PoC مقابل المنفذ 8082. |
| تجاوز المصادقة يمنح وصولاً إدارياً بدون مصادقة. | /api/guest/ يرفض string_render؛ /api/system/ يعيد النتيجة بدون مصادقة. | شغّل المرحلة 1 من PoC. |
| يقيّم SSTI قوالب يتحكم بها المهاجم. | {{ 7*7 }} يعيد 49 عبر المسار المصاب. | شغّل المرحلة 2 من PoC. |
| تتيح السلسلة Pre-Auth RCE. | تجاوز المصادقة + SSTI = حقن قوالب بدون مصادقة بسياق المسؤول. | شغّل PoC للسلسلة الكاملة. |
| PoC يعمل عبر HTTP فقط. | poc.py يرسل طلبات HTTP POST فقط. | افحص poc/poc.py. |
## تحليل السبب الجذري
### CVE-2026-27604: تجاوز المصادقة
يستخرج FOSSBilling API الأدوار من مسار URL:```text
/api/:role/:module/:method
يتحقق مدقق الأدوار مما إذا كان الدور المطلوب مسموحًا به. ومع ذلك، في الإصدارات المعرضة للثغرات، يتم إنشاء الاستثناء الخاص بالأدوار غير المسموح بها ولكن لا يتم طرحه أبدًا:```php // Simplified vulnerable pattern if (!in_array($role, $allowed_roles)) { new \Exception("Role not allowed"); // BUG: missing "throw" }
لأن الاستثناء لا يتم إطلاقه أبدًا، تمر عملية التحقق بصمت. يُحلّ الدور `system` إلى هوية مسؤول cron، مما يمنح وصولاً كاملاً إلى واجهة برمجة التطبيقات الإدارية لأي متصل غير مُصادَق عليه.
التأثير الأمني:```text
/api/guest/system/string_render → denied (guest role, no admin access)
/api/admin/system/string_render → requires authentication
/api/system/system/string_render → admin access WITHOUT authentication (bypass)
يُعيّن دور system إلى هوية مسؤول cron الداخلية، التي تمتلك صلاحيات إدارية كاملة.
تتلقى طريقة واجهة برمجة التطبيقات الإدارية string_render قيمة _tpl من بيانات الطلب وتمرّرها إلى خط أنابيب عرض قوالب Twig دون فرض بيئة معزولة:```php
public function string_render($data)
{
if (!isset($data['_tpl'])) {
error_log('_tpl parameter not passed');
return '';
}
$tpl = $data['_tpl'];
$try_render = $data['_try'] ?? false;
$vars = $data;
unset($vars['_tpl'], $vars['_try']);
return $this->getService()->renderString($tpl, $try_render, $vars);
}
تنتقل طريقة `renderString()` إلى `createTemplateFromString()`:```php
public function createTemplateFromString($tpl, $try_render, $vars)
{
try {
$twig = $this->di['twig'];
$template = $twig->createTemplate($tpl);
$parsed = $template->render($vars);
} catch (\Exception $e) {
$parsed = $tpl;
if (!$try_render) {
throw $e;
}
}
return $parsed;
}
المشكلة الحرجة: createTemplate($tpl) ينشئ قالب Twig من السلسلة التي يتحكم بها المهاجم ويعرضه دون قيود sandbox. يمتلك القالب إمكانية الوصول إلى الكائنات في سياق القالب، بما في ذلك معالج واجهة برمجة تطبيقات الضيف الذي يعرض getDi().
Input: POST /api/system/system/string_render {"_tpl":"{{ 7*7 }}"}
Step 1 (CVE-2026-27604): URL path → role = "system" → role checker: exception instantiated, NOT thrown → system role → cron admin identity → admin API access granted without authentication
Step 2 (CVE-2026-28496): Admin::string_render() → reads _tpl from request → Service::renderString() → createTemplateFromString() → Twig createTemplate("{{ 7*7 }}") → Twig evaluates the expression → returns "49"
Full RCE path (not demonstrated in this safe PoC): {{ guest.getDi() }} → returns the Pimple DI container → PDO, cache, password hashing, extension manager, 40+ services → SQL execution, credential extraction, code execution
## ملخص التصحيح المصدر
يعالج FOSSBilling 0.8.0 كلاً من الثغرتين:
**إصلاح تجاوز المصادقة (CVE-2026-27604):**
يقوم مدقق الأدوار الآن برمي الاستثناء بشكل صحيح للأدوار غير المسموح بها.