SimpleReconSubdomain: Enumeração de Subdomínios

Salve, Hackers!
Em meus estudos de Python + OSINT resolvi codar mais uma tool (sim, sou retardado) que eu realmente usasse nas minhas empreitadas na internet dentro do contexto Domain. Observei diversas tools que fazem algo semelhante, mas queria algo focado em contexto Ativo e Passivo de enumeração e totalmente modular. Até então eu estava usando uma versão simples (simplerecon_v1.py) que executava comandos Linux enviando requests para determinadas APIs públicas, mas eu queria mais! Queria cobrir diversas APIs públicas mesmo com uma limitação de resultados, elas agregam! e dar a possibilidade do uso de determinadas APIs privadas.
Seguindo a filosofia da preguiça
Faça uma coisa, mas, se for repetir, crie um script gambiarra.
— mrcl0wn
WARNING: Nem vou perder tempo explicando o que é enumeração de subdomínios ou resolução de DNS. A ferramenta não faz milagres, não é melhor que qualquer outra que já existe. É somente um resultado de meus estudos.
Adicionei tudo que toca diretamente na infra do alvo como contexto de enumeração ATIVA (envio de request direto ao servidor), e tudo que usa API pública ou privada entra no contexto de enumeração PASSIVA, pois usa terceiros para coletar informações.
Fontes Passivas
As fontes passivas baseiam-se na consulta a bancos de dados de terceiros, APIs e índices públicos ou privados.
- Interação: Nenhum pacote é enviado diretamente para a infraestrutura do alvo.
- Visibilidade: São invisíveis ao monitoramento de segurança do alvo, o que as torna ideais para fases iniciais de reconhecimento furtivo.
- Exemplos incluídos na ferramenta: Logs de Transparência de Certificados (como crt.sh), Shodan, VirusTotal, buscas de código no GitHub.
Fontes Ativas
As fontes ativas realizam comunicação direta com os servidores do alvo (como servidores DNS ou servidores web).
- Interação: O tráfego gerado pode ser observado pelo alvo através de logs de servidores e sistemas de detecção.
- Nível de Detecção: Varia de moderado a alto, dependendo da técnica utilizada.
- Exemplos incluídos na ferramenta:
- Zone Transfer (AXFR): Tentativa de transferir a zona DNS completa (Nível de detecção: Alto).
- DNS Mining: Consultas de registros SPF, DMARC, MX e TXT (Nível de detecção: Moderado).
- Spider: Rastreador (crawler) que acessa páginas HTML e arquivos JS diretamente no servidor do alvo (Nível de detecção: Alto).
- Vhost Probe: Brute-force de Virtual Hosts via cabeçalho HTTP Host (Nível de detecção: Alto).
Agora não mais um wrapper de curl + jq + awk amarrado com barbante (a v1 era isso, e eu assumo). Bom, depois de diversas cervejas, code e grandes pitadas de Claude Code, a versão 2 nasceu: Simple Recon Subdomain.
O SimpleReconSubdomain foi criado do “zero” em Python (kibei muita coisa do string-x) assíncrono e consulta 50 fontes (39 passivas e 11 ativas) em paralelo. Sem dependência de shell externa, sem jq, sem awk. Tudo integrado: NSEC zone walking, extração de TLS SAN, crawling de HTML/JS, detecção de Subdomain Takeover e validação de resolvers em duas etapas (estilo PureDNS). tudo foi organizado de forma modular com Python.
Segmentarei o artigo dentro do contexto do nosso fluxo de coleta e operação. Do clone sujo até o grafo de rede renderizado na tela.
0. Instalação (Sem complicação)
O Comando:
git clone https://github.com/MrCl0wnLab/SimpleReconSubdomain
cd SimpleReconSubdomain
pip install -r requirements.txt
Code language: Bash (bash)
Pronto. As dependências básicas (como httpx pros requests assíncronos, aiodns pra porradaria do brute-force e beautifulsoup4 pro spider ler HTML) já vão estar no gatilho. não esqueça do Python 3.10 ou superior.
1. Usabilidade / Primeiros Passos
Vamos entender um pouco de como usar a ferramenta. Para começar a utilizar a ferramenta, é necessário atender aos seguintes requisitos e configurações iniciais:
- Configuração de APIs: Embora muitas fontes funcionem sem autenticação, o uso pleno exige a inserção de chaves de API no arquivo
config/api_keys.json. Fontes sem chaves configuradas retornarão zero resultados, mas não impedirão a execução dos demais módulos. - Execução: O ponto de entrada principal é o script
simplerecon.py.
Informações rápidas
# Help
python simplerecon.py --help
# 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
Code language: Bash (bash)
Contexto de Perfis (Profiles)
Os perfis são grupos curados de fontes definidos no arquivo config/profiles.json, permitindo executar varreduras temáticas sem a necessidade de listar manualmente cada fonte. O uso da flag --profile tem precedência sobre --sources. Editando o arquivo profiles.json você pode criar novos agrupamentos de sources. Eu particulamente uso o profiles como uma receita de bolo.
O agrupamento de sources abaixo é correspondente ao profile fast.
- Em vez disso:
python3.12 simplerecon.py -d target.com --sources crtsh,certspotter,hackertarget,rapiddns,jldc,alienvault,anubisdb,subdomaincenter,urlhaus,circl,bing - Use:
python3.12 simplerecon.py -d target.com --profile fast
Os principais perfis integrados são:
fast: Executa apenas as fontes mais rápidas e que não exigem autenticação.stealth: Foca em fontes passivas com pegada mínima e limites de taxa de requisição rigorosos.osint: Combina repositórios de código, inteligência de ameaças e logs de certificados.active: Ativa apenas as 7 técnicas de reconhecimento direto, como transferência de zona (AXFR), nsec_walk e vhost_probe.full: Utiliza todas as 46 fontes passivas e ativas disponíveis.
Principais Flags para Uso
As flags do SimpleReconSubdomain permitem ajustar a profundidade da varredura e o formato de saída dos dados:
Definição de Alvo:
-d DOMAIN: Define um único domínio alvo.-l FILE: Permite carregar uma lista de domínios de um arquivo.--stdin: Habilita a ferramenta receber output de pipes.
Seu meio de coleta:
--sources: É o parâmetro utilizado para selecionar manualmente quais módulos de enumeração a ferramenta deve executar durante uma varredura. Ela oferece controle total sobre o processo de reconhecimento, permitindo que o usuário escolha entre as 50 fontes disponíveis (39 passivas e 11 ativas). se deseja ver as source use:--list-sources
Técnicas de Descoberta:
--brute WORDLIST: Ativa o brute-force de DNS utilizando uma lista de palavras personalizada.--verify-live: Realiza probes HTTP/HTTPS para validar se o host está ativo, extrai títulos de páginas, cabeçalhos de servidor e nomes alternativos (SAN) de certificados TLS.--recursive: Ativa a enumeração recursiva, tratando subdomínios encontrados como novos alvos para descobrir níveis mais profundos.--tld-brute: Tenta descobrir o domínio alvo registrado em outras extensões (ex: .net, .io, .com.br).--permute: Gera e resolve variações de subdomínios (estilo Altdns) baseadas nos nomes já descobertos.
Saída e Visualização:
-o FORMAT: Define o formato de saída (txt, json, csv, ndjson ou html).--network-html FILE: Gera um mapa de rede interativo em HTML, transformando os resultados em uma topologia visual navegável.Nível de Verbose (-v):O nível -v 3 é particularmente importante, pois ele traz à tona os “Extras” elementos coletados que estão fora do domínio alvo, como hosts externos, IPs e URLs mineradas de sourcemaps ou arquivos JavaScript
1. Planejamento e Coleta Passiva
O primeiro passo é definir o escopo e o que estamos procurando. Sem plano / direcionamento, você vai se afogar em dado inútil.
O Objetivo: Mapear subdomínios expostos sem tocar ativamente no alvo (modo stealth), trazendo tudo o que motor de busca, certificado (crt.sh) e API entregarem.
O Comando:
python simplerecon.py -d target.com --profile fast
Code language: Bash (bash)
Nessa etapa, o script cospe a primeira camada de informação batendo rápido em fonte aberta / APIs. É eficiente, mas ainda precisamos ir além se quisermos achar aquele painel legado que algum admin esqueceu vivo cinco anos atrás.
2. O tal do OSINT e Verificação ao Vivo
A coleta passiva entrega volume, mas também traz muito ruído típico da internet morta. Agora entramos na fase de filtro. Usamos um “profile” focado exclusivamente nisso.
O comando abaixo usa as fontes do profile osint e introduz a flag --verify-live. O objetivo aqui é resolver tudo o que foi coletado e descartar o “lixo” que não responde mais (timeout/NXDOMAIN), entregando só o que de fato está vivo.
Sources do profile osint: crtsh,certspotter,alienvault,virustotal,shodan,github,grep_app,threatminer,anubisdb,subdomaincenter,hackertarget,rapiddns,urlscan,bevigil,hunterhow,urlhaus,circl,bing
O Comando:
python simplerecon.py -d target.com --profile osint --verify-live --output json --outfile megacorp_subs.json
Code language: Bash (bash)
Boom. Agora temos um .json limpo, formatado e pronto pra ser ingerido e correlacionado nas outras plataformas do seu ciclo de inteligência.
3. Brute-force Massivo (Separando o joio do trigo)
Vamos deixar uma coisa clara: script não faz milagre se você não rodar a força bruta direito. Pra descobrir domínio profundo que não aparece em API nenhuma, o analisa precisa usar wordlist e resolvers de qualidade.
Aqui ativamos o brute-force e parâmetro --validate-resolvers pra aplicar a técnica de dupla checagem. Isso é fundamental pra evitar muito falso positivo de DNS envenenado (comum quando passamos por ISP obscuro e firewall sacana).
O Comando:
python simplerecon.py -d target.com --brute wordlists/all.txt --resolvers https://public-dns.info/nameservers-all.txt --check-resolvers --validate-resolvers --threads 40
Code language: Bash (bash)
4. Persistência de Verdade: o uso de --db (SQLite)
Gerenciar 200 arquivo .txt ou .json espalhados é coisa de cabaço. Se você faz recon contínuo ou monitora múltiplos alvos pra “Bug Bounty”, você precisa de histórico e estrutura.
Pra resolver essa dor, implementei a flag --db. Quando você passa esse parâmetro, a ferramenta joga todos os achados dentro de um banco SQLite local.
O Comando:
python simplerecon.py -d inurl.com.br --source spider --db inurl.db
Code language: Bash (bash)
Descobriu o subdomínio hoje? Tá salvo no banco. Rodou de novo amanhã? O banco guarda o estado atualizado. Isso te permite criar query de verdade, cruzar dado e fazer gestão de ativo como profissional, e não como acumulador de log textualizado.

