SSTI na prática: identificando a tecnologia por trás do template

SSTI (Server-Side Template Injection) acontece quando o input do usuário é processado diretamente dentro de uma engine de template no servidor.
Isso pode permitir desde simples vazamentos até execução remota de código (RCE).

Após confirmar uma possível SSTI, o próximo desafio é descobrir qual engine de template está processando a entrada — informação essencial para validação, mitigação e reporte. Este guia compartilha a metodologia testada que usamos para descobrir a tecnologia por trás dos templates de forma rápida e confiável.

Node.js

Handlebars

Indicadores fortes de Handlebars

  1. Sintaxe {{ }} com blocos/sections usando {{#if ...}}{{/if}}, {{#each ...}}{{/each}}, {{#unless ...}}
  2. Comentários: {{! comment }} ou {{!-- comment --}}
  3. Unescaped output com triple moustache: {{{var}}}
  4. Mensagens de erro/stack trace contendo handlebars, express-handlebars, handlebars.runtime, ou Missing helper
  5. Presença de helpers custom (servidor) — erros do tipo Missing helper: "nomeDoHelper"

Testes inócuos

  1. Teste de comentário
payloads:
{{!comment_handlebars}}
{{!-- long comment --}}

O que observar:

  1. Teste de bloco/section (Handlbars tem #if, #each)
payloads:
{{#if true}}YES{{/if}}

O que observar:

  1. Teste de avaliação de expressão (aritmética)
payloads:
{{7*7}}
{{{7*7}}}

O que observar:

  1. Teste de unescaped output (triple moustache)
payloads:
{{{"<b>OK</b>"}}}

O que observar:

  1. Teste de helper/partial indication
payloads:
{{#each [1,2]}}X{{/each}}
{{> partialName}}

O que observar:

Checklist curto (passo-a-passo)

EJS

Indicadores fortes de EJS

  1. Presença de delimitadores <% %>, <%= %>, <%- %> nas respostas ou em fontes/paths
  2. Arquivos .ejs expostos em comentários, erros ou caminhos (views/*.ejs)
  3. Stack traces/erros Node.js com referências a ejs ou EJS (ou express + referência a arquivos .ejs)
  4. Headers ou banners: X-Powered-By: Express junto com templates que usam <% é um bom indício

Testes inócuos

  1. Teste básico de avaliação (JS expressions)
payloads:
<%= 7*7 %>

O que observar:

  1. Teste de escape vs raw
payloads:
<%= "<b>OK</b>" %>
<%- "<b>OK</b>" %>

O que observar:

  1. Teste de scriptlet (não imprime)
payloads:
<% var x = 1; %>HELLO

O que observar:

  1. Teste de comentário EJS
  1. Provocar erro leve (com cuidado)
payloads:
<%= nonexistentVar %>

O que observar:

Checklist rápido (passo-a-passo)

Nunjucks

Indicadores fortes de Nunjucks

  1. Sintaxe e delimitadores típicos: {{ ... }}, {% ... %}, {# ... #}.
  2. Arquivos .njk, .nunjucks ou referências a views/*.njk expostos em comentários, erros ou caminhos.
  3. Stack traces/erros Node.js com referências a nunjucks, nunjucks.configure() ou nunjucks.render.
  4. Header X-Powered-By: Express combinado com templates que usam {% %}/{{ }} e presença do pacote nunjucks no código/erro.

Testes inócuos

  1. Teste básico de avaliação (expressões)
payloads:
{{ 7*7 }}

O que observar:

  1. Teste de blocos / tags (if / for)
payloads:
{% if true %}YES{% endif %}
{% for i in [1,2] %}X{% endfor %}

O que observar:

  1. Teste de comentário Nunjucks
payloads:
{# teste comentario #}

O que observar:

  1. Teste de filtros (pipes)
payloads:
{{ "abc" | upper }}
{{ [1,2] | length }}

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
{{ unknownVar }}

O que observar:

Checklist rápido (passo-a-passo)

Pug (ex-Jade)

Indicadores fortes de Pug

  1. Sintaxe baseada em indentação e não em delimitadores — ausência de {% %}, {{ }} ou <% %>.
  2. Stack traces/erros Node.js com referências a pug, jade, pug.compile(), pug.render().
  3. Extensões .pug ou .jade em caminhos, mensagens de erro ou diretórios (views/*.pug).
  4. Headers X-Powered-By: Express com estruturas HTML altamente minificadas ou indentadas de forma incomum (padrão de saída do Pug).

Testes inócuos

  1. Teste básico de interpolação
payloads:
#{7*7}

O que observar:

  1. Teste de escape vs não escape
payloads:
#{'<b>OK</b>'}
!{'<b>OK</b>'}

O que observar:

  1. Teste de blocos condicionais
payloads:
if true
OK

O que observar:

  1. Teste de loops
payloads:
each val in [1,2]
= val

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
#{unknownVar}

O que observar:

Checklist rápido (passo-a-passo)

Mustache

Indicadores fortes de Mustache

  1. Sintaxe simples com delimitadores {{ }} sem suporte a lógica (sem {% %}, <% %> ou blocos if/for).
  2. Erros, stack traces ou comentários com menções a mustache, hogan.js, mustache.render() ou Hogan.compile().
  3. Arquivos .mustache ou .hjs visíveis em caminhos (views/*.mustache).
  4. Respostas HTML sem interpretação de código JS — apenas substituição de variáveis simples.
  5. Presença de delimitadores triplo { {{{ }}} } (não escapados) é um indicativo forte de Mustache/Hogan.

Testes inócuos

  1. Teste básico de substituição
payloads:
{{7*7}}

O que observar:

  1. Teste de variável conhecida
payloads:
{{title}}

O que observar:

  1. Teste de escape vs não escape
payloads:
{{ "<b>OK</b>" }}
{{{ "<b>OK</b>" }}}

O que observar:

  1. Teste de seção condicional
payloads:
{{#true}}YES{{/true}}
{{^false}}NO{{/false}}

O que observar:

  1. Teste de comentário Mustache
payloads:
{{! este é um comentário }}

O que observar:

Checklist rápido (passo-a-passo)

Python

Jinja2

Indicadores fortes de Jinja2

  1. Sintaxe característica com delimitadores {{ ... }}, {% ... %}, {# ... #} (semelhante a Nunjucks).
  2. Stack traces ou erros Python com referências a jinja2, TemplateSyntaxError, UndefinedError, ou jinja2.environment.
  3. Extensões .jinja, .jinja2, .html com menções a Jinja nos caminhos (templates/*.jinja2).
  4. Cabeçalhos Server: Werkzeug, X-Powered-By: Flask, ou respostas típicas de frameworks Python.
  5. Saída HTML com espaçamento e estrutura idêntica à renderização padrão de Flask/Django templates.

Testes inócuos

  1. Teste básico de avaliação (expressões)
payloads:
{{ 7*7 }}

O que observar:

  1. Teste de filtros
payloads:
{{ "abc"|upper }}
{{ [1,2,3]|length }}

O que observar:

  1. Teste de blocos / controle de fluxo
payloads:
{% if true %}YES{% endif %}
{% for i in [1,2] %}X{% endfor %}

O que observar:

  1. Teste de comentários
payloads:
{# comentario #}

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
{{ unknown_var }}

O que observar:

Checklist rápido (passo-a-passo)

Mako

Indicadores fortes de Mako

  1. Sintaxe híbrida com delimitadores <% %>, ${ }, <%! %> (mistura de estilo JSP e Python).
  2. Stack traces ou erros Python com referências a mako, mako.template, mako.runtime, ou mako.exceptions.
  3. Extensões .mako em caminhos, mensagens de erro ou diretórios (templates/*.mako).
  4. Cabeçalhos Server: Werkzeug, Server: gunicorn, ou aplicações Python (Flask/Pyramid) com saída de template.
  5. Código HTML misturado com tags <% ... %> ou ${ ... } é sinal clássico de Mako.

Testes inócuos

  1. Teste básico de avaliação
payloads:
${7*7}

O que observar:

  1. Teste de bloco de código
payloads:
<% x = 1 %>OK

O que observar:

  1. Teste de controle de fluxo
payloads:
<% if True: %>YES<% endif %>

O que observar:

  1. Teste de escape vs não escape
payloads:
${"<b>OK</b>"}

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
${unknown_var}

O que observar:

Checklist rápido (passo-a-passo)

Django Templates

Indicadores fortes de Django Template Engine

  1. Sintaxe característica com delimitadores {{ ... }} e {% ... %}, porém sem suporte a execução direta de código Python.
  2. Stack traces/erros Python com menções a django.template, TemplateSyntaxError, VariableDoesNotExist, ou django.core.
  3. Extensões .html em diretórios templates/ (padrão do Django).
  4. Cabeçalhos Server: WSGIServer, X-Frame-Options: DENY, ou páginas de erro com o estilo visual padrão do Django (<h1>TemplateSyntaxError</h1>).
  5. Presença de filtros e tags específicas do Django (|safe, |length, |upper, {% url %}, {% csrf_token %}, etc.).

Testes inócuos

  1. Teste básico de substituição
payloads:
{{ 7*7 }}

O que observar:

  1. Teste de variável conhecida
payloads:
{{ request.path }}
{{ user.username }}

O que observar:

  1. Teste de filtro
payloads:
{{ "abc"|upper }}
{{ [1,2,3]|length }}

O que observar:

  1. Teste de bloco condicional
payloads:
{% if True %}YES{% endif %}

O que observar:

  1. Teste de comentário
payloads:
{% comment %}teste{% endcomment %}

O que observar:

PHP

Twig

Indicadores fortes de Twig

  1. Sintaxe característica com delimitadores {{ ... }}, {% ... %}, {# ... #} (Jinja-like, mas em PHP).
  2. Arquivos .twig ou menções a templates/*.twig em erros ou caminhos.
  3. Stack traces PHP ou mensagens de erro com referências a Twig\Environment, Twig\Error, Twig\Loader.
  4. Cabeçalhos típicos de aplicações PHP (X-Powered-By: PHP, Server: nginx/apache) combinados com templates Twig.
  5. Filtros e funções Twig específicas (|escape, |length, |upper, |raw, |join) presentes nas respostas.

Testes inócuos

  1. Teste básico de avaliação
payloads:
{{ 7*7 }}

O que observar:

  1. Teste de filtro
payloads:
{{ "abc"|upper }}
{{ [1,2,3]|length }}

O que observar:

  1. Teste de bloco condicional
payloads:
{% if true %}YES{% endif %}

O que observar:

  1. Teste de loop
payloads:
{% for i in [1,2] %}X{% endfor %}

O que observar:

  1. Teste de comentário
payloads:
{# comentário #}

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
{{ unknownVar }}

O que observar:

Checklist rápido (passo-a-passo)

Smarty

Indicadores fortes de Smarty

  1. Sintaxe típica com delimitadores {...} para variáveis e funções, {* ... *} para comentários.
  2. Arquivos .tpl visíveis em caminhos (templates/*.tpl) ou mensagens de erro.
  3. Stack traces PHP ou mensagens de erro com referências a Smarty_Internal_Template, SmartyException, Smarty->fetch().
  4. Cabeçalhos PHP típicos (X-Powered-By: PHP) combinados com templates com sintaxe {variable}.
  5. Funções e modificadores Smarty ({$var|escape}, {$arr|count}, {foreach $items as $i}) presentes nas respostas.

Testes inócuos

  1. Teste básico de variável
payloads:
{$testVar}

O que observar:

  1. Teste de escape / raw
payloads:
{$htmlVar}
{$htmlVar nofilter}

O que observar:

  1. Teste de loops
payloads:
{foreach $items as $item}
{$item}
{/foreach}

O que observar:

  1. Teste de condição
payloads:
{if $cond}YES{/if}

O que observar:

  1. Teste de comentário
payloads:
{* comentário *}

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
{$unknownVar}

O que observar:

Checklist rápido (passo-a-passo)

Blade

Indicadores fortes de Blade

  1. Sintaxe característica com delimitadores {{ ... }} para variáveis e {!! ... !!} para saída sem escape.
  2. Arquivos .blade.php em diretórios resources/views/*.blade.php.
  3. Stack traces ou erros PHP com referências a Illuminate\View\View, BladeCompiler, ou ViewException.
  4. Cabeçalhos PHP típicos (X-Powered-By: PHP) combinados com templates Blade.
  5. Diretivas Blade específicas: @if, @foreach, @include, @csrf, @extends, @section, etc.

Testes inócuos

  1. Teste básico de variável
payloads:
{{ 7*7 }}

O que observar:

  1. Teste de escape vs raw
payloads:
{{ "<b>OK</b>" }}
{!! "<b>OK</b>" !!}

O que observar:

  1. Teste de diretiva condicional
payloads:
@if(true)
YES
@endif

O que observar:

  1. Teste de loop
payloads:
@foreach([1,2] as $i)
{{ $i }}
@endforeach

O que observar:

  1. Teste de comentário
payloads:
{{-- comentário --}}

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
{{ $unknownVar }}

O que observar:

Checklist rápido (passo-a-passo)

Java

FreeMarker

Indicadores fortes de FreeMarker

  1. Sintaxe característica com ${ ... } para variáveis e <# ... > ou <#-- ... --> para diretivas e comentários.
  2. Arquivos .ftl visíveis em caminhos (templates/*.ftl) ou mensagens de erro.
  3. Stack traces Java ou mensagens de erro com referências a freemarker.template.Template, freemarker.core, TemplateException.
  4. Cabeçalhos típicos de servidores Java (Server: Apache-Coyote/1.1, Servlet, Spring) combinados com templates FreeMarker.
  5. Diretivas FreeMarker específicas: <#if>, <#list>, <#include>, <#macro>, <#assign>.

Testes inócuos

  1. Teste básico de variável
payloads:
${7*7}

O que observar:

  1. Teste de diretiva condicional
payloads:
<#if true>
YES
</#if>

O que observar:

  1. Teste de loop
payloads:
<#list [1,2] as i>
${i}
</#list>

O que observar:

  1. Teste de comentário
payloads:
<#-- comentário -->

O que observar:

  1. Teste de escape / raw
payloads:
${"<b>OK</b>"?html}

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
${unknownVar}

O que observar:

Checklist rápido (passo-a-passo)

Apache Velocity

Indicadores fortes de Velocity

  1. Sintaxe característica com $variable para variáveis e #directive(...) para diretivas (#if, #foreach, #set, #include).
  2. Arquivos .vm visíveis em caminhos (templates/*.vm) ou mensagens de erro.
  3. Stack traces Java ou mensagens de erro com referências a org.apache.velocity.Template, VelocityEngine, VelocityException.
  4. Cabeçalhos típicos de servidores Java (Server: Apache-Coyote/1.1, Servlet) combinados com templates Velocity.
  5. Diretivas específicas: #if, #foreach, #set, #include, #macro.

Testes inócuos

  1. Teste básico de variável
payloads:
$testVar

O que observar:

  1. Teste de diretiva condicional
payloads:
#if($true)
YES
#end

O que observar:

  1. Teste de loop
payloads:
#foreach($i in [1,2])
$i
#end

O que observar:

  1. Teste de comentário
payloads:
## comentário

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
$unknownVar

O que observar:

Checklist rápido (passo-a-passo)

Thymeleaf

Indicadores fortes de Thymeleaf

  1. Sintaxe característica com atributos HTML th:text, th:utext, th:if, th:each, th:include, th:replace.
  2. Arquivos .html em diretórios templates/ com namespace de atributos th:.
  3. Stack traces ou erros Java com referências a org.thymeleaf.TemplateEngine, TemplateProcessingException.
  4. Cabeçalhos típicos de servidores Java (Server: Apache-Coyote/1.1, Servlet) combinados com templates Thymeleaf.
  5. Uso de expressões ${...} dentro de atributos th: para interpolação de variáveis.

Testes inócuos

  1. Teste básico de variável
payloads:
<p th:text="${7*7}">Fallback</p>

O que observar:

  1. Teste de escape vs raw
payloads:
<p th:text="'<b>OK</b>'">Fallback</p>
<p th:utext="'<b>OK</b>'">Fallback</p>

O que observar:

  1. Teste de condição
payloads:
<p th:if="${true}">YES</p>

O que observar:

  1. Teste de loop
payloads:
<ul>
<li th:each="i : ${[1,2]}">${i}</li>
</ul>

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
<p th:text="${unknownVar}">Fallback</p>

O que observar:

Checklist rápido (passo-a-passo)

Ruby

ERB

Indicadores fortes de ERB

  1. Sintaxe característica com delimitadores <% ... %> para execução de código e <%= ... %> para output.
  2. Arquivos .erb ou .html.erb em diretórios app/views/*.erb ou app/views/**/*.html.erb.
  3. Stack traces ou erros Ruby com referências a ERB::Compiler, ERB#result, ActionView::Template::Error.
  4. Cabeçalhos típicos de servidores Ruby (Server: Puma, Server: WEBrick) combinados com templates ERB.
  5. Uso de <%# ... %> para comentários, que não aparecem na saída renderizada.

Testes inócuos

  1. Teste básico de avaliação (expressões)
payloads:
<%= 7*7 %>

O que observar:

  1. Teste de código sem output (scriptlet)
payloads:
<% x = 1 %>HELLO

O que observar:

  1. Teste de escape vs raw
payloads:
<%= "<b>OK</b>" %>

O que observar:

  1. Teste de comentário
payloads:
<%# este é um comentário %>

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
<%= unknown_var %>

O que observar:

Checklist rápido (passo-a-passo)

Liquid

Indicadores fortes de Liquid

  1. Sintaxe característica com delimitadores {{ ... }} para variáveis e {% ... %} para tags.
  2. Arquivos .liquid ou .html com tags Liquid em diretórios de templates (templates/*.liquid).
  3. Stack traces ou erros Ruby com referências a Liquid::Template, Liquid::Error, Liquid::ParseError.
  4. Uso de filtros Liquid (| upcase, | size, | strip, | escape) e tags (for, if, include, assign).
  5. Comentários {% comment %} ... {% endcomment %} ou {# ... #}.

Testes inócuos

  1. Teste básico de variável
payloads:
{{ 7*7 }}

O que observar:

  1. Teste de variável conhecida
payloads:
{{ page.title }}

O que observar:

  1. Teste de filtro
payloads:
{{ "abc" | upcase }}
{{ [1,2,3] | size }}

O que observar:

  1. Teste de bloco condicional
payloads:
{% if true %}YES{% endif %}

O que observar:

  1. Teste de loop
payloads:
{% for i in (1..2) %}{{ i }}{% endfor %}

O que observar:

  1. Teste de comentário
payloads:
{% comment %} comentário {% endcomment %}

O que observar:

Checklist rápido (passo-a-passo)

Go

Go Templates — text/template & html/template

Indicadores fortes de Go Templates

  1. Sintaxe característica com delimitadores {{ ... }} para variáveis, funções e blocos de controle.
  2. Arquivos .tmpl, .gohtml, .html ou diretórios templates/ usados em projetos Go.
  3. Stack traces ou erros Go com referências a text/template.Template, html/template.Template, Execute, ExecuteTemplate.
  4. Uso de funções internas de Go Templates (len, index, printf, html, urlquery).
  5. Controle de fluxo: {{ if ... }}, {{ range ... }}, {{ with ... }}, {{ end }}.

Testes inócuos

  1. Teste básico de variável
payloads:
{{ 7*7 }}

O que observar:

  1. Teste de variável conhecida
payloads:
{{ .Title }}

O que observar:

  1. Teste de bloco condicional
payloads:
{{ if true }}YES{{ end }}

O que observar:

  1. Teste de loop / range
payloads:
{{ range $i, $v := .Items }}{{ $v }}{{ end }}

O que observar:

  1. Teste de comentário
payloads:
{{/* comentário */}}

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
{{ .UnknownVar }}

O que observar:

Checklist rápido (passo-a-passo)

.NET

Razor

Indicadores fortes de Razor

  1. Sintaxe característica com @ para variáveis, expressões e blocos de código (@{ ... }).
  2. Arquivos .cshtml ou .vbhtml em diretórios Views/ de projetos ASP.NET ou ASP.NET Core.
  3. Stack traces ou erros .NET com referências a Microsoft.AspNetCore.Mvc.Razor.RazorViewEngine, RazorPage, ou RazorCompilation.
  4. Cabeçalhos típicos de servidores ASP.NET (Server: Kestrel, Server: IIS) combinados com templates Razor.
  5. Diretivas Razor específicas: @if, @for, @foreach, @section, @Html.Partial, @RenderBody, @model.

Testes inócuos

  1. Teste básico de variável / expressão
payloads:
@DateTime.Now.Year

O que observar:

  1. Teste de bloco de código
payloads:
@{
    var x = 1;
}
HELLO

O que observar:

  1. Teste de diretiva condicional
payloads:
@if(true){
    <text>YES</text>
}

O que observar:

  1. Teste de loop
payloads:
@for(int i = 1; i <= 2; i++){
    @i
}

O que observar:

  1. Teste de comentário
payloads:
@* comentário *@

O que observar:

  1. Provocar erro leve (com cuidado)
payloads:
@unknownVar

O que observar:

Checklist rápido (passo-a-passo)