
Docker-Lab zur Reproduktion der FOSSBilling-Pre-Auth-RCE-Kette (CVE-2026-27604 Auth-Bypass + CVE-2026-28496 Twig SSTI) mit einem Python-PoC und einem gepatchten Vergleichsziel.
Dieses Repository enthält ein lokales Docker-Lab zur Reproduktion und Validierung der FOSSBilling Pre-Auth RCE-Kette, die aus zwei verketteten Schwachstellen besteht:
| CVE | Typ | CVSS v4 | GHSA | Beschreibung |
|---|---|---|---|---|
| CVE-2026-27604 | Auth-Bypass | 10.0 | GHSA-78x5-c8gw-8279 | Fehlendes throw im API-Rollenprüfer legt Admin-Endpunkte für nicht authentifizierte Aufrufer offen |
| CVE-2026-28496 | SSTI | 9.4 | GHSA-57mv-jm88-66jc | Unsandboxed Twig-Template-Rendering über die string_render API |
FOSSBilling ist eine freie und quelloffene Billing- und Client-Management-Plattform. Die Versionen 0.5.4 bis 0.7.2 sind betroffen. FOSSBilling 0.8.0 behebt beide Schwachstellen.
Dieses Lab vergleicht zwei FOSSBilling-Versionen:
| Dienst | FOSSBilling-Version | Zweck | URL |
|---|---|---|---|
| vuln | 0.7.2 | Verwundbares Ziel | http://localhost:8081 |
| patched | 0.8.0 | Gepatchtes Ziel | http://localhost:8082 |
Die validierte Kette in diesem lokalen Lab ist:```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}
Das gepatchte Ziel (0.8.0) gibt zurück:```json
{"result":null,"error":{"message":"Unknown API call system/system/string_render","code":879}}
Das Lab ist bewusst auf lokale Docker-Dienste beschränkt. Es zielt nicht auf externe Systeme ab und enthält keine Web-Shells, Malware, Persistenz, externe Callbacks, Datenbank-Dumps oder destruktive Payloads.
Die Pre-Auth-RCE erfordert das Zusammenspiel beider Schwachstellen:```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 │ └──────────────────────────────────────────────────────────────┘
Dieses Lab demonstriert die Kette unter Verwendung des sicheren arithmetischen Beweises `{{ 7*7 }}`. Der vollständige RCE-Pfad über `getDi()` wird nicht demonstriert.
## Verifizierte Fakten
| Behauptung | Nachweis | Wie zu verifizieren |
| ----- | -------- | ------------- |
| CVE-2026-27604 ist ein Auth-Bypass in der Rollenverarbeitung der FOSSBilling-API. | GHSA-78x5-c8gw-8279: fehlendes throw im Rollenprüfer ermöglicht /api/system/ als Admin aufzulösen. | PoC ausführen: Gast-Pfad wird verweigert, System-Pfad liefert Admin-Ergebnis. |
| CVE-2026-28496 ist eine SSTI im FOSSBilling-Twig-Rendering. | GHSA-57mv-jm88-66jc: string_render übergibt _tpl an Twig createTemplate() ohne Sandbox. | PoC ausführen: Server wertet {{ 7*7 }} aus und liefert 49 zurück. |
| Beide Schwachstellen betreffen FOSSBilling 0.5.4 bis 0.7.2. | Öffentliche Advisories identifizieren den betroffenen Versionsbereich. | Verwundbares (0.7.2) und gepatchtes (0.8.0) Ziel vergleichen. |
| FOSSBilling 0.8.0 patcht beide Schwachstellen. | Gepatchtes Ziel liefert "Unknown API call" für den getesteten Endpunkt. | PoC gegen Port 8082 ausführen. |
| Der Auth-Bypass gewährt unauthentifizierten Admin-Zugriff. | /api/guest/ verweigert string_render; /api/system/ liefert das Ergebnis ohne Authentifizierung. | Stage 1 des PoC ausführen. |
| Die SSTI wertet angreiferkontrollierte Templates aus. | {{ 7*7 }} liefert 49 über den verwundbaren Pfad. | Stage 2 des PoC ausführen. |
| Die Kette ermöglicht Pre-Auth RCE. | Auth-Bypass + SSTI = unauthentifizierte Template-Injection mit Admin-Kontext. | Den vollständigen Ketten-PoC ausführen. |
| Der PoC ist HTTP-only. | poc.py sendet ausschließlich HTTP-POST-Anfragen. | poc/poc.py inspizieren. |
## Root-Cause-Analyse
### CVE-2026-27604: Auth-Bypass
Die FOSSBilling-API löst Rollen aus dem URL-Pfad auf:```text
/api/:role/:module/:method
Der Rollenprüfer validiert, ob die angeforderte Rolle zulässig ist. In verwundbaren Versionen wird die Ausnahme für nicht zulässige Rollen jedoch instanziiert, aber nie ausgelöst:```php // Simplified vulnerable pattern if (!in_array($role, $allowed_roles)) { new \Exception("Role not allowed"); // BUG: missing "throw" }
Da die Ausnahme nie ausgelöst wird, läuft die Validierung stillschweigend durch. Die Rolle `system` wird auf die Cron-Admin-Identität aufgelöst und gewährt jedem nicht authentifizierten Aufrufer vollständigen Admin-API-Zugriff.
Die Sicherheitsauswirkung:```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)
Die system-Rolle wird der internen Cron-Administratoridentität zugeordnet, die über vollständige administrative Berechtigungen verfügt.
Die Admin-API-Methode string_render empfängt _tpl aus den Anfragedaten und leitet es ohne Sandbox-Durchsetzung in die Twig-Template-Rendering-Pipeline weiter:```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);
}
Die Methode `renderString()` fällt auf `createTemplateFromString()` zurück:```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;
}
Das kritische Problem: createTemplate($tpl) erstellt ein Twig-Template aus dem vom Angreifer kontrollierten String und rendert es ohne Sandbox-Einschränkungen. Das Template hat Zugriff auf Objekte im Template-Kontext, einschließlich des Guest-API-Handlers, der getDi() bereitstellt.
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