5. Monitoramento Contínuo com --watch (Daemon de Operador)
A infraestrutura do seu alvo muda enquanto você dorme, viaja ou toma cerveja. Aquele subdomínio de staging que estava fora do ar de manhã pode subir à noite por descuido de dev. Ficar rodando script na mão ou fazendo gambiarra com sleep no bash também é osso.
O motor do comando --watch não é simples loop. Ele é um daemon de agendamento real baseado em cron. O funcionamento é: você gerencia uma fila de jobs e o daemon lê essa lista a cada minuto. O que bater com o cron atual é disparado. Tudo roda em paralelo (cada coleta no seu próprio subprocesso, persistindo no seu próprio banco local). O melhor? Ele puxa os jobs novos “quentes” (live reload), sem você precisar dar restart no daemon.
Gerenciando a fila de jobs: Antes de subir o daemon, você estrutura o que vai ser monitorado e com qual frequência.
# Cadastro de comando
python simplerecon.py -d target.com --profile fast --quiet --no-banner --no-color --db target.com.db --watch-add "0 */12 * * *"
# Listar todos os jobs (exibe uma tabela alinhada com ID, Schedule, Last Run e o Comando exato)
python simplerecon.py --watch-list
# Deletar um job específico pelo ID (ex: ID 3)
python simplerecon.py --watch-del 3
# Deletar todos os jobs de uma vez
python simplerecon.py --watch-clear
# Ver o log de execuções passadas no banco do sistema (config/system.db)
python simplerecon.py --db-list history
Code language: Bash (bash)
Rodando o scheduler: Com os jobs configurados, você acorda o daemon. Joga isso pra rodar numa VPS (um tmux ou screen resolve a sua vida). Aqui não precisa passar --db: o daemon gerencia as execuções isoladas com base no que foi agendado.
python simplerecon.py --watch
Code language: Bash (bash)
(Pra matar o processo, o clássico Ctrl-C.)
Quando o scheduler bate o minuto exato do cron, ele cospe a execução no terminal, por exemplo:

