# Template injection via spliced query arguments

User input reaching template source or generated code without escaping, via query, filter, or CLI-style args

_Area: Web injection and code execution. Seen in 3 disclosed reports; one confirmed bounty: $23,000; latest 2026-08-21. Updated 2026-11-02._

## How it works

A template engine or compiler treats some input as *source code* rather than *data*. The assumption that breaks is "this value is a literal, not code." Two ways it happens: (1) a runtime evaluator is handed a user-controlled string and executes it as script [E2](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-gaining-access-to-ubers-user-data-through-ampscript-evaluation.md); (2) a compiler splices raw token bytes into generated code without escaping or routing through the normal expression emitter [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-hrwp-4hh9-c8r8.md). A third shape: framework code reads `$_SERVER['argv']` to detect CLI flags, but under `register_argc_argv=On` the query string populates `argv`, so URL tokens become CLI options that redirect template paths [E1](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md). In every case the boundary between configuration/data and executable template source is missing.

## Where it shows up

- Endpoints that take a `message`, `template`, `preview`, or `filter` parameter and pass it to an evaluator (AMPScript, Liquid, Handlebars, Twig, Volt) [E2](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-gaining-access-to-ubers-user-data-through-ampscript-evaluation.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-hrwp-4hh9-c8r8.md)
- Compiler cases that read `expr.value` / raw token bytes directly instead of calling the expression emitter or escaping quotes [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-hrwp-4hh9-c8r8.md)
- PHP code branching on `isset($_SERVER['argv'])` or `empty($_SERVER['argv'])` with no `PHP_SAPI === 'cli'` check [E1](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md)
- `register_argc_argv=On` in Docker/php.ini, letting query tokens like `--templatesPath` become flags [E1](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md)
- Template loaders that accept a path/URL and only gate it with `file_exists()`/`stat()` [E1](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md)
- Subdomains named after marketing vendors (exacttarget, sendgrid) serving preview/test consoles [E2](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-gaining-access-to-ubers-user-data-through-ampscript-evaluation.md)

## How to review

The invariant: **no user-controlled byte may reach template source, generated code, or a CLI-option parser without passing through the engine's escaping/expression path.**

- For compilers: every filter/function case must route arguments through `expression()` (or equivalent); flag any case using raw `expr.value` [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-hrwp-4hh9-c8r8.md).
- For evaluators: confirm the parameter is treated as data, not script; check whether the vendor language supports HTTP callbacks or data-lookup functions [E2](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-gaining-access-to-ubers-user-data-through-ampscript-evaluation.md).
- For CLI detection: require an explicit `PHP_SAPI` check, not just `argv` presence; verify `register_argc_argv` state in the deployed config [E1](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md).
- For path includes: confirm the wrapper/path is allowlisted, not merely `file_exists`-gated [E1](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md).

## How finders spotted it

- Read `bootstrap.php`, saw `App::cliOption` reading flags, then read `cliOption` and noticed it only checks `empty($_SERVER['argv'])` with no SAPI check; recalled `register_argc_argv` behavior, then enumerated stream wrappers passing `file_exists` to find `ftp://` [E1](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md).
- Asset monitoring flagged a previously-404 subdomain now serving an internal test console; recognized example syntax as AMPScript from the vendor-named subdomain and docs, then tested whether `message` was evaluated via an HTTP callback [E2](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-gaining-access-to-ubers-user-data-through-ampscript-evaluation.md).
- Code review of the `join` case: noticed separator and array token values used as raw `expr.value` bytes rather than through `expression()`, confirmed against generated C that both fragments concatenate unescaped [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-hrwp-4hh9-c8r8.md).

## Variants seen

- CLI-flag injection via query string redirecting template path to an FTP URL, then Twig injection to RCE [E1](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md)
- Marketing-script evaluator (AMPScript) executing user `message` against data extensions [E2](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-gaining-access-to-ubers-user-data-through-ampscript-evaluation.md)
- Compiler filter (`join`) splicing raw tokens into generated PHP [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-hrwp-4hh9-c8r8.md)

## Impact and severity

E1 and E3 reach RCE from unauthenticated or template-level input [E1](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-hrwp-4hh9-c8r8.md). E2 leaked driver UUIDs and emails, and the script could be parameterized to dump entire data extensions, scaling from one record to the whole table [E2](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-gaining-access-to-ubers-user-data-through-ampscript-evaluation.md). Severity rises with reach (unauthenticated, whole-table) and with combination (path control + template injection) [E1](https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md).

## Seen in real software
Open-source packages and projects, and products, where this pattern was found. If the target runs one, compare the version it runs with the fix.
- **phalcon/cphalcon (composer)** <= 5.15.0, fixed in 5.16.0: Phalcon Volt compiler `join` filter compile-time PHP code injection (SSTI leads to RCE) (CVE-2026-59989). Report: https://bugbountylibrary.com/r/ghsa%3AGHSA-hrwp-4hh9-c8r8.md. Fix: https://github.com/phalcon/cphalcon/commit/e434061be3b7161930476c1368c868badc71e1bd

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-how-an-obscure-php-footgun-led-to-rce-in-craft-cms.md
- E2: https://bugbountylibrary.com/r/blog%3Aassetnote-io-resources-research-gaining-access-to-ubers-user-data-through-ampscript-evaluation.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-hrwp-4hh9-c8r8.md
