Resumo:
- O endpoint discovery combina técnicas passivas e ativas para mapear rotas esquecidas, APIs expostas e painéis administrativos desprotegidos.
- O recon passivo utiliza Google Dorks, Wayback Machine, Certificate Transparency, Shodan e Censys para mapear a superfície de ataque sem enviar requisições diretas ao servidor.
- O JavaScript de aplicações React, Vue, Angular e Next.js revela endpoints ocultos, chamadas de API e, quando source maps estão expostos, o código-fonte original da aplicação.
- O fuzzing ativo com ffuf e feroxbuster é mais eficaz quando combinado com wordlists construídas a partir dos dados coletados sobre o próprio alvo.
- Cada framework tem estruturas de URL previsíveis e pontos de exposição específicos que tornam o recon mais direcionado.
- Todo o processo pode ser automatizado em um pipeline de shell script, acompanhado de um checklist para garantir cobertura sistemática em cada engajamento.
Antes de explorar qualquer vulnerabilidade, o pentester precisa mapear o que existe no ambiente-alvo, incluindo endpoints não documentados, rotas administrativas ocultas, arquivos de configuração e APIs internas expostas. Este guia aborda todas essas técnicas, apresentando ferramentas e exemplos prontos para uso.
Por que endpoint discovery é a etapa mais crítica do pentest
Aplicações modernas acumulam endpoints ao longo de anos de desenvolvimento: rotas criadas para debug e nunca removidas, APIs internas expostas por acidente, painéis administrativos protegidos apenas por URL obscura, versões antigas de endpoints ainda funcionais e esquecidas.
O processo de descoberta de endpoints, chamado de endpoint discovery ou recon de superfície de ataque, combina técnicas passivas (sem tocar o servidor) e ativas (enviando requisições) para mapear tudo o que pode ser acessado.
Fase 1: Reconhecimento passivo
O recon passivo coleta informações sem enviar requisições diretas ao alvo. É a fase mais segura juridicamente e, frequentemente, a mais produtiva porque explora dados já públicos.
Google Dorks: usando o próprio Google como scanner
O Google indexa arquivos e páginas que deveriam estar restritos. Com operadores de busca avançados, como o Google Dorks, é possível encontrar endpoints expostos, arquivos de configuração, painel de admin e documentação de API diretamente no índice do Google, sem tocar no servidor do alvo.
Encontrar arquivos de configuracao de API expostos
site:alvo.com filetype:json "api_key" OR "apikey" OR "secret"
site:alvo.com filetype:yaml OR filetype:yml "swagger" OR "openapi"
site:alvo.com filetype:json "endpoints" OR "routes" OR "paths"
# Encontrar painéis de administração e login site:alvo.com inurl:admin OR inurl:administrator OR inurl:panel site:alvo.com inurl:login OR inurl:signin OR inurl:dashboard site:alvo.com inurl:manage OR inurl:management OR inurl:backend site:alvo.com intitle:"admin" OR intitle:"dashboard" OR intitle:"control panel"
# Encontrar documentação de API e Swagger site:alvo.com inurl:swagger OR inurl:api-docs OR inurl:redoc site:alvo.com inurl:openapi.json OR inurl:swagger.json OR inurl:api/v1 site:alvo.com filetype:yaml "swagger: '2.0'" OR "openapi: '3.0'"
# Encontrar arquivos de ambiente e configuração site:alvo.com filetype:env OR filetype:config OR filetype:cfg site:alvo.com inurl:.git OR inurl:/.env OR inurl:config.php
# Encontrar subdomínios e subdiretórios site:*.alvo.com -www # todos os subdomains site:alvo.com inurl:/api/ OR inurl:/v1/ OR inurl:/v2/
# Encontrar backups e arquivos expostos site:alvo.com filetype:bak OR filetype:old OR filetype:backup site:alvo.com filetype:sql OR filetype:db OR filetype:sqlite
Wayback Machine: endpoints que existiram e ainda funcionam
O Internet Archive armazena snapshots históricos de aplicações web. Endpoints removidos da interface pública frequentemente continuam funcionando no servidor. A ferramenta waybackurls automatiza a extração de todas as URLs históricas de um domínio.
// Shell # Instalar waybackurls go install github.com/tomnomnom/waybackurls@latest
# Extrair todas as URLs historicas do dominio waybackurls alvo.com | tee wayback_urls.txt
# Filtrar por extensao ou padrao waybackurls alvo.com | grep -E "\.(php|asp|aspx|jsp|json|xml|env|bak|sql)" > endpoints_ext.txt waybackurls alvo.com | grep -E "/api/|/v[0-9]+/|/admin|/internal" > endpoints_api.txt
# Remover duplicatas e ordenar waybackurls alvo.com | sort -u > unique_urls.txt
# Ferramenta alternativa: gau (Get All URLs) go install github.com/lc/gau/v2/cmd/gau@latest gau alvo.com --subs | tee gau_urls.txt
# Combinar waybackurls + gau para cobertura máxima cat <(waybackurls alvo.com) <(gau alvo.com) | sort -u > all_historical_urls.txt
# Testar quais ainda respondem (filtrar ativos) cat all_historical_urls.txt | httpx -silent -status-code -content-length -o active_endpoints.txt
Certificate Transparency: mapeando subdomains
Todo certificado TLS emitido para um domínio é registrado publicamente nos logs de Certificate Transparency. Isso cria um banco de dados auditável de subdomínios que pode revelar ambientes de staging, APIs internas, painel de admin e outros ativos não publicados.
// Shell # Consultar certificate transparency logs # Via curl diretamente na API do crt.sh curl -s "https://crt.sh/?q=%.alvo.com&output=json" | \ python3 -c "import json,sys; [print(e['name_value']) for e in json.load(sys.stdin)]" | \ sort -u | grep -v "^\*" > subdomains_cert.txt
# Ferramenta subfinder (recon de subdomains multi-fonte) go install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest subfinder -d alvo.com -all -o subdomains.txt
# Ferramenta amass go install github.com/owasp-amass/amass/v4/...@master amass enum -passive -d alvo.com -o subdomains_amass.txt
# Verificar quais subdomains estão ativos cat subdomains.txt | httpx -silent -ports 80,443,8080,8443,3000,5000 \ -status-code -title -o live_subdomains.txt
# Procurar por subdomains reveladores grep -E "api|internal|admin|dev|staging|beta|test|qa|uat|old|legacy|backup" subdomains.txt
Shodan e Censys: o que está exposto na internet
// Shell / Shodan # Shodan: buscar por organização, ASN ou hostname shodan search "hostname:alvo.com" --fields ip_str,port,hostnames,org,http.title shodan search "org:Empresa Alvo" --fields ip_str,port,http.title shodan search "ssl.cert.subject.cn:*.alvo.com"
# Dorks Shodan para encontrar serviços específicos # Painel de administração expostos shodan search 'http.title:"admin" hostname:alvo.com' # APIs com documentação Swagger shodan search 'http.html:"swagger" hostname:alvo.com' # Servidores de CI/CD expostos shodan search 'product:"Jenkins" hostname:alvo.com' # Bancos de dados sem autenticação shodan search 'product:"Elasticsearch" hostname:alvo.com port:9200'
# Censys (alternativa ao Shodan com maior cobertura de TLS) # Via API: curl -u "API_ID:API_SECRET" \ "https://search.censys.io/api/v2/hosts/search?q=services.tls.certificates.leaf_data.names%3Aalvo.com" | \ python3 -m json.tool
Fase 2: Análise de JavaScript (a técnica mais produtiva)
Aplicações React, Vue, Angular e Next.js compilam todo o código do frontend em bundles JavaScript que ficam publicamente acessíveis. Esses bundles frequentemente contêm rotas completas da aplicação, endpoints de API hardcoded, chaves de API, tokens de acesso, URLs de ambientes internos e lógica de negócio.
Por que funciona tão bem
Bundlers como Webpack, Vite e esbuild unem e modificam o código, mas não o criptografam. Todo endpoint definido no frontend, toda string de URL, todo fetch() e axios.get() está disponível para quem souber onde procurar.
Localizando e baixando os bundles JS
// Shell # Passo 1: Identificar os arquivos JS do bundle # Abrir DevTools > Network > filtrar por JS > recarregar a página # Os arquivos principais geralmente seguem o padrão: # main.[hash].js, chunk.[hash].js, app.[hash].js
# Passo 2: Baixar todos os JS do domínio automaticamente # Ferramenta: getJS go install github.com/003random/getJS@latest getJS --url https://alvo.com --complete --output js_files.txt
# Baixar com wget recursivo (apenas JS) wget -r -l 2 -A "*.js" --no-parent https://alvo.com -P ./js_dump/
# Via Burp Suite: navegar a aplicação, depois: # Target > Site Map > filtrar por .js > botão direito > Save all responses
# Passo 3: Verificar todos os JS via curl
cat js_files.txt | xargs -I {} curl -s {} >> all_js_content.txt
LinkFinder: extraindo endpoints de JS automaticamente
// Shell / LinkFinder # Instalar LinkFinder git clone https://github.com/GerbenJavado/LinkFinder.git cd LinkFinder && pip3 install -r requirements.txt
# Analisar arquivo JS local python3 linkfinder.py -i main.abc123.js -o results.html
# Analisar URL diretamente python3 linkfinder.py -i https://alvo.com/static/js/main.abc123.js -o cli
# Analisar o domínio inteiro (crawl + análise de JS) python3 linkfinder.py -i https://alvo.com -d -o results.html
# Filtrar apenas endpoints de API python3 linkfinder.py -i https://alvo.com -d -o cli | grep -E "/api/|/v[0-9]+/"
# Ferramenta alternativa: subjs
go install github.com/lc/subjs@latest
echo "https://alvo.com" | subjs | xargs -I {} python3 linkfinder.py -i {} -o cli
Source Maps: o código-fonte original exposto
Source maps (arquivos .js.map) são gerados durante o build para facilitar o debug em produção. Quando deixados publicamente acessíveis, eles expõem o código-fonte original, antes da minificação, com nomes de variáveis, funções, rotas e comentários do desenvolvedor.
// Shell / Go # Verificar se source maps estão expostos # Os source maps ficam no mesmo path do JS, com extensão .map curl -I https://alvo.com/static/js/main.abc123.js.map
# Automatizar a verificação para todos os JS encontrados
cat js_files.txt | while read url; do
map_url="${url}.map"
status=$(curl -s -o /dev/null -w "%{http_code}" "$map_url")
if [ "$status" = "200" ]; then
echo "[!] Source map exposto: $map_url"
fi
done
# Baixar e reconstruir o código original # Ferramenta: sourcemapper go install github.com/denandz/sourcemapper@latest sourcemapper -url https://alvo.com/static/js/main.abc123.js.map -output ./src_reconstructed/
# Ferramenta alternativa: source-map-explorer npm install -g source-map-explorer source-map-explorer main.abc123.js main.abc123.js.map
# Ferramenta: unwebpack (reconstrução de bundles Webpack) npm install -g unwebpack-sourcemap unwebpack-sourcemap main.abc123.js.map ./output/
Regex para minerar endpoints de JS minificado
Quando source maps não estão disponíveis, é possível minerar o JS minificado diretamente com regex. O objetivo é extrair strings que parecem paths de API:
// Shell / Python # Regex para extrair paths de API do JS minificado # Executar no conteúdo de todos os JS baixados
# Padrão 1: strings que parecem paths de API
grep -oE '"/[a-zA-Z0-9_/-]{2,50}"' all_js_content.txt | sort -u
# Padrão 2: URLs completas de API grep -oE 'https?://[a-zA-Z0-9./_-]+/api/[a-zA-Z0-9._/-]+' all_js_content.txt | sort -u
# Padrão 3: paths começando com /api/, /v1/, /v2/ etc. grep -oE '"(/api/|/v[0-9]+/|/graphql|/rest/|/rpc/)[^"]*"' all_js_content.txt | sort -u
# Padrão 4: fetch() e axios calls grep -oE "fetch(['"][^'"]+['"]" all_js_content.txt | grep -oE "['"][^'"]+['"]" grep -oE "axios.(get|post|put|delete|patch)(['"][^'"]+['"]" all_js_content.txt
# Padrão 5: constantes de URL (comuns em React/Vue) grep -oE "API_URLs*=s*['"][^'"]+['"]" all_js_content.txt grep -oE "BASE_URLs*=s*['"][^'"]+['"]" all_js_content.txt grep -oE "ENDPOINTs*=s*['"][^'"]+['"]" all_js_content.txt
# Script Python completo para minerar endpoints python3 - <<'EOF' import re, sys, glob
patterns = [
r'["'](/api/[^"'\s]{1,100})["']',
r'["'](/vd+/[^"'\s]{1,100})["']',
r'fetch(["'](https?://[^"']+)["']',
r'axios.w+(["'](https?://[^"']+)["']',
r'["'](https://[a-z0-9-]+.alvo.com/[^"'\s]{1,200})["']'
]
found = set()
for f in glob.glob('./js_dump/**/*.js', recursive=True):
content = open(f, errors='ignore').read()
for pat in patterns:
for m in re.findall(pat, content):
found.add(m)
for url in sorted(found): print(url) EOF
Variáveis de ambiente e segredos no JavaScript
Frameworks modernos (React, Vue, Next.js) injetam variáveis de ambiente no bundle JavaScript no momento do build. Qualquer variável prefixada com REACT_APP_NEXT_PUBLIC_VITE_ ou similar fica disponível publicamente no bundle. Isso frequentemente expõe chaves de API, URLs de serviços internos e tokens:
// Shell # Buscar variáveis de ambiente expostas no JS grep -oE "REACT_APP_[A-Z_]+=\s*['"][^'"]+['"]" all_js_content.txt grep -oE "NEXT_PUBLIC_[A-Z_]+=\s*['"][^'"]+['"]" all_js_content.txt grep -oE "VITE_[A-Z_]+\s*:\s*['"][^'"]+['"]" all_js_content.txt
# Buscar chaves e tokens diretamente
grep -oE "(api_key|apikey|api-key|secret|token|password|auth)["'\s]*[:=]["'\s]*[a-zA-Z0-9_\-]{10,}" all_js_content.txt -i
# Buscar URLs de serviços internos grep -oE "https?://[a-z0-9.-]+\.(internal|local|corp|intranet|private)[^"'\s]*" all_js_content.txt grep -oE "https?://(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)[^"'\s]+" all_js_content.txt
# Ferramenta: SecretFinder (especializada em segredos em JS) git clone https://github.com/m4ll0k/SecretFinder.git python3 SecretFinder.py -i https://alvo.com -e -o results.html python3 SecretFinder.py -i main.abc123.js -o cli
Fase 3: Fuzzing ativo de diretórios e endpoints
Com as informações do recon passivo e da análise de JS, parte-se para a enumeração ativa: enviar requisições ao servidor tentando descobrir paths que existem, mas não foram encontradas ainda. As ferramentas principais são ffuf, feroxbuster e dirsearch.
ffuf: o padrão da indústria
// Shell / ffuf # Instalação go install github.com/ffuf/ffuf/v2@latest
# Fuzzing básico de diretórios ffuf -u https://alvo.com/FUZZ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \ -mc 200,201,301,302,401,403 -o ffuf_results.json -of json
# Fuzzing de endpoints de API com extensões ffuf -u https://alvo.com/api/FUZZ -w wordlist.txt \ -e .json,.php,.asp,.aspx,.xml -mc all -fc 404 -o api_results.json
# Fuzzing com headers de autenticação ffuf -u https://alvo.com/FUZZ -w wordlist.txt \ -H "Authorization: Bearer eyJhbGc..." -H "Cookie: session=abc123" \ -mc all -fc 404
# Fuzzing recursivo (entra em subdiretórios encontrados) ffuf -u https://alvo.com/FUZZ -w wordlist.txt -recursion -recursion-depth 3 \ -mc 200,301,302,401,403 -o recursive_results.json -of json
# Fuzzing de parâmetros em endpoints conhecidos ffuf -u https://alvo.com/api/users?FUZZ=1 -w params_wordlist.txt \ -mc all -fc 400,404 -fs 0
# Filtros úteis para reduzir falsos positivos # -fc: filtrar por status code # -fs: filtrar por tamanho de resposta (em bytes) # -fw: filtrar por número de palavras # -fl: filtrar por número de linhas # -fr: filtrar por regex no corpo da resposta
# Rate limiting para evitar detecção ffuf -u https://alvo.com/FUZZ -w wordlist.txt -p 0.1 -t 5 # 0.1s de delay, 5 threads
Wordlists: o que usar para cada contexto
| Contexto | Wordlist recomendada | Onde Obter |
| Diretórios gerais | directory-list-2.3-medium.txt | SecLists / dirbuster |
| Endpoints de API REST | api/api-endpoints.txt | SecLists/Discovery/Web-Content |
| Arquivos comuns | raft-medium-files.txt | SecLists/Discovery/Web-Content |
| Parâmetros de query | burp-parameter-names.txt | SecLists/Discovery/Web-Content |
| Subdomains | subdomains-top1million-5000.txt | SecLists/Discovery/DNS |
| Paths de admin | AdminPanels.fuzz.txt | SecLists/Discovery/Web-Content |
| Nomes de backup | backup-filenames.txt | SecLists/Discovery/Web-Content |
| Rotas Next.js/Nuxt | next-js-routes.txt | kaeferjaeger.gay wordlists |
| APIs comuns (custom) | Construir com waybackurls + JS mining | Processo descrito neste artigo |
Construindo wordlists customizadas a partir do alvo
A wordlist mais eficaz é a construída especificamente para o alvo, combinando dados do waybackurls, da análise de JS e de dicionários genéricos:
// Shell / Bash #!/usr/bin/env bash # Gerar wordlist customizada para o alvo
TARGET="alvo.com"
echo "[*] Coletando URLs históricas..." waybackurls $TARGET | grep -oE "(/[a-zA-Z0-9_/-]+)" | sort -u > wordlist_wayback.txt
echo "[*] Extraindo paths do JavaScript..."
getJS --url https://$TARGET --complete | xargs -I {} \
python3 linkfinder.py -i {} -o cli 2>/dev/null | \
grep -oE "(/[a-zA-Z0-9_/-]{2,50})" | sort -u > wordlist_js.txt
echo "[*] Combinando com wordlists genéricas..." cat wordlist_wayback.txt wordlist_js.txt \ /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt \ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt | \ sort -u | \ grep -v "#" | \ awk 'length > 2 && length < 60' > wordlist_custom.txt
echo "[*] Total de entries: $(wc -l < wordlist_custom.txt)" echo "[*] Iniciando fuzzing com wordlist customizada..." ffuf -u https://$TARGET/FUZZ -w wordlist_custom.txt -mc all -fc 404 -o results.json -of json
feroxbuster: fuzzing recursivo e rápido
// Shell / feroxbuster # Instalação cargo install feroxbuster # ou: apt install feroxbuster
# Fuzzing recursivo com detecção automática de subdiretórios feroxbuster --url https://alvo.com --wordlist wordlist.txt \ --depth 3 --threads 50 --output results.txt
# Com headers de autenticação feroxbuster --url https://alvo.com \ --wordlist wordlist.txt \ --headers "Authorization: Bearer TOKEN" \ --headers "Cookie: session=abc" \ --status-codes 200,201,301,302,401,403
# Fuzzing de extensões específicas feroxbuster --url https://alvo.com \ --wordlist wordlist.txt \ --extensions php,asp,aspx,json,xml,bak,env \ --filter-status 404,429
# Modo silencioso com output formatado feroxbuster --url https://alvo.com \ --wordlist wordlist.txt \ --quiet \ --output ferox_results.txt
// Shell / feroxbuster # Instalação cargo install feroxbuster # ou: apt install feroxbuster
# Fuzzing recursivo com detecção automática de subdiretórios feroxbuster --url https://alvo.com --wordlist wordlist.txt \ --depth 3 --threads 50 --output results.txt
# Com headers de autenticação feroxbuster --url https://alvo.com \ --wordlist wordlist.txt \ --headers "Authorization: Bearer TOKEN" \ --headers "Cookie: session=abc" \ --status-codes 200,201,301,302,401,403
# Fuzzing de extensões específicas feroxbuster --url https://alvo.com \ --wordlist wordlist.txt \ --extensions php,asp,aspx,json,xml,bak,env \ --filter-status 404,429
# Modo silencioso com output formatado feroxbuster --url https://alvo.com \ --wordlist wordlist.txt \ --quiet \ --output ferox_results.txt
Fase 4: Tricks específicos por framework
Cada framework tem estruturas de URL previsíveis, arquivos de configuração próprios e pontos de exposição específicos. Conhecer essas características multiplica a eficácia do recon.
Next.js
Next.js é o framework React mais popular para produção. Ele gera rotas de API em /api/ e expõe estruturas previsíveis que podem ser exploradas:
// Shell / Next.js # Estrutura de rotas Next.js: # /api/* -> API Routes (server-side, podem ter qualquer lógica) # /_next/static/ -> assets estáticos (JS chunks, CSS) # /_next/data/ -> dados pré-renderizados (JSON com dados da página) # /api/auth/* -> autenticação (NextAuth.js)
# Encontrar dados pré-renderizados (/_next/data/) # Esses endpoints retornam o JSON usado pelo getServerSideProps/getStaticProps # Formato: /_next/data/[build_id]/[pagina].json
# Passo 1: descobrir o build ID (presente em qualquer JS do bundle) curl -s https://alvo.com | grep -oE '"buildId":"[^"]+"' # Resultado: "buildId":"abc123xyz"
# Passo 2: acessar dados pré-renderizados de páginas conhecidas curl https://alvo.com/_next/data/abc123xyz/index.json curl https://alvo.com/_next/data/abc123xyz/dashboard.json curl https://alvo.com/_next/data/abc123xyz/admin.json curl https://alvo.com/_next/data/abc123xyz/profile.json curl https://alvo.com/_next/data/abc123xyz/users.json
# Fuzzing de /_next/data/ com páginas comuns ffuf -u "https://alvo.com/_next/data/abc123xyz/FUZZ.json" \ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt \ -mc 200
# Encontrar todas as API Routes via análise do bundle JS grep -oE '"/api/[a-zA-Z0-9/_-]+"' *.js | sort -u
# Verificar NextAuth endpoints (autenticação) curl https://alvo.com/api/auth/providers curl https://alvo.com/api/auth/session curl https://alvo.com/api/auth/csrf
# Encontrar rotas dinâmicas expostas # [id].js -> /api/users/1, /api/users/2... # [...slug].js -> captura qualquer path
# Wordlist específica para Next.js API Routes ffuf -u https://alvo.com/api/FUZZ \ -w next-js-api-routes.txt -mc all -fc 404
React (SPA) e Vue
// Shell / JS # React/Vue SPA: toda a lógica de roteamento está no JS # O servidor serve index.html para qualquer rota (client-side routing)
# Passo 1: extrair rotas definidas no React Router / Vue Router grep -oE 'path:s*["'][^"']+["']' *.js | sort -u grep -oE '<Routes+path=["'][^"']+["']' *.js | sort -u
# Padrão de rotas no React Router v6 grep -oE '"s*/[a-zA-Z0-9/:_-]+"' main.*.js | grep -E "^"/" | sort -u
# Passo 2: mapear chamadas de API no código grep -oE 'axios.(get|post|put|delete)(["'][^"']+["']' *.js | sort -u grep -oE 'fetch(["'][^"']+["']' *.js | sort -u grep -oE 'api.[a-zA-Z]+(["'][^"']+["']' *.js | sort -u
# Passo 3: encontrar a URL base da API (environment variable) grep -oE "process.env.[A-Z_]+" *.js | sort -u grep -oE "import.meta.env.[A-Z_]+" *.js | sort -u
# Vue Router: extrair rotas do bundle grep -oE 'path:s*"[^"]+"' *.js | grep -oE '"[^"]+"' | sort -u
# Vuex/Pinia store actions: revelar nomes de endpoints
grep -oE "actionss*:s*{[^}]+}" *.js
Angular
// Shell / Angular # Angular: rotas definidas nos módulos de roteamento # Buscar no bundle por RouterModule e definições de rotas
grep -oE 'path:s*["'][^"']*["']' main.*.js | sort -u
grep -oE '{path:["'][^"']*["']' main.*.js | sort -u
# Services Angular: onde as chamadas HTTP estão definidas
# Buscar por HttpClient calls
grep -oE 'this.http.(get|post|put|delete)(["'][^"']+["']' *.js | sort -u
grep -oE '"[^"]*"[,s]*{[^}]*responseType' *.js | sort -u
# Angular environment files (prodution vs development) # environment.ts / environment.prod.ts sao compilados no bundle grep -oE 'apiUrls*:s*["'][^"']+["']' *.js | sort -u grep -oE 'baseUrls*:s*["'][^"']+["']' *.js | sort -u
# Lazy-loaded modules: o Angular carrega chunks separados # Verificar o manifesto de chunks para descobrir módulos ocultos curl https://alvo.com/main.*.js | grep -oE 'chunk-[a-zA-Z0-9]+.js'
Laravel, Django, Rails: endpoints do backend
// Shell / Frameworks # Laravel: estrutura previsível # /api/ -> rotas de API definidas em routes/api.php # /_debugbar -> laravel debugbar (em modo dev) # /telescope -> laravel telescope (monitoramento) # /horizon -> laravel horizon (queue monitoring) # /nova -> laravel nova (admin panel) # /sanctum/csrf-cookie -> autenticação Sanctum
# Verificar exposição de ferramentas Laravel curl -s https://alvo.com/_debugbar/open | head -c 200 curl -s https://alvo.com/telescope/api/requests curl -s https://alvo.com/horizon/api/stats curl -s https://alvo.com/nova
# Django: estrutura previsível # /admin/ -> Django Admin (interface de admin padrão) # /api/ -> Django REST Framework # /api/v1/, /api/v2/ -> endpoints versionados # /django-admin/ -> admin em path alternativo # /static/ -> arquivos estáticos # /__debug__/ -> django-debug-toolbar
# Verificar Django Admin e debug curl -s https://alvo.com/admin/ curl -s https://alvo.com/__debug__/ curl -s https://alvo.com/api/schema/ # DRF schema autogenerado curl -s https://alvo.com/api/docs/ # DRF browsable API
# Ruby on Rails: estrutura previsível # /rails/info -> informações do framework (em dev) # /rails/mailers -> previa de emails # /admin -> activeadmin # GET /api/v1/users.json -> padrão Rails de formato JSON
# Verificar exposição Rails curl -s https://alvo.com/rails/info/properties curl -s https://alvo.com/rails/info/routes
GraphQL, Swagger e documentação de API exposta
// Shell / Bash
# GraphQL: endpoints comuns
endpoints_graphql=(
"/graphql" "/graphiql" "/playground"
"/api/graphql" "/v1/graphql" "/v2/graphql"
"/graphql/console" "/graphql/playground"
)
for ep in "${endpoints_graphql[@]}"; do
status=$(curl -s -o /dev/null -w "%{http_code}" "https://alvo.com$ep")
echo "$status -> https://alvo.com$ep"
done
# Swagger / OpenAPI: documentação completa da API exposta
endpoints_swagger=(
"/swagger" "/swagger-ui" "/swagger-ui.html"
"/api-docs" "/api/docs" "/docs"
"/openapi.json" "/swagger.json"
"/v1/swagger.json" "/v2/swagger.json"
"/api/openapi" "/redoc" "/rapidoc"
"/swagger/index.html" "/api/swagger"
)
for ep in "${endpoints_swagger[@]}"; do
status=$(curl -s -o /dev/null -w "%{http_code}" "https://alvo.com$ep")
[ "$status" != "404" ] && echo "[!] $status -> https://alvo.com$ep"
done
# Extrair todos os endpoints do Swagger encontrado
# Ferramenta: swagger-parser
curl -s https://alvo.com/openapi.json | python3 - <<'EOF'
import json, sys
data = json.load(sys.stdin)
base = data.get("servers", [{}])[0].get("url", "")
for path, methods in data.get("paths", {}).items():
for method in methods:
print(f"{method.upper()} {base}{path}")
EOF
Fase 5: Infraestrutura, subdomains e ambientes esquecidos
Descoberta e enumeração de subdomínios
// Shell # Pipeline completo de subdomain discovery
# 1. Passive (sem tocar o alvo) subfinder -d alvo.com -all -silent > passive_subs.txt amass enum -passive -d alvo.com >> passive_subs.txt curl -s "https://crt.sh/?q=%.alvo.com&output=json" | \ python3 -c "import json,sys; [print(e['name_value']) for e in json.load(sys.stdin)]" >> passive_subs.txt
# 2. Brute force DNS (ativo) ffuf -u "https://FUZZ.alvo.com" \ -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \ -mc 200,301,302,401,403 -H "Host: FUZZ.alvo.com"
# ferramenta alternativa com DNS brute force dnsx -d alvo.com -w subdomains-wordlist.txt -silent -a -cname >> passive_subs.txt
# 3. Limpar e deduplicar sort -u passive_subs.txt | grep -v "^*" > all_subs.txt
# 4. Verificar quais estão ativos cat all_subs.txt | httpx -silent -status-code -title -tech-detect \ -ports 80,443,8080,8443,3000,4000,5000,8000,8888 \ -o live_subs.txt
# 5. Identificar subdomains interessantes grep -E "api|admin|internal|dev|staging|beta|test|qa|uat|old|legacy|backup|auth|login|dashboard|manage" live_subs.txt
Encontrar ambientes de staging e desenvolvimento
Ambientes de staging e desenvolvimento frequentemente têm autenticação mais fraca, dados reais e funcionalidades não disponíveis em produção. Eles são encontrados por convenção de nomes:
// Shell / Bash # Convenções de nome comuns para ambientes não-produção prefixos=(dev staging stage test qa uat beta alpha sandbox demo preview release canary pilot internal corp intranet) sufixos=(dev staging test qa uat)
# Testar combinações
for prefix in "${prefixos[@]}"; do
for domain in alvo.com api.alvo.com app.alvo.com; do
url="https://${prefix}.${domain}"
status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$url")
[ "$status" != "000" ] && echo "$status -> $url"
done
done
# Procurar no Wayback Machine por subdomínios históricos waybackurls alvo.com | grep -oE "https?://[a-zA-Z0-9.-]+.alvo.com" | sort -u
# Verificar registros DNS históricos via SecurityTrails curl -s "https://api.securitytrails.com/v1/domain/alvo.com/subdomains" \ -H "APIKEY: seu_token" | python3 -m json.tool
# Verificar CORS mal configurado (revela domínios internos confiados) curl -H "Origin: https://evil.com" -I https://alvo.com/api/users | grep -i "access-control" # Se retornar: Access-Control-Allow-Origin: * -> qualquer origem aceita
Arquivos de configuração e backups expostos
// Shell / Bash # Arquivos que não deveriam estar públicos mas frequentemente estão arquivos=( ".env" ".env.local" ".env.production" ".env.backup" ".git/config" ".git/HEAD" ".git/COMMIT_EDITMSG" "config.js" "config.json" "config.php" "config.yaml" "database.yml" "database.json" "db.json" "wp-config.php" "configuration.php" "settings.py" "docker-compose.yml" "docker-compose.yaml" "Dockerfile" ".dockerenv" "package.json" "composer.json" "Gemfile" "robots.txt" "sitemap.xml" "crossdomain.xml" "phpinfo.php" "info.php" "test.php" "debug.php" "backup.zip" "backup.tar.gz" "site.zip" "www.zip" ".DS_Store" "Thumbs.db" "desktop.ini" "readme.txt" "README.md" "CHANGELOG.md" "server-status" "server-info" )
for f in "${arquivos[@]}"; do
status=$(curl -s -o /dev/null -w "%{http_code}" "https://alvo.com/$f")
if [[ "$status" =~ ^(200|206)$ ]]; then
echo "[!] EXPOSTO $status -> https://alvo.com/$f"
fi
done
# Verificar exposição de repositório .git curl -s https://alvo.com/.git/config | head -20 # Se retornar configuração: repositório exposto
# Ferramenta: GitDumper (extrair repositório exposto) git clone https://github.com/internetwache/GitTools.git ./GitTools/Dumper/gitdumper.sh https://alvo.com/.git/ ./git_dump/ ./GitTools/Extractor/extractor.sh ./git_dump/ ./extracted_src/
Fase 6: Automatizando o pipeline completo de recon
Em engajamentos reais, o recon precisa ser sistemático e documentado. O pipeline abaixo combina todas as técnicas anteriores em um fluxo automatizado:
// Bash / Pipeline #!/usr/bin/env bash # endpoint_discovery.sh - Pipeline completo de recon # Uso: ./endpoint_discovery.sh alvo.com
TARGET="${1:?Usage: $0 <domain>}"
OUTPUT_DIR="./recon_${TARGET//[^a-zA-Z0-9]/_}"
mkdir -p $OUTPUT_DIR/{subdomains,js_files,endpoints,wordlists,active}
echo "[1/8] Subdomain discovery..." subfinder -d $TARGET -all -silent 2>/dev/null > $OUTPUT_DIR/subdomains/passive.txt curl -s "https://crt.sh/?q=%.$TARGET&output=json" 2>/dev/null | \ python3 -c "import json,sys; [print(e['name_value']) for e in json.load(sys.stdin)]" \ >> $OUTPUT_DIR/subdomains/passive.txt sort -u $OUTPUT_DIR/subdomains/passive.txt > $OUTPUT_DIR/subdomains/all.txt echo " Found $(wc -l < $OUTPUT_DIR/subdomains/all.txt) unique subdomains"
echo "[2/8] Probing live hosts..." cat $OUTPUT_DIR/subdomains/all.txt | httpx -silent -status-code -ports 80,443,8080,8443 \ -o $OUTPUT_DIR/subdomains/live.txt echo " Found $(wc -l < $OUTPUT_DIR/subdomains/live.txt) live hosts"
echo "[3/8] Historical URLs..." waybackurls $TARGET > $OUTPUT_DIR/endpoints/wayback.txt gau $TARGET --subs >> $OUTPUT_DIR/endpoints/wayback.txt sort -u $OUTPUT_DIR/endpoints/wayback.txt -o $OUTPUT_DIR/endpoints/wayback.txt echo " Found $(wc -l < $OUTPUT_DIR/endpoints/wayback.txt) historical URLs"
echo "[4/8] Collecting JavaScript files..." getJS --url https://$TARGET --complete 2>/dev/null > $OUTPUT_DIR/js_files/urls.txt echo " Found $(wc -l < $OUTPUT_DIR/js_files/urls.txt) JS files"
echo "[5/8] Analyzing JavaScript for endpoints..." cat $OUTPUT_DIR/js_files/urls.txt | while read url; do python3 ~/tools/LinkFinder/linkfinder.py -i "$url" -o cli 2>/dev/null done | sort -u > $OUTPUT_DIR/endpoints/from_js.txt echo " Extracted $(wc -l < $OUTPUT_DIR/endpoints/from_js.txt) endpoints from JS"
echo "[6/8] Checking for source maps..."
cat $OUTPUT_DIR/js_files/urls.txt | while read url; do
status=$(curl -s -o /dev/null -w "%{http_code}" "${url}.map")
[ "$status" = "200" ] && echo "[!] Source map exposed: ${url}.map"
done > $OUTPUT_DIR/js_files/sourcemaps.txt
echo "[7/8] Building custom wordlist..." cat $OUTPUT_DIR/endpoints/wayback.txt $OUTPUT_DIR/endpoints/from_js.txt | \ grep -oE "(/[a-zA-Z0-9_/-]+)" | sort -u > $OUTPUT_DIR/wordlists/custom.txt wc -l $OUTPUT_DIR/wordlists/custom.txt
echo "[8/8] Active fuzzing..." ffuf -u "https://$TARGET/FUZZ" \ -w $OUTPUT_DIR/wordlists/custom.txt \ -mc all -fc 404 -t 40 -p 0.05 \ -o $OUTPUT_DIR/active/ffuf_results.json -of json -silent
echo "[DONE] Results in $OUTPUT_DIR" echo "Summary:" echo " Subdomains: $(wc -l < $OUTPUT_DIR/subdomains/all.txt)" echo " Live hosts: $(wc -l < $OUTPUT_DIR/subdomains/live.txt)" echo " Historical URLs: $(wc -l < $OUTPUT_DIR/endpoints/wayback.txt)" echo " JS endpoints: $(wc -l < $OUTPUT_DIR/endpoints/from_js.txt)"
Arsenal de ferramentas: instalação e uso rápido
| Ferramentas | Instalação | Uso principal | Melhor para |
| ffuf | go install github.com/ffuf/ffuf/v2@latest | Fuzzing de diretórios e endpoints | Todos os contextos de fuzzing ativo |
| feroxbuster | cargo install feroxbuster | Fuzzing recursivo automatico | Sites com estrutura de subdiretórios |
| waybackurls | go install github.com/tomnomnom/waybackurls@latest | URLs históricas do Wayback Machine | Encontrar endpoints legados |
| gau | go install github.com/lc/gau/v2/cmd/gau@latest | URLs de múltiplas fontes históricas | Complementar ao waybackurls |
| subfinder | go install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest | Subdomains passivos multi-fonte | Mapeamento de superfície de ataque |
| httpx | go install github.com/projectdiscovery/httpx/cmd/httpx@latest | Probe de URLs e detecção de tecnologia | Verificar quais hosts estão ativos |
| LinkFinder | git clone + pip3 install -r requirements.txt | Extrair URLs de arquivos JS | Análise de bundles de SPA |
| getJS | go install github.com/003random/getJS@latest | Coletar todos os JS de um domínio | Primeira etapa de análise de JS |
| sourcemapper | go install github.com/denandz/sourcemapper@latest | Reconstruir código de source maps | Quando source maps estão expostos |
| SecretFinder | git clone + pip3 install -r requirements.txt | Encontrar segredos em JS | Chaves, tokens, variáveis sensíveis |
| amass | go install github.com/owasp-amass/amass/v4/…@master | Enumeração avançada de subdomains | Mapeamento profundo de infraestrutura |
| dnsx | go install github.com/projectdiscovery/dnsx/cmd/dnsx@latest | Resolução e brute force DNS | Validar subdomínios e encontrar novos |
Checklist de endpoint discovery por engajamento
Recon passivo
- Google Dorks executados: arquivos, painel admin, documentação de API, env files.
- waybackurls + gau executados e URLs históricas filtradas por extensão e path.
- Certificate Transparency consultado (crt.sh + subfinder).
- Shodan/Censys consultados para o ASN/org do alvo.
- txt e sitemap.xml verificados.
Análise de JavaScript
- Todos os arquivos JS identificados e baixados via getJS.
- LinkFinder executado em cada JS para extrair endpoints.
- Source maps verificados para cada JS: se expostos, código original reconstruído.
- Regex executada no JS minificado para extrair paths e URLs.
- Variáveis de ambiente e segredos buscados no bundle.
- Rotas do framework identificadas (React Router, Vue Router, Next.js API Routes).
- Chamadas de API extraídas (fetch, axios, HttpClient).
Enumeração ativa
- ffuf/feroxbuster executados com wordlist genérica.
- Wordlist customizada construída combinando wayback + JS mining + genérica.
- Fuzzing recursivo realizado para subdiretórios encontrados.
- Extensões relevantes para o stack testadas (.php, .asp, .json, .bak, .env).
- Endpoints de API fuzzados (ffuf -u /api/FUZZ com wordlist de API).
Infraestrutura e subdomains
- Subdomínios passivos coletados (subfinder + amass + crt.sh).
- Força bruta DNS realizada com wordlist de subdomínios.
- Subdomínios ativos identificados via httpx com detecção de tecnologia.
- Subdomínios de staging/dev procurados por convenção de nomes.
- Arquivos de configuração e backup comuns verificados em todos os hosts.
- Repositório .git verificado: se exposto, conteúdo extraído.
Específicos de framework
- js: buildId extraído, /_next/data/ fuzzado, /api/* mapeado.
- GraphQL: endpoints comuns testados, introspecção verificada.
- Swagger/OpenAPI: documentação exposta procurada e endpoints extraídos.
- Django/Laravel: /admin/, /telescope/, /horizon/ verificados.
- CORS: política verificada para origens aceitas.
O Endpoint Discovery é a combinação de recon passivo (Wayback, Google Dorks, CT logs), análise profunda de JavaScript (bundles, source maps, extração de rotas) e fuzzing ativo estruturado com wordlists customizadas para o alvo.
Em aplicações modernas baseadas em React, Vue, Angular ou Next.js, o JavaScript do frontend é frequentemente a fonte mais rica de informação sobre a arquitetura do sistema. Source maps expostos entregam código-fonte completo. Variáveis de ambiente no bundle entregam chaves e URLs internas. Rotas definidas no client-side router entregam o mapa completo da aplicação.
Na Vantico, a fase de recon é tratada como parte crítica de cada engajamento. A qualidade do recon determina a qualidade das descobertas: endpoints encontrados apenas via análise de JavaScript ou source maps expostos frequentemente levam aos achados de maior severidade, justamente porque nunca foram submetidos a revisão de segurança.
A plataforma da Vantico permite registrar e organizar cada endpoint descoberto com o método de discovery, evidência e status de teste, mantendo rastreabilidade completa entre o recon e os achados finais.
Conheça outros artigos técnicos da Vantico clicando aqui.
Siga a Vantico nas redes sociais e fique informado sobre tecnologia e insights da área.