Nota sobre o formato Cron: Se você nunca tocou em Linux na vida e não sabe ler um cron, aqui vai a cola básica:
┌───────────── Minuto (0 - 59)
│ ┌─────────── Hora (0 - 23)
│ │ ┌───────── Dia do mês (1 - 31)
│ │ │ ┌─────── Mês (1 - 12)
│ │ │ │ ┌───── Dia da semana (0 - 6, 0 = Domingo)
│ │ │ │ │
* * * * *Code language: JavaScript (javascript)
| Exemplo Cron | O que faz | Cenário típico |
|---|---|---|
*/5 * * * * | A cada 5 minutos | Monitorar alvo agressivo / pegar staging que sobe e desce |
*/15 * * * * | A cada 15 minutos | Watch de Bug Bounty sem fritar o resolver |
*/30 * * * * | A cada 30 minutos | Coleta contínua “de boa” |
0 * * * * | De hora em hora (no minuto 0) | Recon padrão de rotina |
0 */6 * * * | A cada 6 horas | Alvo grande, pra não floodar fonte passiva |
0 */12 * * * | A cada 12 horas | Snapshot manhã/noite |
0 0 * * * | A cada 24 horas (meia-noite) | Varredura diária completa |
0 9 * * * | Uma vez por dia, às 09:00 | Relatório fresquinho pro café |
0 9-18 * * * | De hora em hora, das 09h às 18h | Acompanhar deploy em horário comercial |
0 8 * * 1 | Toda segunda-feira, às 08:00 | Diff semanal de superfície de ataque |
0 3 * * 0 | Todo domingo, às 03:00 | Brute-force pesado na madrugada, com a rede vazia |
0 0 1 * * | Dia 1 de cada mês, à meia-noite | Inventário mensal de ativos |
Dica de Trincheira: o daemon
--watchlê a fila a cada minuto, então a menor granularidade real é de 1 em 1 minuto (* * * * *). Não adianta inventar segundo no cron ele não existe nesse formato. E pensa no alvo: profilefastaguenta*/5, mas brute-force com wordlist gigante de 5 em 5 minutos é tiro no pé (você vira ruído, queima resolver e ainda corre risco de takedown). Tarefa pesada → janela larga (0 */12ou madrugada). Tarefa leve → janela curta.
Dica bônus: se você nunca decorou cron e nem quer, joga a expressão no crontab.guru que ele traduz pra humano antes de você comprometer o job.
6. Customização Total: Criando um Novo Módulo (Extensibilidade de Verdade)
Cansou das 50 fontes nativas? Comprou acesso a uma API obscura ou quer integrar aquele script que você usa na surdina? O core do SimpleReconSubdomain foi desenhado pra ser plug-and-play.
O fluxo é estupidamente simples: toda fonte herda de BaseSource (sources/base.py). Você só joga o seu arquivo Python dentro de sources/passive/ ou sources/active/. Não precisa editar mais nenhum arquivo pra registrar o módulo.
Regras de uso:
- O nome da classe deve ser o nome do arquivo em Title Case (ex: o arquivo
myservice.pyobriga aclass Myservice). - A variável
NAMEinterna deve ser o nome exato do arquivo sem o.py. DESCRIPTIONdefine um breve texto sobre seu módulo; isso é acessado ao listar tal módulo ou em contexto futuro.API_TOKEN_IS_REQUIREMENTvariável booleana que ajuda na visualização de módulos que usam API.
Os canais de dados (o que seu módulo pode contribuir): Um módulo não cospe só subdomínio. A classe base gerencia três canais de dados que vão direto pro output (visíveis com -v 3) e pro banco SQLite (--db), separando o joio do trigo pra você:
| Tipo de dado | Como enviar pro motor | Onde vai parar |
|---|---|---|
| Domínios (no escopo) | return self._filter(found, domain) | Output principal em todos os formatos. |
| Hosts externos e IPs | Automático via _filter(). O que for fora de escopo é filtrado e roteado. | self.extras['hosts'] e self.extras['ips'] (IPs detectados magicamente). |
| URLs completas | self._add_url(url, domain) | extras['urls'] e na tabela urls do banco (com deduplicação e limite por fonte). |
Observação: Nunca retorne sua lista crua. Sempre passe por
self._filter(...)(ele normaliza pra caixa baixa, remove lixo tipo*.e captura IP). Achou um link inteiro no meio do payload? Joga noself._add_url(...). Você raramente vai precisar manipular o dicionárioself.extrasna mão.
Exemplo 1: Fonte Passiva (APIs) Se você vai bater numa API via HTTP, o padrão é esse. Simples, assíncrono e direto.
Arquivo: sources/passive/myservice.py
<strong>from sources.base import BaseSource
from core.config import get_key</strong>
class <strong>Myservice</strong>(<strong>BaseSource</strong>):
<strong>NAME</strong> = 'myservice'
<strong>DESCRIPTION</strong> = 'Minha API secreta de recon'
<strong>API_TOKEN_IS_REQUIREMENT</strong> = 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', []):
# Nomes no escopo vão pro resultado
subdomains.add(entry['hostname'])
# Achou URL completa? Manda pro motor persistir no banco e extras
if entry.get('url'):
self.<strong>_add_url</strong>(entry['url'], domain)
# O _filter() retorna só o que é do alvo e joga o resto (IPs, hosts externos) pros extras
return self.<strong>_filter</strong>(subdomains, domain)
Code language: Python (python)
Não esqueça de adicionar sua chave lá no config/api_keys.json: {"myservice": "sua-chave-aqui"}.
Exemplo 2: Fonte Ativa (lidando com bloqueio) Vai fazer consulta DNS nativa ou rodar uma biblioteca que bloqueia a thread? Se você enfiar código síncrono (tipo dnspython) no meio do loop assíncrono, a ferramenta vai engasgar e você vai chorar achando que é bug meu. Use asyncio.wait_for combinado com run_in_executor.
Arquivo: sources/active/myactive.py
<strong>import asyncio
from sources.base import BaseSource</strong>
class <strong>Myactive</strong>(<strong>BaseSource</strong>):
<strong>NAME</strong> = 'myactive'
<strong>DESCRIPTION</strong> = 'Probe de DNS customizado e agressivo'
<strong>API_TOKEN_IS_REQUIREMENT</strong> = False
async def fetch(self, domain: str) -> set[str]:
loop = asyncio.get_event_loop()
try:
# Joga o código bloqueante pra um executor pra não travar o event loop
return await asyncio.wait_for(
loop.run_in_executor(None, self._run, domain),
timeout=max(self.timeout, 30),
)
except asyncio.TimeoutError:
self.<strong>_vlog</strong>(1, 'timeout estourou, probe lento demais')
return set()
def _run(self, domain: str) -> set[str]:
subdomains: set[str] = set()
try:
import dns.resolver
# ... coloque suas chamadas síncronas bloqueantes do dnspython aqui ...
# Achou uma URL no meio de um TXT record bizarro?
# self.<strong>_add_url</strong>(url, domain) --> Vai pro DB e pros extras
except Exception as exc:
self.<strong>_log_exc</strong>(exc) # Loga o erro sem estourar a cara do usuário
return self.<strong>_filter</strong>(subdomains, domain)
Code language: Python (python)
7. Visualização e Disseminação
Terminal cuspindo texto é lindo, nós amamos. Mas na hora de documentar a operação ou escalar a visualização pra quem não vive no shell, a história muda.
Adicionei suporte nativo pra gerar grafo de rede interativo (--network-map e --network-html {file}) em HTML e exportação limpa em Markdown (-o markdown), facilitando a vida de quem precisa montar report sem estresse.
O Comando Final:
# OUTPUT MARKDOWN
python simplerecon.py -d inurl.com.br --profile full --network-map -o markdown --outfile report_target.md
# OUTPUT VIS NETWORK / HTML
python simplerecon.py -d inurl.com.br --profile full --network-map --network-html inurl.com.br.htmlCode language: Bash (bash)

