(SRDomain) Simple Recon - Domain v2.0.0

Ferramenta de enumeração passiva e ativa de domínios para workflows de OSINT e reconhecimento.

Simple Recon URL

Construída em Python assíncrono - consulta 50 fontes (39 passivas + 11 ativas) em paralelo, sem dependências externas de shell.

Técnicas inspiradas em subfinder, amass, puredns e subjack: detecção de wildcard por multi-probe, zone walking DNSSEC NSEC, extração de SAN em certificados TLS, mineração de registros SRV, validação em duas passagens com resolvedores confiáveis, enumeração recursiva, crawling HTML/JS com mineração de sourcemap, mineração de registros CAA, coleta de robots.txt/sitemap, força bruta de NS secundário, varredura PTR baseada em ASN, detecção de IP/provedor cloud, aprendizado de wordlist a partir de nomes descobertos e fingerprinting de takeover de subdomínio.

Autor:    Cleiton Pinheiro (a.k.a MrCl0wn)
Blog:     https://blog.mrcl0wn.com
GitHub:   https://github.com/MrCl0wnLab
Twitter:  https://twitter.com/MrCl0wnLab

[!CAUTION] Aviso legal: o uso do SimpleReconDomain para atacar alvos sem consentimento mútuo prévio é ilegal. É de responsabilidade do usuário final obedecer a todas as leis locais, estaduais e federais aplicáveis. Os desenvolvedores não assumem qualquer responsabilidade e não se responsabilizam por qualquer uso indevido ou dano causado por este programa.

Sumário


Instalação

git clone https://github.com/osintbrazuca/SimpleReconDomain
cd SimpleReconDomain
pip install -r requirements.txt

Dependências (requirements.txt):

