本仓库包含一个本地 Docker 实验环境,用于复现和验证 CVE-2026-28496,这是一个影响 FOSSBilling 的 Twig 模板渲染行为的服务器端模板注入漏洞。
FOSSBilling 是一个免费开源的开票和客户管理平台。0.8.0 之前的版本受到不安全的 Twig 模板渲染行为影响,该行为可以评估提供的模板表达式。本实验使用 FOSSBilling 0.8.0 作为已修补的比较目标。
本实验比较两个 FOSSBilling 版本:
| 服务 | FOSSBilling 版本 | 用途 | URL |
|---|---|---|---|
| vuln | 0.7.2 | 易受攻击的比较目标 | http://localhost:8081 |
| patched | 0.8.0 | 已修补的比较目标 | http://localhost:8082 |
本本地实验中演示的 HTTP 验证路径为:```text Unauthenticated HTTP request in this local FOSSBilling 0.7.2 lab → POST /api/system/system/string_render → JSON body contains _tpl={{ 7*7 }} → vulnerable target renders the Twig expression → patched target does not expose the same tested API behavior
在易受攻击的目标中,API调用返回:```json
{"result":"49","error":null}
在已修补的目标上,相同的请求返回:```json {"result":null,"error":{"message":"Unknown API call system/system/string_render","code":879}}
本实验使用FOSSBilling 0.7.2和FOSSBilling 0.8.0验证易受攻击版本与已修补版本的HTTP行为。
本实验有意将范围限定在本地Docker服务。它不针对外部系统,也不包含网页后门、恶意软件、持久化、外部回调、数据库导出或破坏性负载。
## Verified Facts
| 声明 | 证据 | 在本实验中如何验证 |
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| CVE-2026-28496影响FOSSBilling 0.8.0之前的版本。 | 公开的CVE和公告元数据将FOSSBilling 0.8.0之前的版本识别为受Twig SSTI影响。 | 查看参考资料部分,并比较易受攻击版本和已修补版本。 |
| FOSSBilling 0.7.2被用作易受攻击的比较目标。 | 易受攻击服务基于官方`fossbilling/fossbilling:0.7.2` Docker镜像构建。 | 检查`vuln/Dockerfile`并运行`docker compose ps -a`。 |
| FOSSBilling 0.8.0被用作已修补的比较目标。 | 已修补服务基于官方`fossbilling/fossbilling:0.8.0` Docker镜像构建。 | 检查`patched/Dockerfile`并运行`docker compose ps -a`。 |
| 易受攻击的HTTP路径是`/api/system/system/string_render`。 | 易受攻击目标对于`_tpl={{ 7*7 }}`返回带有`result: "49"`的JSON。 | 运行`python3 poc/poc.py --url http://localhost:8081`。 |
| 已修补目标不暴露相同的HTTP行为。 | 已修补目标返回`Unknown API call system/system/string_render`。 | 运行`python3 poc/poc.py --url http://localhost:8082`。 |
| PoC仅使用HTTP。 | `poc/poc.py`发送HTTP POST请求,不调用Docker、Docker Compose、shell命令或容器API。 | 检查`poc/poc.py`。 |
| 本实验在Docker Compose启动时自动安装两个FOSSBilling目标。 | 一次性安装器sidecar容器完成设置并以状态0退出。 | 运行`docker compose ps -a`和`docker compose logs installer-vuln installer-patched`。 |
| 易受攻击目标渲染无害的Twig表达式。 | 来自端口8081的HTTP响应是`{"result":"49","error":null}`。 | 运行易受攻击的PoC命令。 |
| 已修补目标不通过测试的API路径渲染相同的表达式。 | 来自端口8082的HTTP响应是带有代码`879`的JSON API错误。 | 运行已修补的PoC命令。 |
## 假设与未知
本实验使用FOSSBilling 0.7.2作为易受攻击的比较目标,因为公开的漏洞研究将FOSSBilling 0.8.0之前的版本识别为受影响,而0.7.2是测试链中使用的最新易受攻击版本。
本实验使用FOSSBilling 0.8.0作为已修补的比较目标,因为公开的公告元数据将0.8.0识别为已修补版本。
本实验关注可观察的HTTP行为:```text
POST /api/system/system/string_render
使用此 JSON 正文:```json {"_tpl":"{{ 7*7 }}"}
该实验表明,FOSSBilling 0.7.2 通过 HTTP API 路径渲染所提供的 Twig 表达式,而 FOSSBilling 0.8.0 不公开相同的 API 调用。
本实验并不声称测试 FOSSBilling 的所有模板渲染功能。CVE-2026-28496 还涉及其他 Twig 渲染上下文,例如应用程序内部可用的模板渲染功能。
本实验未演示完整的未经认证的远程代码执行链。它仅验证在本地 FOSSBilling 0.7.2 目标上观察到的未经认证的 HTTP 行为,并与 FOSSBilling 0.8.0 进行比较。完整的公开利用链涉及额外的 API 授权行为,超出了此处展示的安全 Twig 表达式验证范围。
本实验未演示:
* 远程命令执行,
* 凭证提取,
* 数据库导出,
* 扩展安装,
* 任意文件写入,
* 持久化,
* Web Shell 上传,
* 外部回调,
* 针对非实验系统的攻击,
* 或后利用活动。
## 根本原因总结
CVE-2026-28496 的根本原因是不安全的 Twig 模板渲染。
FOSSBilling 使用 Twig 渲染动态模板。在受影响版本中,提供的模板字符串可在缺乏充分沙箱限制的情况下传递给 Twig 渲染逻辑。
易受攻击的行为可总结为:```text
Input template string
→ FOSSBilling API receives _tpl
→ System\Api\Admin::string_render() reads _tpl
→ System\Service::renderString() receives the template string
→ Twig creates a template from the supplied string
→ Twig evaluates the expression
→ rendered output is returned in the HTTP response
对于这个无害的模板表达式:```twig {{ 7*7 }}
脆弱目标计算表达式并返回:```text
49
安全问题并不仅限于算术求值。算术求值只是本实验室中使用的安全可见信号。
更敏感的安全问题在于,未沙箱化的 Twig 模板可能访问模板上下文中暴露的对象和方法。公开研究描述了一条影响更大的路径:当存在合适的模板上下文对象时,Twig 模板执行可以触及应用程序内部,包括依赖注入容器。
简化的漏洞模型为:```text Template renderer → unsandboxed Twig expression → method/object access may be possible → application internals may become reachable → sensitive services may become reachable
FOSSBilling 0.8.0 中的修补设计强化了脆弱行为。在本实验室中,修补后的目标不再暴露经过测试的API调用:```json
{"result":null,"error":{"message":"Unknown API call system/system/string_render","code":879}}
安全教训是:```text Template engines must not render user-controlled template strings in a privileged application context unless strict authorization and sandbox boundaries are enforced.
## 源代码分析
易受攻击的 HTTP 行为由 FOSSBilling 0.7.2 中的源代码路径支持。
API 方法从请求数据接收 `_tpl`,并将其传递给系统服务渲染器。
相关漏洞入口点:```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);
}
重要的数据流是:```text HTTP request body → _tpl → System\Api\Admin::string_render() → System\Service::renderString()
在 FOSSBilling 0.7.2 中,`renderString()` 尝试将提供的值作为模板名称加载。如果失败,则将提供的值视为模板字符串并传递给 `createTemplateFromString()`。
简化漏洞流程:```php
public function renderString($tpl, $try_render, $vars)
{
$twig = $this->di['twig'];
try {
$template = $twig->load($tpl);
$parsed = $template->render($vars);
} catch (\Exception) {
// $twig->load throws an exception when $tpl is a raw template string
$parsed = $this->createTemplateFromString($tpl, $try_render, $vars);
}
return $parsed;
}
易受攻击的接收点是 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;
}
安全相关的源模式是:```text
_tpl from request data
→ used as $tpl
→ passed to Twig createTemplate()
→ rendered server-side
这解释了易受攻击的实验室结果:```text POST /api/system/system/string_render {"_tpl":"{{ 7*7 }}"}
易受攻击的响应:```json
{"result":"49","error":null}
值 49 表明提供的 Twig 表达式已在服务端被求值。
安全的实验室有效载荷仅使用算术运算:```twig {{ 7*7 }}
然而,根本原因比算术表达式求值的敏感性更高。在易受攻击的渲染上下文中,Twig模板可能会与模板环境中存在的应用程序对象交互。公开研究描述了更高影响的链,其中API上下文对象可以暴露对应用程序内部(如依赖注入容器)的访问。
来源级回归检查确认了更深层次的行为:```text
FOSSBilling 0.7.2:
{{ guest.getDi() }}
→ DI_VISIBLE
FOSSBilling 0.8.0:
{{ guest.getDi() }}
→ blocked by Twig sandbox policy
这就是为何该漏洞最好被理解为不安全的模板渲染,而不仅仅是一个计算器式的表达式评估漏洞。
FOSSBilling 0.8.0 通过强化字符串渲染并移除经测试的易受攻击的 HTTP 行为,改变了该漏洞的行为。
在修补后的版本中,字符串渲染通过沙盒感知渲染进行路由,而不是直接使用广泛的 Twig 能力渲染任意模板字符串。