8. Docker: Rodando sem poluir a máquina
Se você não quer sujar o sua maquina com dependência, a conteinerização é a saída. O setup via Docker roda a ferramenta de forma isolada e manda os argumentos de linha de comando direto pro script Python dentro do contêiner.
Você tem dois caminhos de build: compilar a partir do código local ou direto do repositório (sem nem precisar clonar na máquina). Arquivos docker estão na pasta docker/
A) Build local (na raiz do repositório):
docker build -t docker/simplerecon -f docker/Dockerfile .Code language: Bash (bash)
B) Build direto do GitHub:
docker build -t docker/simplerecon - < docker/Dockerfile<strong>.remote</strong>Code language: Bash (bash)
Executando: Pra rodar, é só chamar a imagem e passar os argumentos na sequência. O parâmetro --rm garante que o contêiner morra limpo depois do uso.
docker run --rm docker/simplerecon -d target.com --profile fastCode language: Bash (bash)
Persistência e agendamento no Docker: Se for usar o --db, gerenciar chave de API ou rodar o scheduler (--watch), você precisa mapear os volumes (bind mounts) do host pra dentro do contêiner caso contrário o dado some no limbo junto com o contêiner. A documentação completa com as opções avançadas de build (como injetar --build-arg), montagem de banco e log de comandos está no detalhe em docker/README.md.
Conclusão
Mapear superfície de ataque é o que separa quem só executa comando de quem realmente sabe o que está fazendo. Rodar ferramenta qualquer um (user de Linkedin) roda. Pensar o escopo, encadear coleta passiva, brute-force, monitoramento e transformar uma lista de subdomínio bruta em ativo gerenciado já é outro jogo.
O SimpleReconSubdomain não foi feito pra tool mágica. Ele tenta automatizar o trabalho braçal da coleta de infraestrutura: do clone sujo até o grafo de rede renderizado na tela, tudo resolvido via terminal, com poucas linhas.
Pra quem está começando, isso quebra o mito da ferramenta mágica. Pra quem já está na estrada (rodado), serve de lembrete de que intel não nasce pronta: ela é construída, etapa por etapa, com método (às vezes com cerveja), critério, automação e um pouco de ódio.
Happy Hacking e até a próxima!
REFS
Post feito ao som de:



