Summary
A Server-Side Template Injection (SSTI) vulnerability exists in FOSSBilling's template rendering system. Administrators with access to features that render Twig templates (email templates, mass mail campaigns, custom payment adapters, and the string_render API endpoint) can inject arbitrary Twig expressions, leading to information disclosure and remote code execution. The vulnerability exists because Twig templates are rendered without a sandbox, allowing access to the full Twig environment, API context, and the application's dependency injection container.
Impact
Information Disclosure to Remote Code Execution: Templates can access sensitive data via the guest, admin, and client API globals that are injected into the Twig environment. This includes company settings, client data, and in specific scenarios, staff password hashes.
Additionally, these API handler objects expose a getDi() method returning the full dependency injection container, which grants direct access to:
- PDO - arbitrary SQL read/write against the entire database (not limited to what API endpoints return)
- Symfony
FilesystemAdapter - cache read/write (enables extension manifest poisoning)
- Password service - password hashing for creating rogue accounts
- Session service - session manipulation
- All other registered application services
An admin-authenticated attacker can chain SSTI with DI container access to achieve remote code execution on the host server.
- When chained with GHSA-78x5-c8gw-8279 (authorization bypass), the authentication requirement is eliminated, allowing unauthenticated remote attackers to achieve full remote code execution.
- When this flaw is reached through the Custom payment gateway, the rendered template is returned directly to invoice-paying clients, so exploitation may present as stored client-side script execution in addition to the broader SSTI and DI-container abuse described above.
Privilege Required:
- Standalone: Administrator (trusted role).
- Chained with GHSA-78x5-c8gw-8279: None (unauthenticated).
Affected Versions
FOSSBilling version 0.1.0 through 0.7.2 (all releases to date).
Patched Versions
0.8.0
Details
The vulnerability exists in the template rendering chain:
src/modules/System/Service.php - The renderString() method uses $twig->createTemplate($tpl) to render arbitrary strings without sandbox restrictions.
src/modules/System/Api/Admin.php - The string_render() admin API endpoint passes user-supplied _tpl directly to renderString().
src/modules/Email/Service.php - Email templates are rendered via _parse() which calls renderString() with the template content and subject.
src/di.php - The Twig environment is configured without a sandbox, and with StringLoaderExtension enabled.
src/library/Api/Handler.php / src/library/Api/Abstract.php - The getDi() method on API handler objects returns the raw DI container.
The following variables are available in email templates:
| Variable |
Availability |
Contains |
guest |
Always |
Guest API - access to public system data. getDi() exposes full DI container. |
admin |
When admin is logged in |
Admin API - full administrative access. getDi() exposes full DI container. |
client |
When client_id is set |
Client API - client-specific data. getDi() exposes full DI container. |
staff |
When to_staff=true |
Raw staff database row (includes pass). |
c |
When to_client |
Sanitized client array (no password). |
c |
When to_admin |
Raw Admin model (includes pass). |
Affected Components
src/modules/Email/Service.php - Email template rendering
src/modules/System/Api/Admin.php - string_render admin API endpoint
src/modules/System/Service.php - Template string rendering
src/modules/Massmailer/Service.php - Mass email campaigns
src/library/Payment/Adapter/Custom.php - Custom payment adapter templates
src/library/Box/TwigExtensions.php - Twig configuration
src/library/Api/Handler.php - getDi() exposes full DI container
src/library/Api/Abstract.php - getDi() base implementation
Workarounds
- Audit existing email templates for suspicious Twig expressions.
- Rotate all admin and client API tokens.
- Block external access to /api/system/* at reverse proxy/WAF to mitigate chaining with GHSA-78x5-c8gw-8279.
Acknowledgements
Thanks to @0xcan1337 for responsible disclosure, vulnerability analysis, and PoC. Additional thanks to @Chocapikk for identifying the getDi() DI container exposure and RCE escalation chain, and to @VadlaReddySai for independently reporting the Custom payment gateway manifestation of this issue.
Summary
A Server-Side Template Injection (SSTI) vulnerability exists in FOSSBilling's template rendering system. Administrators with access to features that render Twig templates (email templates, mass mail campaigns, custom payment adapters, and the
string_renderAPI endpoint) can inject arbitrary Twig expressions, leading to information disclosure and remote code execution. The vulnerability exists because Twig templates are rendered without a sandbox, allowing access to the full Twig environment, API context, and the application's dependency injection container.Impact
Information Disclosure to Remote Code Execution: Templates can access sensitive data via the
guest,admin, andclientAPI globals that are injected into the Twig environment. This includes company settings, client data, and in specific scenarios, staff password hashes.Additionally, these API handler objects expose a
getDi()method returning the full dependency injection container, which grants direct access to:FilesystemAdapter- cache read/write (enables extension manifest poisoning)An admin-authenticated attacker can chain SSTI with DI container access to achieve remote code execution on the host server.
Privilege Required:
Affected Versions
FOSSBilling version 0.1.0 through 0.7.2 (all releases to date).
Patched Versions
0.8.0
Details
The vulnerability exists in the template rendering chain:
src/modules/System/Service.php- TherenderString()method uses$twig->createTemplate($tpl)to render arbitrary strings without sandbox restrictions.src/modules/System/Api/Admin.php- Thestring_render()admin API endpoint passes user-supplied_tpldirectly to renderString().src/modules/Email/Service.php- Email templates are rendered via_parse()which callsrenderString()with the template content and subject.src/di.php- The Twig environment is configured without a sandbox, and withStringLoaderExtensionenabled.src/library/Api/Handler.php/src/library/Api/Abstract.php- ThegetDi()method on API handler objects returns the raw DI container.The following variables are available in email templates:
guestgetDi()exposes full DI container.admingetDi()exposes full DI container.clientclient_idis setgetDi()exposes full DI container.staffto_staff=truecto_clientcto_adminpass).Affected Components
src/modules/Email/Service.php- Email template renderingsrc/modules/System/Api/Admin.php-string_renderadmin API endpointsrc/modules/System/Service.php- Template string renderingsrc/modules/Massmailer/Service.php- Mass email campaignssrc/library/Payment/Adapter/Custom.php- Custom payment adapter templatessrc/library/Box/TwigExtensions.php- Twig configurationsrc/library/Api/Handler.php-getDi()exposes full DI containersrc/library/Api/Abstract.php-getDi()base implementationWorkarounds
Acknowledgements
Thanks to @0xcan1337 for responsible disclosure, vulnerability analysis, and PoC. Additional thanks to @Chocapikk for identifying the
getDi()DI container exposure and RCE escalation chain, and to @VadlaReddySai for independently reporting the Custom payment gateway manifestation of this issue.