(SRDomain) Simple Recon - Domain v2.0.0
Ferramenta de enumeração passiva e ativa de domínios para workflows de OSINT e reconhecimento.
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
- Chaves de API
- Uso
- Perfis
- Predefinições de Run-Config
- Módulos Passivos vs Ativos
- Fontes
- Força Bruta de DNS
- Aprendizado de Wordlist - Candidatos Derivados de Padrões
- Força Bruta de TLD
- Extras - Hosts Externos, IPs e URLs
- Mapeamento de Rede - Grafo JSON e Visualização HTML
- Relatório em Markdown
- Banco de Dados - Persistência em SQLite
- Monitoramento Contínuo - --watch
- Técnicas Avançadas
- Detecção de Takeover de Subdomínio
- Formatos de Saída
- Uso Avançado
- Criando um Novo Módulo
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:
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

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

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):
- Flags explícitas de CLI (sempre vencem)
- Valores do arquivo JSON em
--config - 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) |

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, formatoip:portaceito) - Uma URL
http://ouhttps://(baixada automaticamente viahttpx)
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 |
api3 … api9 (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

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).

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

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 antigasscans/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)

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-adde--watcha 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:
- Cadeia de CNAME - comparada com sufixos conhecidos de provedores (
*.amazonaws.com,*.azurewebsites.net,*.run.app, etc.) - 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
- 🌐 Blog: http://blog.mrcl0wn.com
- 🐙 GitHub: @MrCl0wnLab
- 🐦 Twitter: @MrCl0wnLab
- 📧 Email: mrcl0wnlab@\gmail.com
Contribuições ✨
Contribuições de qualquer tipo são bem-vindas!
⭐ Se este projeto foi útil, considere dar uma estrela!
💡 Sugestões e feedback são sempre bem-vindos!
💀 Hacker Hackeia!

Comments