
FOSSBillingの認証前RCEチェーン(CVE-2026-27604認証バイパス + CVE-2026-28496 Twig SSTI)を再現するDockerラボ。Python PoCとパッチ適用済みの比較対象を含む。
このリポジトリには、FOSSBilling の認証前 RCE チェーンを再現および検証するためのローカル Docker ラボが含まれています。このチェーンは、2 つの連鎖する脆弱性で構成されています:
| CVE | 種類 | CVSS v4 | GHSA | 説明 |
|---|---|---|---|---|
| CVE-2026-27604 | 認証バイパス | 10.0 | GHSA-78x5-c8gw-8279 | API ロールチェッカーに throw が欠落しているため、管理者エンドポイントが未認証の呼び出し元に公開される |
| CVE-2026-28496 | SSTI | 9.4 | GHSA-57mv-jm88-66jc | string_render API を介したサンドボックス化されていない Twig テンプレートレンダリング |
FOSSBilling は、無料のオープンソース請求およびクライアント管理プラットフォームです。バージョン 0.5.4 から 0.7.2 が影響を受けます。FOSSBilling 0.8.0 は両方の脆弱性にパッチを適用しています。
このラボでは、2 つの 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 }}` を使用してこのチェーンを実演します。`getDi()` を経由する完全な RCE パスは実演しません。
## 検証済みの事実
| 主張 | 証拠 | 検証方法 |
| ----- | -------- | ------------- |
| CVE-2026-27604 は FOSSBilling API のロール処理における認証バイパスです。 | GHSA-78x5-c8gw-8279: ロールチェッカーに throw が欠落しているため、/api/system/ が管理者として解決されます。 | PoC を実行: ゲストパスは拒否され、システムパスは管理者結果を返します。 |
| CVE-2026-28496 は FOSSBilling の Twig レンダリングにおける SSTI です。 | 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」を返します。 | ポート 8082 に対して PoC を実行します。 |
| 認証バイパスにより、未認証の管理者アクセスが可能になります。 | /api/guest/ は string_render を拒否しますが、/api/system/ は認証なしで結果を返します。 | PoC のステージ 1 を実行します。 |
| SSTI は攻撃者が制御するテンプレートを評価します。 | {{ 7*7 }} は脆弱なパスを通じて 49 を返します。 | PoC のステージ 2 を実行します。 |
| このチェーンにより 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 管理者 ID に解決され、認証されていない呼び出し元に完全な管理者 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 管理者 ID にマッピングされ、完全な管理者権限を持ちます。
string_render 管理 API メソッドはリクエストデータから _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 テンプレートを作成し、サンドボックス制限なしでレンダリングします。このテンプレートはテンプレートコンテキスト内のオブジェクトにアクセスでき、getDi() を公開するゲスト API ハンドラーも含まれます。
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):**
ロールチェッカーは、許可されていないロールに対して適切に例外をスローするようになりました。