Pacote Finalidade
httpx[socks] Cliente HTTP assíncrono para todas as fontes passivas e download de listas de resolvedores via URL ([socks] habilita --proxy socks5://)
aiodns Resolvedor DNS assíncrono para força bruta e validação
dnspython Transferência de zona (AXFR), mineração de registros DNS, zone walking NSEC, enumeração SRV
beautifulsoup4 Parsing de HTML para a fonte ativa spider (extração de <a>, <link>, <script>)

Docker

Execute sem uma instalação Python local. Dois caminhos de build, a mesma imagem - depois basta passar os argumentos de CLI diretamente:

docker build -t docker/simplerecon -f docker/Dockerfile .

# B) straight from GitHub — no local checkout needed
docker build -t docker/simplerecon - < docker/Dockerfile.remote

# run (args after the image name go to simplerecon.py)
docker run --rm docker/simplerecon -d target.com

Veja docker/README.md para todas as opções de build (incluindo o one-liner curl | docker build - e --build-arg REF=), persistência de resultados (volume --db), o log de comandos / registro --watch, montagem de chaves de API e execução do scheduler.


Chaves de API

As chaves de API são armazenadas em config/api_keys.json (ignorado pelo git para evitar vazamentos).

{
    "alienvault_otx":        "",
    "hackertarget":          "",
    "urlscan":               "",
    "virustotal":            "",
    "securitytrails":        "",
    "shodan":                "",
    "github_token":          "",
    "censys_id":             "",
    "censys_secret":         "",
    "grayhatwarfare_token":  "",
    "leakix_token":          "",
    "fullhunt_token":        "",
    "chaos_key":             "",
    "c99_key":               "",
    "netlas_key":            "",
    "onyphe_key":            "",
    "greynoise_key":         "",
    "fofa_key":              "",
    "pulsedive_key":         "",
    "intelx_key":            "",
    "publicwww_key":         "",
    "merklemap_key":         "",
    "bevigil_key":           "",
    "hunterhow_key":         "",
    "circl_user":            "",
    "circl_pass":            ""
}

Preencha as chaves que você tiver. Fontes com chaves vazias ainda funcionam se suportarem acesso não autenticado (hackertarget, urlscan, alienvault, dnsdumpster, commoncrawl, anubisdb, subdomaincenter, threatminer, scraping gratuito do fofa, urlhaus, circl, bing). Fontes marcadas como exigindo chave retornam zero resultados quando a chave está ausente.

Onde obter cada chave:

Chave URL
alienvault_otx https://otx.alienvault.com → Settings → API Integration
hackertarget https://hackertarget.com/membership
urlscan https://urlscan.io/user/signup
virustotal https://www.virustotal.com/gui/join-us
securitytrails https://securitytrails.com/app/account
shodan https://account.shodan.io
censys_id / censys_secret https://search.censys.io/account/api
github_token https://github.com/settings/tokens (escopo: public_repo)
grayhatwarfare_token https://grayhatwarfare.com/account
leakix_token https://leakix.net/login → API Keys
fullhunt_token https://fullhunt.io/user/api
chaos_key https://chaos.projectdiscovery.io → API Key
c99_key https://api.c99.nl → Sign up
netlas_key https://app.netlas.io/profile/ → API Keys
onyphe_key https://www.onyphe.io/login/ → API
greynoise_key https://viz.greynoise.io/account/
fofa_key https://fofa.so/userInfo
pulsedive_key https://pulsedive.com/api/
intelx_key https://intelx.io/account?tab=developer
publicwww_key https://publicwww.com/api.html
merklemap_key https://www.merklemap.com/dashboard/api (requer assinatura paga)
bevigil_key https://bevigil.com/osint-api → Sign up
hunterhow_key https://hunter.how/api → Get API key
circl_user / circl_pass https://www.circl.lu/services/passive-dns/ → Solicitar acesso (opcional; camada gratuita não autenticada disponível)

Uso

Básico

# Single domain
python simplerecon.py -d target.com

# List of domains
python simplerecon.py -l domains.txt

# List available sources
python simplerecon.py --list-sources

# List available profiles (curated source groups)
python simplerecon.py --list-profiles

# Print built-in usage examples and exit
python simplerecon.py --list-examples

# Run a predefined profile (no need to spell out sources)
python simplerecon.py -d target.com --profile fast
python simplerecon.py -d target.com --profile osint --verify-live

Screenshot

Exemplos em contexto OSINT

Bug bounty - mapear a superfície de ataque externa (apenas passivo):

python simplerecon.py -d megacorp.com \
  --sources crtsh,virustotal,shodan,censys,github,chaos \
  --output json --outfile megacorp_subs.json

Enumeração completa - passivo + força bruta + verificação de hosts ativos:

python simplerecon.py -d target.com \
  --brute wordlists/subdomains-top1million-20000.txt \
  --resolvers assets/wordlist/resolvers.txt \
  --wildcard-tests 5 \
  --validate-resolvers \
  --verify-live \
  --output json \
  --outfile target_full.json

Força bruta em massa estilo PureDNS com lista comunitária de resolvedores:

python simplerecon.py -d target.com \
  --brute wordlists/all.txt \
  --resolvers https://public-dns.info/nameservers-all.txt \
  --check-resolvers \
  --validate-resolvers \
  --threads 40

Recon profundo - inclui fontes ativas e enumeração recursiva:

python simplerecon.py -d target.com \
  --sources crtsh,certspotter,github,chaos,nsec_walk,srv_enum \
  --brute wordlists/top5000.txt \
  --recursive --recursive-depth 2 \
  --verify-live \
  --output json --outfile deep_recon.json

Zone walking DNSSEC (requer NSEC, não NSEC3):

python simplerecon.py -d target.com --sources nsec_walk -v

Descoberta de ativos a partir de uma lista de domínios:

python simplerecon.py -l scope.txt --output json --outfile all_subs.json --timeout 60

Modo silencioso - envie os subdomínios diretamente via pipe para outra ferramenta:

python simplerecon.py -d target.com --no-banner | httpx -silent

Todas as flags

Target:
  -d DOMAIN              Single target domain
  -l FILE                File with one domain per line
  --stdin                Read domains from stdin (one per line); enables pipe-friendly use

Output:
  -o {txt,json,csv,ndjson,html,markdown}
                         Output format (default: txt).
                         ndjson   = one compact JSON line per subdomain - ideal for jq piping
                         html     = interactive network-map page (vis-network via CDN)
                         markdown = human-readable reconnaissance report
  --outfile FILE         Write output to file
  --network-map          Include network graph (nodes/edges) in JSON output.
                         Auto-enabled when -o html or --network-html is used.
  --network-html FILE    Write an HTML network-map visualization to FILE alongside the
                         main output. Combine with any -o format.
  --db FILE              Per-target results database - persist the run and use as comparison source.
                         Stores subdomains, live data, TLD variants, URLs (with source) and
                         (at -v 3) external hosts/IPs. The command log lives in config/system.db.
  --db-news              Output and save only values not seen in prior --db runs (requires --db)
  --db-list TYPE         List and exit: domains | urls | extras (from --db) or history
                         (from config/system.db, no --db). Optional -d filter.

Monitoring:
  --watch-add CRON       Register the current command (minus --watch-add) in config/system.db
                         on a 5-field cron schedule (e.g. "0,15,30,45 * * * *").
  --watch                Run the scheduler daemon: each minute launch due jobs in parallel
                         (prints each fired command). No --db needed; Ctrl-C to stop.
  --watch-list           List registered watch jobs with their IDs, then exit.
  --watch-del ID         Delete the watch job with the given ID (from --watch-list).
  --watch-clear          Delete all watch jobs, then exit.

Performance:
  -t N                   Thread multiplier for brute-force concurrency (default: 8)
  --timeout N            HTTP/DNS timeout in seconds (default: 30)
  --rate-limit N         Max concurrent HTTP requests per source (0 = unlimited)

Network:
  --proxy URL            Route all HTTP requests through a proxy
                         (e.g. http://127.0.0.1:8080 or socks5://host:port)
  --user-agent UA        Override User-Agent for all source HTTP requests

Source control:
  --profile PROFILE      Run a predefined source group (fast, stealth, osint,
                         code, active, full). Overrides --sources.
  --sources LIST         Comma-separated sources (default: all)
  --exclude LIST         Comma-separated sources to exclude (applied after --sources/--profile)
  --no-passive           Skip passive sources; run active/brute only
  --list-sources         Print all sources with descriptions and exit
  --list-profiles        Print all profiles with their source sets and exit
  --list-examples        Print built-in usage examples and exit

Run-config:
  --config FILE          Load CLI argument defaults from a JSON preset file.
                         Only keys absent from the command line are applied;
                         explicit CLI flags always win.
                         Template: config/run_config.example.json

Brute-force:
  --brute WORDLIST       Wordlist path for DNS brute-force
  --resolvers FILE_OR_URL
                         DNS resolver IPs - local file or https:// URL
                         (e.g. config/resolvers.txt or https://public-dns.info/nameservers-all.txt)
  --check-resolvers      Test each resolver against example.com before brute-force;
                         remove non-responsive ones (PureDNS technique)
  --wildcard-tests N     Random probes for wildcard detection (default: 3)
                         Higher values reduce false negatives on load-balanced DNS
  --validate-resolvers   Re-validate results against Google/Cloudflare after brute-force
                         to eliminate DNS-poisoned false positives (PureDNS two-pass)
  --permute              Generate Altdns-style permutations from found subdomains
  --learn-words          Derive brute-force candidates from patterns in discovered names
                         (numeric sequences, environment families, geo variants, version bumps).
                         Runs after passive/active gathering, feeds into --brute when combined.
  --tld-brute [FILE]     Discover live TLD variants of the target (e.g. target.net, target.io).
                         Strips the current TLD, resolves {base}.{tld} for every entry in the
                         wordlist. Optional FILE overrides the default assets/wordlist/tlds.txt (~250 TLDs).
                         Results are stored separately as tld_variants in all output formats.

Post-processing:
  --verify-live          HTTP/HTTPS probe; also extracts TLS certificate SANs (Amass technique)
                         and fingerprints potential subdomain takeovers (Subjack technique)
  --recursive            Re-enumerate discovered subdomains as new targets (Subfinder technique)
  --recursive-depth N    Max recursion depth when --recursive is enabled (default: 1)

Display:
  -v [LEVEL]             Verbose level 1–4 (1=zero results, 2=+HTTP codes,
                         3=+body +extras (external hosts/IPs/URLs), 4=+exceptions)
  -q, --quiet            Results only; suppress all process messages
  --no-banner            Suppress banner and all process output (clean pipe mode)
  --no-color             Disable ANSI colors

Screenshot


Perfis

Perfis são grupos de fontes selecionados, definidos em config/profiles.json. Use --profile NAME em vez de digitar listas longas em --sources. Perfis também podem definir padrões automaticamente (ex.: rate_limit).

python simplerecon.py --list-profiles
python simplerecon.py -d target.com --profile fast
Perfil Descrição Fontes
fast Varredura rápida - apenas as fontes mais rápidas sem autenticação crtsh, certspotter, hackertarget, rapiddns, jldc, alienvault, anubisdb, subdomaincenter, urlhaus, circl, bing
stealth Pegada mínima - apenas passivo, com rate limit (rate_limit=2) crtsh, certspotter, wayback, commoncrawl, robtex, anubisdb
osint Repositórios de código + threat intel + logs CT + bancos de ativos crtsh, certspotter, alienvault, virustotal, shodan, github, grep_app, threatminer, anubisdb, subdomaincenter, hackertarget, rapiddns, urlscan, bevigil, hunterhow, urlhaus, circl, bing
code Apenas busca em código github, grep_app
active Apenas técnicas ativas zone_transfer, ns_brute, dns_mining, caa_enum, nsec_walk, srv_enum, spider, robots_sitemap, ptr_sweep, asn_sweep, vhost_probe
full Todas as fontes passivas e ativas disponíveis all

Adicione ou edite perfis modificando config/profiles.json:

{
  "myprofile": {
    "description": "My custom set",
    "sources": ["crtsh", "github", "shodan"],
    "options": {"rate_limit": 5}
  }
}

Predefinições de Run-Config

Um run-config é um arquivo JSON que armazena valores padrão dos argumentos de CLI, permitindo executar varreduras repetíveis sem linhas de comando longas.

python simplerecon.py -d target.com --config config/run_config.example.json
python simplerecon.py -d target.com --config my_scan.json

Precedência (da mais alta → mais baixa):

  1. Flags explícitas de CLI (sempre vencem)
  2. Valores do arquivo JSON em --config
  3. Padrões embutidos do argparse

Apenas as chaves presentes no JSON são aplicadas; chaves desconhecidas são ignoradas silenciosamente, para que os configs permaneçam compatíveis com versões futuras/anteriores. Um config mínimo já é perfeitamente válido - basta incluir apenas as chaves que você quer definir:

{
  "profile": "osint",
  "verify_live": true,
  "output": "json",
  "outfile": "results.json"
}

O template comentado em config/run_config.example.json documenta todas as chaves disponíveis. Copie e edite para criar sua própria predefinição.

# List-examples shows ready-to-copy command patterns
python simplerecon.py --list-examples

Módulos Passivos vs Ativos

Passivo

Fontes passivas consultam bancos de dados de terceiros, APIs e índices públicos. Nenhum pacote é enviado para a infraestrutura do alvo.

  • Seguro para executar durante fases de recon passivo
  • Invisível ao monitoramento de segurança do alvo
  • Exemplos: logs de Certificate Transparency, Shodan, VirusTotal, busca de código no GitHub, Common Crawl

Ativo

Fontes ativas se comunicam diretamente com os servidores DNS do alvo. O alvo pode observar esse tráfego.

Módulo O que faz Nível de detecção
zone_transfer Tentativa de AXFR em todos os nameservers Alto - conecta ao NS do alvo
ns_brute Descoberta de NS secundário (SOA MNAME + força bruta de nomes comuns), depois AXFR/IXFR em cada candidato Alto - queries DNS ao NS do alvo
dns_mining Queries de registros SPF / DMARC / MX / TXT Moderado - queries DNS ao NS do alvo
caa_enum Mineração de registros CAA - o campo iodef: vaza hostnames e URLs internos Baixo - uma única query DNS por alvo
nsec_walk Zone walking DNSSEC NSEC para enumerar toda a zona Alto - consulta diretamente o NS autoritativo
srv_enum Enumeração de registros SRV para ~70 prefixos comuns de serviço Moderado - queries DNS a resolvedores públicos
spider Crawler HTML em BFS (profundidade 2, 100 páginas): segue links <a href> e <link href>; também coleta arquivos JS de <script src> e <link rel=modulepreload>; minera subdomínios do conteúdo JS e segue referências //# sourceMappingURL / X-SourceMap até arquivos .map Alto - requisições HTTP diretas ao alvo
robots_sitemap Busca robots.txt (extrai diretivas Sitemap: e hostnames em paths), depois baixa e faz parsing recursivo de sitemap.xml / <sitemapindex> - extrai todos os hostnames de <loc> Moderado - HTTP direto ao alvo
ptr_sweep Varredura reversa de PTR em blocos /24 contendo IPs do alvo Moderado - queries DNS a resolvedores públicos
asn_sweep Consulta o ASN do alvo via bgp.he.net, obtém todos os prefixos CIDR pertencentes a ele, depois faz PTR sweep em cada IP de cada bloco Moderado - HTTP para bgp.he.net + queries DNS PTR
vhost_probe Força bruta de virtual host via fuzzing do header HTTP Host Alto - requisições HTTP diretas ao alvo

Módulos ativos estão incluídos em --sources all. Para executá-los explicitamente:

# Run only active sources
python simplerecon.py -d target.com --no-passive --sources zone_transfer,dns_mining,nsec_walk,srv_enum,spider

# Mix passive + specific active
python simplerecon.py -d target.com --sources crtsh,shodan,nsec_walk,srv_enum,spider

# Or just use the curated active profile
python simplerecon.py -d target.com --profile active

[!NOTE] Zone transfer (AXFR) só funciona se o nameserver do alvo estiver mal configurado. Quando funciona, vaza toda a zona DNS.

Zone walking NSEC só funciona quando o domínio usa NSEC (não NSEC3). NSEC3 usa nomes com hash e bloqueia a enumeração. O módulo detecta isso automaticamente e encerra de forma graciosa.


Fontes

python simplerecon.py --list-sources

Fontes Passivas (39)

Fonte Requer chave Notas
rapiddns Não Dataset de DNS
jldc Não Banco de subdomínios Anubis
crtsh Não Certificate Transparency
certspotter Não Certificate Transparency
merklemap Obrigatória Agregador de logs CT (alternativa ao crt.sh) - requer assinatura paga
anubisdb Não Banco de dados de DNS passivo do jonlu.ca
subdomaincenter Não Índice gratuito de subdomínios apoiado pelo Netcraft
threatminer Não DNS passivo / threat intel do ThreatMiner
urlscan Opcional Rate limit maior com chave
hackertarget Opcional Rate limit maior com chave
wayback Não API CDX do web.archive.org (paginada, até 200 mil entradas)
commoncrawl Não API CDX do Common Crawl - dados históricos de crawl independentes
robtex Não DNS passivo
alienvault Opcional Threat intelligence da OTX
bufferover Não FDNS do Rapid7 via TLS
dnsdumpster Não Ferramenta de recon DNS (web scraping)
github Obrigatória Busca de código por referências fixas ao domínio
grep_app Não Busca de código do grep.app no GitHub
virustotal Obrigatória Endpoint de subdomínios do VT
securitytrails Obrigatória Histórico de DNS
censys Obrigatória Busca de certificados
shodan Obrigatória Consulta de domínio DNS
grayhatwarfare Obrigatória Buckets públicos de nuvem (AWS/Azure/GCP)
leakix Opcional Serviços expostos e ativos em nuvem
fullhunt Obrigatória Índice completo de hosts e subdomínios da internet
chaos Obrigatória Banco de subdomínios continuamente atualizado do ProjectDiscovery
c99 Obrigatória Buscador de subdomínios do C99.nl
netlas Obrigatória Descoberta de ativos e subdomínios em toda a internet
onyphe Obrigatória Motor de busca de defesa cibernética
greynoise Obrigatória Dados de ruído de internet / scanner passivo
fofa Opcional Busca de ativos de internet FOFA (fallback de scrape gratuito sem chave)
pulsedive Opcional Consulta de observáveis de threat-intel
intelx Obrigatória Busca de dados vazados do IntelligenceX
publicwww Obrigatória Busca de código-fonte em toda a web pública
bevigil Obrigatória OSINT BeVigil - subdomínios extraídos de apps mobile
hunterhow Obrigatória Banco de dados global de ativos do Hunter.how
urlhaus Não Banco de URLs maliciosas do URLhaus (abuse.ch) - sem necessidade de autenticação
circl Não (Basic auth opcional) CIRCL Passive DNS - camada gratuita pública; chave dá taxa maior
bing Não Busca Bing - scraping anti-bot com rotação de UA e múltiplos templates

Fontes Ativas (11)

Fonte Requer chave Notas
zone_transfer Não Transferência de zona DNS (AXFR) em nameservers públicos
ns_brute Não Descoberta de NS secundário (SOA MNAME + força bruta de 16 prefixos) + AXFR/IXFR em todos os candidatos
dns_mining Não Mineração de registros SPF / DMARC / MX
caa_enum Não Mineração do campo CAA iodef: - vaza hostnames internos de valores mailto: e https://
nsec_walk Não Zone walking DNSSEC NSEC
srv_enum Não Enumeração de registros SRV (~70 prefixos de serviço)
spider Não Crawler HTML em BFS + minerador JS: segue <a href> / <link href> (profundidade 2, 100 páginas), coleta arquivos JS de <script src>, extrai subdomínios do conteúdo JS, segue referências de sourcemap .map
robots_sitemap Não Diretivas de robots.txt + extração recursiva de hostnames em <loc> de sitemap.xml / <sitemapindex> (máx. 20 sitemaps, profundidade 2)
ptr_sweep Não Varredura reversa de PTR em blocos /24 contendo IPs do alvo
asn_sweep Não Resolve o ASN do alvo via bgp.he.net → obtém todos os prefixos IPv4 pertencentes a ele (≤/20) → PTR sweep
vhost_probe Não Força bruta de virtual host via fuzzing do header HTTP Host (lista embutida com mais de 130 palavras)

Screenshot


Força Bruta de DNS

Wordlists recomendadas

Wordlist Tamanho Caso de uso
subdomains-top1million-5000.txt 5k Varredura inicial rápida
subdomains-top1million-20000.txt 20k Bug bounty padrão
subdomains-top1million-110000.txt 110k Enumeração completa
best-dns-wordlist.txt (Assetnote) ~9M Pentest profundo
# Clone SecLists
git clone --depth 1 https://github.com/danielmiessler/SecLists.git

# Or download a single file
wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/DNS/subdomains-top1million-20000.txt

# Assetnote wordlist (generated from real CT log data)
wget https://wordlists-cdn.assetnote.io/data/manual/best-dns-wordlist.txt

Resolvedores DNS customizados

Por padrão a ferramenta usa 6 resolvedores embutidos (Google, Cloudflare, Quad9, OpenDNS). Para força bruta em larga escala, forneça uma lista maior:

# Bundled list (~30 verified public resolvers)
python simplerecon.py -d target.com --brute wordlist.txt \
  --resolvers assets/wordlist/resolvers.txt

# Download a community list on-the-fly (7000+ resolvers)
python simplerecon.py -d target.com --brute wordlist.txt \
  --resolvers https://public-dns.info/nameservers-all.txt

# With health check - filters dead resolvers before brute-force
python simplerecon.py -d target.com --brute wordlist.txt \
  --resolvers https://public-dns.info/nameservers-all.txt \
  --check-resolvers

--resolvers aceita:

  • Um caminho de arquivo local (config/resolvers.txt, um IP por linha, comentários com # suportados, formato ip:port aceito)
  • Uma URL http:// ou https:// (baixada automaticamente via httpx)

A lista é deduplicada e embaralhada automaticamente para distribuir a carga entre todos os resolvedores.

Outras fontes públicas de resolvedores:

Exemplo completo de força bruta

# Fast - bundled resolvers, no validation
python simplerecon.py -d target.com \
  --brute wordlists/top5000.txt \
  --resolvers assets/wordlist/resolvers.txt \
  --threads 20

# Thorough - community resolvers, health check, two-pass validation
python simplerecon.py -d target.com \
  --brute wordlists/subdomains-top1million-20000.txt \
  --resolvers https://public-dns.info/nameservers-all.txt \
  --check-resolvers \
  --wildcard-tests 5 \
  --validate-resolvers \
  --threads 30

Força Bruta de TLD

--tld-brute descobre registros ativos do domínio alvo sob outros TLDs (ex.: target.net, target.io, target.com.br). Útil para proteção de marca, detecção de typosquatting e mapeamento do portfólio completo de domínios de um alvo.

# Use the built-in wordlist (~250 TLDs)
python simplerecon.py -d target.com --tld-brute

# Use a custom TLD list
python simplerecon.py -d target.com --tld-brute custom_tlds.txt

# Combine with passive enumeration and live verification
python simplerecon.py -d target.com --tld-brute --verify-live -o json --outfile results.json

A ferramenta remove o TLD atual do alvo (tratando automaticamente TLDs compostos como .co.uk e .com.br), depois resolve {base}.{tld} para cada entrada da wordlist. Apenas as variantes que resolvem no DNS são retornadas.

Os resultados aparecem em um campo separado tld_variants na saída JSON/CSV/NDJSON e são impressos no terminal ao final de cada execução.

A wordlist padrão é assets/wordlist/tlds.txt. Edite-a ou forneça seu próprio arquivo com --tld-brute FILE.


Aprendizado de Wordlist - Candidatos Derivados de Padrões

--learn-words analisa os nomes de subdomínio já descobertos (por fontes passivas e ativas) e deriva candidatos de força bruta direcionados a partir dos padrões que encontra - sem depender de nenhuma wordlist estática.

# Derive candidates from passive results, then resolve them
python simplerecon.py -d target.com --profile fast --learn-words

# Combine with a traditional wordlist (merged before resolution)
python simplerecon.py -d target.com --profile fast --learn-words --brute wordlists/top5000.txt

# Full pipeline: passive → learn → brute → live-check
python simplerecon.py -d target.com --profile osint --learn-words \
  --brute wordlists/subdomains-top1million-5000.txt \
  --verify-live -o json --outfile results.json

Padrões detectados

Padrão Exemplo de entrada Candidatos gerados
Sequências numéricas api1, api2 api3api9 (preenche lacunas e estende sequências)
Famílias de ambiente dev-api, dev-admin staging-api, qa-api, prod-api, staging-admin
Variantes geográficas cdn-us, cdn-eu cdn-br, cdn-uk, cdn-de, cdn-sg
Incrementos de versão app-v2 app-v1, app-v3
Combinações de tokens dev, api, eu cada um em ≥2 nomes pares em produto cruzado ausentes: dev-eu, api-eu

O learner retorna apenas candidatos novos - nomes que ainda não estão no conjunto já descoberto. Quando combinado com --brute, os dois conjuntos de palavras são mesclados antes da resolução DNS, de forma que apenas uma passagem de força bruta é executada.


Extras - Hosts Externos, IPs e URLs

O nível de verbosidade -v 3 exibe elementos coletados durante a enumeração que ficam fora do domínio alvo. Útil para mapear infraestrutura de parceiros, descobrir ativos relacionados e entender o ecossistema mais amplo de um alvo. (O nível 3 também habilita o log de preview do corpo HTTP.)

# External hosts from CT logs + GitHub + APIs
python simplerecon.py -d target.com --sources crtsh,github -v 3

# Spider: external hosts + all crawled URLs
python simplerecon.py -d target.com --sources spider -v 3 --no-banner

# Full run: external hosts, IPs from live check, crawled URLs
python simplerecon.py -d target.com --profile osint --verify-live -v 3 -o json --outfile out.json

O que é coletado

Categoria Fonte Exemplos
Hosts externos Todas as 46 fontes via _filter() partner.com, cdn.cloudfront.net, internal.corp encontrados em SANs de certificado ou código
IPs --verify-live (resolvido por subdomínio) 1.2.3.4, 2606:4700::
URLs Crawl do spider (páginas + JS + .map) além de wayback, commoncrawl, urlscan, alienvault https://target.com/api/v1, https://target.com/static/app.js

Saída por formato

txt - seções anexadas (cabeçalhos suprimidos com --no-banner/--quiet):

api.target.com
www.target.com

# External hosts
partner.com
cdn.fastly.net

# IPs
104.21.1.1
172.67.1.1

# URLs
https://target.com/static/main.js
https://target.com/static/main.js.map

json - objeto "extras" no nível superior:

"extras": {
  "hosts": ["cdn.fastly.net", "partner.com"],
  "ips":   ["104.21.1.1", "172.67.1.1"],
  "urls":  ["https://target.com/static/main.js"]
}

ndjson - linhas adicionais com o campo type:

{"domain": "target.com", "subdomain": "partner.com",              "type": "extra_host"}
{"domain": "target.com", "subdomain": "104.21.1.1",               "type": "extra_ip"}
{"domain": "target.com", "subdomain": "https://target.com/api/v1","type": "extra_url"}

csv - linhas extras com type = extra_host, extra_ip, extra_url.

Receitas jq para extras

# External hosts only
python simplerecon.py -d target.com --sources crtsh,censys -v 3 -o ndjson \
  | jq 'select(.type == "extra_host") | .subdomain'

# IPs (combine with verify-live)
python simplerecon.py -d target.com --profile fast --verify-live -v 3 -o ndjson \
  | jq 'select(.type == "extra_ip") | .subdomain'

# All crawled JS files
python simplerecon.py -d target.com --sources spider -v 3 -o ndjson \
  | jq 'select(.type == "extra_url" and (.subdomain | endswith(".js"))) | .subdomain'

Mapeamento de Rede - Grafo JSON e Visualização HTML

Transforma o resultado da enumeração plana em uma topologia de rede navegável: um grafo JSON (nós + arestas) que você pode encaminhar via pipe para outras ferramentas, e uma página HTML interativa para triagem visual. O grafo é construído inteiramente a partir dos dados já coletados durante a execução - sem requisições ou varreduras extras.

Referência de flags - três eixos

As três flags operam de forma independente e podem ser combinadas:

Flag Papel Onde a saída aparece Combinável?
-o html Formato de saída primário - substitui txt/json/csv/ndjson stdout ou --outfile Um -o por vez
--network-html FILE Artefato paralelo - sempre grava a visualização HTML em FILE FILE (qualquer caminho) Sim - funciona junto com qualquer -o
--network-map Injeta um bloco "network" (nós/arestas) na saída JSON Dentro do documento JSON Só faz sentido com -o json; auto-habilitado por -o html/--network-html

Exemplos concretos

# 1) HTML only - no JSON, no txt
python simplerecon.py -d target.com --verify-live -o html --outfile map.html
# → map.html (interactive graph)

# 2) JSON enriched with the graph block
python simplerecon.py -d target.com --verify-live --network-map -o json --outfile out.json
# → out.json: {"domain":..., "subdomains":..., "live_hosts":..., "network": {nodes, edges, stats}}
#   (without --network-map the JSON does not include the "network" field)

# 3) Plain-text results + HTML map side by side
python simplerecon.py -d target.com --verify-live -o txt --outfile out.txt --network-html map.html
# → out.txt (flat subdomain list)  +  map.html (visualization)

# 4) Two artifacts in one run - JSON data + HTML for the browser
python simplerecon.py -d target.com --verify-live --network-map -o json --outfile out.json --network-html map.html
# → out.json (with network block)  +  map.html (visualization)

# 5) Multiple targets merged into one combined graph
python simplerecon.py -l targets.txt --verify-live --tld-brute -o html --outfile multi.html

Modelo do grafo

Tipo de nó Construído a partir de Notas
domain alvo da varredura um por alvo
subdomain result.subdomains colorido por live.status (2xx/3xx/4xx/5xx/nenhum)
ip live[sub].ips (requer --verify-live) compartilhado entre subdomínios que compartilham um IP
cloud live[sub].cloud um nó por provedor (AWS / Cloudflare / GCP / …)
cname live[sub].cname apenas quando o CNAME aponta para fora da zona do alvo
tld_variant result.tld_variants (requer --tld-brute) ligado de volta ao domínio raiz
Relação da aresta Direção
has_subdomain domain → subdomain
resolves_to subdomain → ip
hosted_on subdomain → cloud
cname_to subdomain → cname
tld_variant_of tld_variant → domain

Formato JSON (com --network-map)

{
  "domain": "target.com",
  "subdomains": [...],
  "live_hosts": { ... },
  "network": {
    "nodes": [
      {"id": "target.com",     "type": "domain",    "label": "target.com", "color": "#1976d2"},
      {"id": "api.target.com", "type": "subdomain", "label": "api",        "color": "#4caf50", "status": 200},
      {"id": "104.18.22.1",    "type": "ip",        "label": "104.18.22.1","color": "#00897b"},
      {"id": "cloud:cloudflare","type": "cloud",    "label": "CLOUDFLARE", "color": "#fbc02d"}
    ],
    "edges": [
      {"from": "target.com",     "to": "api.target.com", "relation": "has_subdomain"},
      {"from": "api.target.com", "to": "104.18.22.1",    "relation": "resolves_to"},
      {"from": "api.target.com", "to": "cloud:cloudflare","relation": "hosted_on"}
    ],
    "stats": {"domains": 1, "subdomains": 42, "ips": 18, "clouds": 3, "cnames": 5, "tld_variants": 2, "edges": 71}
  }
}

Visualizador HTML

Arquivo único e autocontido. Carrega o vis-network 9.1.9 a partir do unpkg.com (CDN - requer internet ao abrir). Recursos:

  • Layout com direcionamento por força, com zoom, pan, botões de navegação, controles de teclado
  • Clicar em um nó → painel de detalhes com status HTTP, título, header do servidor
  • Legenda com contagens por tipo e chave de cores de status
  • Todos os alvos de uma execução com múltiplos alvos mesclados em um único grafo

Screenshot

Receitas jq para o bloco do grafo

# Top providers across the surface
jq '.network.nodes[] | select(.type == "cloud") | .label' out.json | sort | uniq -c

# Subdomains pointing at a specific IP
jq -r --arg ip 104.18.22.1 \
  '.network.edges[] | select(.relation == "resolves_to" and .to == $ip) | .from' out.json

# CNAMEs to external services (potential third-party dependencies)
jq -r '.network.nodes[] | select(.type == "cname") | .label' out.json

# Quick summary
jq '.network.stats' out.json

Técnicas Avançadas

Detecção de Wildcard por Multi-probe (PureDNS)

Em vez de um único probe de subdomínio aleatório (pouco confiável sob balanceamento de carga DNS), a ferramenta dispara --wildcard-tests N probes e só confirma wildcard quando ≥ ceil(N/2) resolvem. A união de todos os IPs retornados é usada como conjunto de filtro durante a força bruta.

python simplerecon.py -d target.com --brute wordlist.txt --wildcard-tests 5

Validação em Duas Passagens com Resolvedores Confiáveis (PureDNS)

Após força bruta em massa com resolvedores públicos baratos, revalide apenas contra Google/Cloudflare. Elimina falsos positivos causados por envenenamento de DNS em resolvedores não confiáveis.

python simplerecon.py -d target.com --brute wordlist.txt --validate-resolvers

Extração de SAN em Certificados TLS (Amass)

Durante --verify-live, a ferramenta realiza um handshake SSL bruto na porta 443 e extrai os Subject Alternative Names do certificado do servidor. Hostnames recém-descobertos são adicionados de volta ao conjunto de subdomínios.

python simplerecon.py -d target.com --verify-live --output json --outfile out.json
# JSON output includes "tls_sans": ["cdn.target.com", "api.target.com", ...] per live host

Zone Walking DNSSEC NSEC (Amass)

Registros NSEC formam uma lista encadeada ordenada de todos os nomes na zona DNS. A fonte nsec_walk percorre a cadeia para enumerar toda a zona sem uma transferência de zona. Funciona apenas quando o domínio usa NSEC (não NSEC3 - o módulo detecta e reporta isso automaticamente).

python simplerecon.py -d target.com --sources nsec_walk -v
# Example domain with NSEC: nlnetlabs.nl

Enumeração de Registros SRV (Amass)

Consulta ~70 prefixos SRV comuns (_http._tcp, _ldap._tcp, _kerberos._tcp, _autodiscover._tcp, _sip._tcp, etc.). Registros SRV frequentemente revelam hostnames internos não encontrados por fontes passivas.

python simplerecon.py -d target.com --sources srv_enum -v

A lista de prefixos está em config/srv_prefixes.json - edite-a para adicionar serviços específicos do domínio.

Enumeração Recursiva (Subfinder)

Depois de enumerar target.com, os subdomínios descobertos, como api.target.com, são usados como novos alvos de enumeração para encontrar entradas mais profundas (v2.api.target.com, internal.api.target.com, etc.).

python simplerecon.py -d target.com --recursive --recursive-depth 2

Busca de Código no GitHub

Busca no GitHub por arquivos de código-fonte contendo referências ao domínio alvo (subdomínios fixos em configs, arquivos .env, scripts de CI). Requer um github_token em config/api_keys.json.

python simplerecon.py -d target.com --sources github -v

Detecção de Takeover de Subdomínio

Quando --verify-live está habilitado, cada host ativo é verificado quanto a sinais de takeover por três métodos independentes:

Método Como Campo
Fingerprint de corpo Corpo da resposta comparado com ~30 assinaturas de serviços takeover
Cadeia de CNAME Cadeia completa de CNAME percorrida; sufixo comparado com 21 serviços takeover: "cname:<service>"
WAF / CDN Headers da resposta comparados com 11 provedores waf
python simplerecon.py -d target.com --verify-live -o json --outfile out.json
[LIVE] orphan.target.com  → 404  - NoSuchBucket [TAKEOVER? aws-s3]
[LIVE] docs.target.com    → 404  - There isn't a GitHub Pages site here. [TAKEOVER? cname:github-pages]
[LIVE] api.target.com     → 200  - API Gateway [cloudflare]

Serviços detectáveis para takeover (corpo + CNAME):

aws-s3, github-pages, heroku, netlify, fastly, shopify, ghost-io, surge-sh, zendesk, readme-io, unbounce, webflow, squarespace, hubspot, freshdesk, sendgrid, uservoice, wpengine, pantheon, teamwork, acquia, bigcartel

Fingerprinting de WAF / CDN:

cloudflare, akamai, fastly, cloudfront, incapsula, sucuri, azure-cdn, google, imperva, barracuda, f5-big-ip

Todas as assinaturas estão definidas em verify/live_check.py (_TAKEOVER_CNAME, _TAKEOVER_BODY, _WAF_HEADERS) - edite ali para adicionar novos serviços.

[!IMPORTANT] Uma correspondência positiva é um candidato, não um takeover confirmado. Sempre verifique a titularidade do CNAME antes de reportar.

Receitas jq para triagem de takeover

python simplerecon.py -d target.com --verify-live -o ndjson \
  | jq 'select(.takeover != null)'

# Only CNAME-based hits
python simplerecon.py -d target.com --verify-live -o ndjson \
  | jq 'select(.takeover | strings | startswith("cname:"))'

# Show WAF-protected hosts
python simplerecon.py -d target.com --verify-live -o ndjson \
  | jq 'select(.waf != null) | {subdomain, waf, status}'

# JSON outfile - extract takeover candidates
jq -r '.live_hosts | to_entries[] | select(.value.takeover != null) | .key' out.json \
  | dnsx -silent -cname -resp

Formatos de Saída

Terminal (padrão)

------------------------------------------------------------
[*] Enumerating: target.com
------------------------------------------------------------
[*] Running passive sources...
[*] [crtsh] +42 subdomains
[*] [github] +8 subdomains
[*] [chaos] +12 subdomains
[*] [nsec_walk] +31 subdomains
[*] [srv_enum] +3 subdomains

[+] Total unique subdomains found: 72

api.target.com
dev.target.com
mail.target.com
...

JSON

python simplerecon.py -d target.com --verify-live -o json --outfile results/target.json
{
  "domain": "target.com",
  "timestamp": "2026-05-27T14:32:01.123456",
  "total": 72,
  "subdomains": [
    "api.target.com",
    "dev.target.com",
    "mail.target.com"
  ],
  "live_hosts": {
    "api.target.com": {
      "status": 200,
      "title": "API Gateway",
      "server": "nginx/1.24.0",
      "content_length": 1842,
      "url": "https://api.target.com",
      "ips": ["104.18.22.1", "104.18.23.1"],
      "cloud": "cloudflare",
      "tls_sans": ["api.target.com", "*.api.target.com", "cdn.target.com"],
      "cname": null,
      "waf": "cloudflare",
      "takeover": null
    },
    "orphan.target.com": {
      "status": 404,
      "title": "",
      "server": "AmazonS3",
      "content_length": 320,
      "url": "https://orphan.target.com",
      "ips": ["52.217.33.142"],
      "cloud": "aws",
      "tls_sans": [],
      "cname": "orphan.target.com.s3-website-us-east-1.amazonaws.com",
      "waf": null,
      "takeover": "cname:aws-s3"
    }
  },
  "sources": {
    "crtsh": 42,
    "github": 8,
    "chaos": 12,
    "nsec_walk": 31,
    "srv_enum": 3,
    "tls_sans": 5
  }
}

CSV

python simplerecon.py -d target.com --verify-live -o csv --outfile results/target.csv
domain,subdomain,type,status,title,server,ips,cloud,tls_sans,takeover,cname,waf
target.com,api.target.com,subdomain,200,API Gateway,nginx/1.24.0,104.18.22.1|104.18.23.1,cloudflare,api.target.com|*.api.target.com,,,cloudflare
target.com,mail.target.com,subdomain,200,Webmail,Apache/2.4,203.0.113.5,,,,
target.com,orphan.target.com,subdomain,404,,AmazonS3,52.217.33.142,aws,,cname:aws-s3,orphan.target.com.s3-website-us-east-1.amazonaws.com,
target.com,target.net,tld_variant,,,,,,,,,,

NDJSON

Uma linha JSON compacta por subdomínio - projetada para streaming e uso com jq via pipe.

python simplerecon.py -d target.com --verify-live -o ndjson
python simplerecon.py -d target.com --verify-live -o ndjson --outfile results/target.ndjson
{"domain": "target.com", "subdomain": "api.target.com", "type": "subdomain", "status": 200, "title": "API Gateway", "server": "nginx/1.24.0", "ips": ["104.18.22.1"], "cloud": "cloudflare", "waf": "cloudflare"}
{"domain": "target.com", "subdomain": "orphan.target.com", "type": "subdomain", "status": 404, "server": "AmazonS3", "ips": ["52.217.33.142"], "cloud": "aws", "cname": "orphan.target.com.s3-website-us-east-1.amazonaws.com", "takeover": "cname:aws-s3"}
{"domain": "target.com", "subdomain": "dev.target.com", "type": "subdomain"}
{"domain": "target.com", "subdomain": "target.net", "type": "tld_variant"}

Exemplos com jq:

# Live hosts only
python simplerecon.py -d target.com --verify-live -o ndjson | jq 'select(.status != null)'

# Takeover candidates
python simplerecon.py -d target.com --verify-live -o ndjson | jq 'select(.takeover != null)'

# WAF-protected hosts
python simplerecon.py -d target.com --verify-live -o ndjson | jq 'select(.waf != null) | {subdomain, waf}'

# Extract only subdomains (pipe-friendly)
python simplerecon.py -d target.com -o ndjson | jq -r '.subdomain'

TXT

python simplerecon.py -d target.com -o txt --outfile results/target.txt

HTML - Mapa de rede interativo

python simplerecon.py -d target.com --verify-live -o html --outfile results/target.html

Gera uma página HTML autocontida que renderiza a topologia descoberta como um grafo interativo (nós: domain / subdomain / IP / cloud / CNAME / variante de TLD - arestas: has_subdomain, resolves_to, hosted_on, cname_to, tld_variant_of). Carrega o vis-network a partir de um CDN, então precisa de acesso à internet ao ser aberto. Nós de subdomínio são coloridos pelo status HTTP (verde 2xx, laranja 3xx, vermelho 4xx, roxo 5xx, cinza inacessível). Veja Mapeamento de Rede para detalhes.

Markdown - Relatório de reconhecimento legível para humanos

python simplerecon.py -d target.com --verify-live -o markdown --outfile report.md

Gera um documento Markdown estruturado com métricas resumidas, tabelas de hosts ativos, candidatos a takeover, hashes de corpo duplicados, todos os subdomínios, variantes de TLD, contribuições por fonte e extras - pronto para colar em issues do GitHub, wikis, ou entregar a clientes. Veja Relatório em Markdown para detalhes.


Relatório em Markdown

-o markdown produz um relatório de reconhecimento completo como um único arquivo .md.

# Markdown as primary output
python simplerecon.py -d target.com --verify-live -o markdown --outfile report.md

# Pipe to a terminal Markdown viewer
python simplerecon.py -d target.com --verify-live -o markdown | glow -

Seções incluídas

Seção Aparece quando
Cabeçalho (data, fontes, totais) Sempre
Tabela de métricas resumidas Sempre
⚠ Candidatos a Takeover Qualquer takeover detectado por --verify-live
Tabela de Hosts Ativos (status, título, servidor, cloud, WAF, ms) --verify-live
Hashes de Corpo Duplicados ≥2 hosts compartilham o mesmo hash de resposta
Todos os Subdomínios (bloco de código) Sempre
Variantes de TLD --tld-brute
Extras (hosts, IPs, URLs) -v 3
Contribuições por Fonte Sempre

A seção duplicate_bodies sinaliza hosts que retornaram conteúdo de resposta idêntico - um sinal confiável de DNS wildcard ou de farms de CDN que não foram capturados pelo filtro de wildcard.


Banco de Dados - Persistência em SQLite

Persista cada execução em um arquivo SQLite, compare novos achados contra execuções anteriores e leia os dados armazenados de volta - tudo usando o sqlite3 da stdlib do Python (sem dependência extra). Existem dois armazenamentos:

  • Banco de resultados por alvo - --db FILE (um caminho escolhido por você). Contém apenas resultados de recon (subdomínios, URLs, extras, variantes de TLD), e também é a fonte de comparação para --db-news. Arquivos diferentes são armazenamentos independentes.
  • Banco fixo do sistema - config/system.db (resolvido em relação à instalação, nunca passado como parâmetro). Contém dados de sistema/log: o log de histórico de comandos (uma linha por execução) e os jobs do scheduler watch (veja Monitoramento Contínuo).

Screenshot

Ex.: inurl.db

Salvando resultados - --db

# Save the full run to recon.db
python simplerecon.py -d target.com --db recon.db

# Capture URLs, IPs and external hosts too
python simplerecon.py -d target.com --db recon.db --verify-live -v 3

Uma execução com --db armazena os subdomínios descobertos (mais metadados de verificação de hosts ativos quando --verify-live), variantes de TLD, URLs coletadas (marcadas com a fonte que as encontrou) e - em -v 3 - hosts externos e IPs. A linha de comando executada é registrada separadamente, no log de comandos fixo em config/system.db (toda execução, mesmo sem --db).

Apenas novidades desde a última execução - --db-news

--db-news compara a execução atual com tudo que está armazenado em --db e exibe (e salva) apenas os valores nunca vistos antes - monitoramento contínuo embutido, substituindo a receita manual com comm -13.

# First run: everything is new and saved
python simplerecon.py -d target.com --db recon.db --db-news

# Later: prints + stores only newly appeared subdomains/URLs
python simplerecon.py -d target.com --db recon.db --db-news -o json --outfile new.json

Inspecionar o banco de dados - --db-list

Leia os dados armazenados de volta como linhas de texto simples, prontas para pipe (sem executar enumeração):

python simplerecon.py --db recon.db --db-list domains    # discovered subdomains (from --db)
python simplerecon.py --db recon.db --db-list urls       # collected URLs (from --db)
python simplerecon.py --db recon.db --db-list extras     # external hosts / IPs (from --db)
python simplerecon.py --db-list history                  # command log (from config/system.db, no --db)

# Filter to one target, then pipe onward
python simplerecon.py --db recon.db --db-list domains -d target.com | httpx -silent

Screenshot

Schema

Somente inserção (append-only), indexado por domain (sem tabela de execução/pai - a auditoria de comandos vive em config/system.db).

--db por alvo (apenas resultados):

Tabela Conteúdo
subdomains subdomínios descobertos + campos de verificação de hosts ativos (status, title, ips, cloud, takeover, waf, …)
tld_variants variantes de TLD ativas encontradas por --tld-brute
extras hosts externos e IPs (salvos em -v 3)
urls URLs coletadas, cada uma marcada com sua fonte de origem

config/system.db fixo (log de sistema + scheduler):

Tabela Conteúdo
command_history uma linha por execução - command, targets, timestamp
watch_jobs jobs do scheduler cron - command, schedule, last_run (veja Monitoramento Contínuo)

URLs são coletadas de spider, wayback, commoncrawl, urlscan e alienvault (toda URL dentro do escopo que eles encontram) e armazenadas independentemente do nível de verbosidade (diferente de hosts/IPs extras, que precisam de -v 3). Consulte qualquer coisa diretamente com sqlite3:

sqlite3 recon.db 'SELECT url, source FROM urls ORDER BY source'
sqlite3 config/system.db 'SELECT DISTINCT command, timestamp FROM command_history'

[!NOTE] Bancos de dados por alvo criados por versões antigas são migrados automaticamente no primeiro uso - as tabelas anteriores history/scan_id (ou as ainda mais antigas scans/command_history) são incorporadas ao schema plano somente-resultados, sem perda de dados.


Monitoramento Contínuo (--watch)

Um scheduler cron embutido: registre comandos de recon uma vez, depois execute um daemon que os dispara conforme o agendamento. Os jobs (e seus agendamentos) vivem no config/system.db fixo — sem necessidade de cron/systemd externos. Sem dependência extra (matcher cron de 5 campos desenvolvido internamente).

Registrar um job - --watch-add "CRON"

Acrescente --watch-add "<cron>" a qualquer comando normal. O comando (com --watch-add removido) é armazenado junto com o agendamento:

# every 15 minutes, run a fast scan that persists results + diff to target.db
python simplerecon.py -d target.com --profile fast --db target.db --quiet \
  --watch-add "0,15,30,45 * * * *"
# stored: simplerecon.py -d target.com --profile fast --db target.db --quiet

O agendamento é uma expressão cron padrão de 5 campos (minuto hora dia-do-mês mês dia-da-semana) suportando *, listas (0,15,30), intervalos (9-17) e passos (*/30).

Executar o scheduler - --watch

python simplerecon.py --watch        # no --db needed; Ctrl-C to stop

A cada minuto o daemon recarrega watch_jobs, e cada job cujo cron corresponde ao minuto atual é disparado. Jobs que vencem no mesmo minuto rodam em paralelo (cada um como seu próprio subprocesso, persistindo em seu próprio --db). Jobs recém-adicionados são captados ao vivo, sem reiniciar o daemon.

Gerenciar jobs

python simplerecon.py --watch-list      # list jobs (each row shows its ID)
python simplerecon.py --watch-del 3     # delete job #3 (ID from --watch-list)
python simplerecon.py --watch-clear     # delete all jobs
python simplerecon.py --db-list history # command run log (config/system.db)

--watch-list imprime uma tabela alinhada:

Watch jobs (config/system.db)

  ID   SCHEDULE             LAST RUN            COMMAND
  1    0,15,30,45 * * * *   2026-06-06T22:45    /abs/simplerecon.py -d target.com --profile fast --db target.db --quiet
  2    */30 * * * *         —                   /abs/simplerecon.py -d b.com --profile osint --db b.db --quiet

Example

# Cron
* * * * *
│ │ │ │ └─── Dia da semana (0 - 6, onde 0 = domingo)
│ │ │ └───── Mês (1 - 12)
│ │ └─────── Dia do mês (1 - 31)
│ └───────── Hora (0 - 23)
└─────────── Minuto (0 - 59)

Screenshot

Quando o daemon dispara um job, ele imprime o comando agendado, ex.: [+] [watch] 22:45 fired job #1: /abs/simplerecon.py -d target.com --profile fast --db target.db --quiet.

[!TIP] Execute --watch-add e --watch a partir do mesmo diretório de trabalho para que caminhos relativos dentro do comando armazenado (ex.: --db target.db) resolvam de forma consistente.


Uso Avançado

Detecção de IP e Provedor de Nuvem

Quando --verify-live está habilitado, cada host ativo também é resolvido para seus endereços IP e o provedor de nuvem é identificado por fingerprint - ambos os campos aparecem em todos os formatos de saída.

python simplerecon.py -d target.com --verify-live -o json --outfile out.json

Ordem de detecção:

  1. Cadeia de CNAME - comparada com sufixos conhecidos de provedores (*.amazonaws.com, *.azurewebsites.net, *.run.app, etc.)
  2. Faixas CIDR de IP - endereços IP comparados com as faixas publicadas de AWS, Azure, GCP, Cloudflare, Fastly

Provedores detectados: aws, azure, gcp, cloudflare, fastly, github, heroku, netlify, vercel, digitalocean

# jq - show IPs and cloud per live host
jq '.live_hosts | to_entries[] | {host: .key, ips: .value.ips, cloud: .value.cloud}' out.json

# ndjson - filter only AWS-hosted subdomains
python simplerecon.py -d target.com --verify-live -o ndjson | jq 'select(.cloud == "aws")'

Suporte a proxy

Encaminhe todas as requisições HTTP (fontes passivas, fontes ativas, --verify-live) através de um proxy (Burp, mitmproxy, SOCKS5):

# HTTP/S proxy (e.g. Burp Suite)
python simplerecon.py -d target.com --proxy http://127.0.0.1:8080

# SOCKS5 proxy (e.g. Tor)
python simplerecon.py -d target.com --proxy socks5://127.0.0.1:9050

User-Agent customizado

python simplerecon.py -d target.com --user-agent 'Mozilla/5.0 (compatible; MyScanner/1.0)'

Excluindo fontes

# Run everything except noisy/slow sources
python simplerecon.py -d target.com --exclude github,intelx,publicwww

# Profile with overrides
python simplerecon.py -d target.com --profile osint --exclude merklemap,chaos

Alvos via stdin / pipe

# Explicit flag
echo 'target.com' | python simplerecon.py --stdin -o ndjson

# Auto-detected when stdin is not a TTY
cat domains.txt | python simplerecon.py -o txt

# Chain with amass, subfinder, or other tools
subfinder -silent -d target.com | python simplerecon.py --stdin --sources crtsh,virustotal

httpx - sondagem HTTP

python simplerecon.py -d target.com --no-banner | httpx -silent -status-code -title -tech-detect

# Filter only 200 OK
python simplerecon.py -d target.com --no-banner | httpx -silent -mc 200

nmap - varredura de portas

python simplerecon.py -d target.com -o txt --outfile subs.txt
nmap -iL subs.txt -p 80,443,8080,8443 -T4 --open

nuclei - varredura de vulnerabilidades

python simplerecon.py -d target.com --no-banner \
  | httpx -silent \
  | nuclei -t cves/ -silent

dnsx - resolução DNS e rastreamento de cadeia CNAME

# Find potential subdomain takeovers
python simplerecon.py -d target.com --no-banner \
  | dnsx -silent -cname -resp \
  | grep -E 'amazonaws|azurewebsites|github.io|herokuapp'

eyewitness - screenshots

python simplerecon.py -d target.com --verify-live -o txt --outfile subs.txt
eyewitness --web -f subs.txt --no-prompt -d screenshots/

String-x - enriquecimento e automação

O String-x (aka strx) é uma ferramenta modular de automação que usa um placeholder {STRING}. Ele se combina naturalmente com o SimpleReconDomain via pipes.

# HTTP probe all discovered subdomains
python simplerecon.py -d target.com --no-banner \
  | strx -st "echo {STRING}" -module "clc:http_probe" -pm

# Resolve subdomains → extract IPs → Shodan lookup per IP
python simplerecon.py -d target.com --no-banner \
  | strx -st "echo {STRING}" -module "clc:dns" -pm \
  | strx -st "echo {STRING}" -module "ext:ip" -pm \
  | strx -st "echo {STRING}" -module "clc:shodan" -pm

# Enrich with DNS + geolocation in a single chain
python simplerecon.py -d target.com --no-banner \
  | strx -st "echo {STRING}" -module "clc:dns|ext:ip|clc:geoip" -pm

# Send live subdomains to Telegram
python simplerecon.py -d target.com --no-banner \
  | strx -st "echo {STRING}" -module "con:telegram" -pm

Criando um Novo Módulo

Todas as fontes herdam de BaseSource em sources/base.py. Coloque o arquivo em sources/passive/ ou sources/active/ - nenhum outro arquivo precisa ser editado.

O nome da classe deve ser o nome do arquivo em title-case (ex.: myservice.py → classe Myservice), e NAME deve ser igual ao nome do arquivo sem .py.

O que uma fonte pode contribuir

O fetch() de uma fonte retorna os subdomínios dentro do escopo que ela encontrou. Além disso, a classe base permite que qualquer fonte alimente três canais de dados que fluem para a saída (extras aparecem em -v 3) e para o armazenamento SQLite (--db):

Dado Como contribuir Onde aparece
Domínios (subdomínios dentro do escopo) return self._filter(found, domain) conjunto de resultados - todos os formatos de saída
Hosts externos & IPs automático - _filter() encaminha itens descartados fora do escopo para self.extras['hosts'] / self.extras['ips'] (IPs detectados automaticamente) extras (exibido em -v 3; tabela extras no --db)
URLs self._add_url(url, domain) - verificado quanto ao escopo, deduplicado, com limite (_MAX_URLS_PER_SOURCE) extras['urls'] + tabela urls (atribuído à fonte, persistido em toda execução com --db)

Regra geral: sempre encaminhe os achados brutos através de self._filter(...) (ele normaliza maiúsculas/minúsculas, remove *., e coleta hosts/IPs fora do escopo de graça), e chame self._add_url(...) para qualquer URL completa que você encontrar. Raramente será necessário escrever diretamente em self.extras.

Nova Fonte Passiva

# sources/passive/myservice.py
from sources.base import BaseSource
from core.config import get_key

class Myservice(BaseSource):
    NAME = 'myservice'
    DESCRIPTION = 'My custom service'
    API_TOKEN_IS_REQUIREMENT = True

    async def fetch(self, domain: str) -> set[str]:
        api_key = get_key('myservice')
        if not api_key:
            return set()

        subdomains: set[str] = set()
        headers = {'Authorization': f'Bearer {api_key}'}
        async with self._make_client(headers=headers) as client:
            resp = await self._get(client, f'https://api.myservice.com/subdomains/{domain}')
            if resp.status_code == 200:
                for entry in resp.json().get('data', []):
                    subdomains.add(entry['hostname'])         # in-scope names → result
                    if entry.get('url'):
                        self._add_url(entry['url'], domain)   # full URLs → extras['urls'] + DB

        # _filter() returns the in-scope subdomains and auto-captures any out-of-scope
        # hostnames / IPs into self.extras['hosts'] / self.extras['ips'].
        return self._filter(subdomains, domain)

Adicione a chave em config/api_keys.json:

{ "myservice": "your-api-key-here" }

Nova Fonte Ativa

Use asyncio.wait_for + run_in_executor para chamadas DNS bloqueantes, evitando travamentos:

# sources/active/myactive.py
import asyncio
from sources.base import BaseSource

class Myactive(BaseSource):
    NAME = 'myactive'
    DESCRIPTION = 'Active: custom DNS probe'
    API_TOKEN_IS_REQUIREMENT = False

    async def fetch(self, domain: str) -> set[str]:
        loop = asyncio.get_event_loop()
        try:
            return await asyncio.wait_for(
                loop.run_in_executor(None, self._run, domain),
                timeout=max(self.timeout, 30),
            )
        except asyncio.TimeoutError:
            self._vlog(1, 'timed out')
            return set()

    def _run(self, domain: str) -> set[str]:
        subdomains: set[str] = set()
        try:
            import dns.resolver
            # ... blocking dnspython calls here ...
            # Same helpers apply to active sources, e.g. a URL found in a TXT record:
            #   self._add_url(url, domain)            # → extras['urls'] + DB
            # Out-of-scope hostnames / IPs are captured automatically by _filter() below.
        except Exception as exc:
            self._log_exc(exc)
        return self._filter(subdomains, domain)

📄 LICENÇA

Este projeto está licenciado sob a Apache License - veja o arquivo LICENSE para detalhes.

👨‍💻 AUTOR

MrCl0wn


Contribuições ✨

Contribuições de qualquer tipo são bem-vindas!

Lista de colaboradores

⭐ Se este projeto foi útil, considere dar uma estrela!

💡 Sugestões e feedback são sempre bem-vindos!

💀 Hacker Hackeia!