Server-Side Template Injection (SSTI)
SSTI tests whether user input is concatenated into a server-side template and evaluated. The exam expects engine identification and a path from evaluation to file read or command execution.
Core rules and concepts
- Detection payloads vary by engine:
{{7*7}}for Jinja2/Twig,${7*7}for Freemarker and JSP EL,<%= 7*7 %>for ERB,#{7*7}for Ruby, and{7*7}for Smarty. - Engine confirmation: Jinja2 returns
7777777for{{7*'7'}}, while Twig returns49. - RCE paths reach Python object internals in Jinja2 (
__class__,__mro__,__subclasses__, orconfigglobals),_selfin Twig,?new()in Freemarker, andRuntimein Velocity. - Blind SSTI is confirmed with time delays or DNS callbacks.
- Sandboxed environments require escape chains and filter bypasses.
Worked example
A name parameter reflects Hello {{7*7}} as Hello 49, proving evaluation. Send {{7*'7'}}; the response is 7777777, identifying Jinja2 rather than Twig. Then submit {{ cycler.__init__.__globals__.os.popen('id').read() }}, which returns uid=33(www-data). Reasoning: confirming the engine avoids sending Twig syntax to a Jinja2 target, and the globals path reaches the os module for command execution.
Traps that cost marks
- Treating a mathematical result as proof without confirming the engine.
- Testing only one syntax family.
- Forgetting to URL-encode braces and quotes.
- Giving up when underscores or quotes are filtered; use
request.args,attr, or hex escapes. - Confusing client-side templating in Angular or Vue with server-side evaluation.