Herramienta de enumeración de dominios pasiva y activa para flujos de trabajo de OSINT y reconocimiento.
Construido con Python asíncrono - consulta 50 fuentes (39 pasivas + 11 activas) en paralelo, sin dependencias externas de shell.
Técnicas inspiradas en subfinder, amass, puredns y subjack: detección de wildcard mediante múltiples sondas, recorrido de zona DNSSEC NSEC (zone walking), extracción de SAN de certificados TLS, minería de registros SRV, validación en dos pasadas con resolvers de confianza, enumeración recursiva, rastreo de HTML/JS con minería de sourcemaps, minería de registros CAA, recolección de robots.txt/sitemap, fuerza bruta de NS secundarios, barrido de PTR basado en ASN, detección de IP/proveedor de nube, aprendizaje de wordlist a partir de nombres descubiertos, y fingerprinting de toma de subdominios (subdomain takeover).
Author: Cleiton Pinheiro (a.k.a MrCl0wn)
Blog: https://blog.mrcl0wn.com
GitHub: https://github.com/MrCl0wnLab
Twitter: https://twitter.com/MrCl0wnLab
Caution
Aviso legal: El uso de SimpleReconDomain para atacar objetivos sin consentimiento mutuo previo es ilegal. Es responsabilidad del usuario final cumplir con todas las leyes locales, estatales y federales aplicables. Los desarrolladores no asumen ninguna responsabilidad y no se hacen responsables de ningún uso indebido o daño causado por este programa.
- Instalación
- Claves de API
- Uso
- Perfiles
- Preajustes de Run-Config
- Módulos Pasivos vs Activos
- Fuentes
- Fuerza Bruta de DNS
- Aprendizaje de Wordlist - Candidatos Derivados de Patrones
- Fuerza Bruta de TLD
- Extras - Hosts Externos, IPs y URLs
- Mapeo de Red - Visualización en JSON de Grafo y HTML
- Informe en Markdown
- Base de Datos - Persistencia SQLite
- Monitoreo Continuo - --watch
- Técnicas Avanzadas
- Detección de Subdomain Takeover
- Formatos de Salida
- Uso Avanzado
- Creación de un Nuevo Módulo
git clone https://github.com/osintbrazuca/SimpleReconDomain
cd SimpleReconDomain
pip install -r requirements.txtDependencias (requirements.txt):
| Paquete | Propósito |
|---|---|
httpx[socks] |
Cliente HTTP asíncrono para todas las fuentes pasivas y la descarga de URL de resolvers ([socks] habilita --proxy socks5://) |
aiodns |
Resolver DNS asíncrono para fuerza bruta y validación |
dnspython |
Transferencia de zona (AXFR), minería de registros DNS, recorrido de zona NSEC, enumeración SRV |
beautifulsoup4 |
Parseo de HTML para la fuente activa spider (extracción de <a>, <link>, <script>) |
Ejecuta sin necesidad de un entorno Python local. Dos rutas de build, la misma imagen - luego pasa los argumentos de CLI directamente:
# A) from local code (build context = repo root)
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.comConsulta docker/README.md para conocer todas las opciones de build (incluyendo el one-liner curl | docker build - y --build-arg REF=), la persistencia de resultados (volumen --db), el log de comandos / registro de --watch, el montaje de claves de API y la ejecución del scheduler.
Las claves de API se almacenan en config/api_keys.json (excluido de git para evitar fugas).
{
"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": ""
}Completa las claves que tengas. Las fuentes con claves vacías igual se ejecutan si admiten acceso sin autenticación (hackertarget, urlscan, alienvault, dnsdumpster, commoncrawl, anubisdb, subdomaincenter, threatminer, scraping gratuito de fofa, urlhaus, circl, bing). Las fuentes marcadas como que requieren clave devuelven cero resultados cuando falta la clave.
Dónde obtener cada clave:
# 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-liveBug bounty - mapear superficie de ataque externa (solo pasivo):
python simplerecon.py -d megacorp.com \
--sources crtsh,virustotal,shodan,censys,github,chaos \
--output json --outfile megacorp_subs.jsonEnumeración completa - pasivo + fuerza bruta + verificación en vivo:
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.jsonFuerza bruta masiva al estilo PureDNS con lista de resolvers de la comunidad:
python simplerecon.py -d target.com \
--brute wordlists/all.txt \
--resolvers https://public-dns.info/nameservers-all.txt \
--check-resolvers \
--validate-resolvers \
--threads 40Recon profundo - incluye fuentes activas y enumeración 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.jsonRecorrido de zona DNSSEC (requiere NSEC, no NSEC3):
python simplerecon.py -d target.com --sources nsec_walk -vDescubrimiento de activos a partir de una lista de dominios:
python simplerecon.py -l scope.txt --output json --outfile all_subs.json --timeout 60Modo silencioso - envía los subdominios directamente a otra herramienta por pipe:
python simplerecon.py -d target.com --no-banner | httpx -silentTarget:
-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
Los perfiles son grupos de fuentes seleccionados, definidos en config/profiles.json. Usa --profile NAME en lugar de escribir listas largas de --sources. Los perfiles también pueden establecer valores por defecto automáticamente (p. ej. rate_limit).
python simplerecon.py --list-profiles
python simplerecon.py -d target.com --profile fast| Perfil | Descripción | Fuentes |
|---|---|---|
fast |
Escaneo rápido - solo las fuentes sin autenticación más veloces | crtsh, certspotter, hackertarget, rapiddns, jldc, alienvault, anubisdb, subdomaincenter, urlhaus, circl, bing |
stealth |
Huella mínima - solo pasivo, con límite de tasa (rate_limit=2) |
crtsh, certspotter, wayback, commoncrawl, robtex, anubisdb |
osint |
Repositorios de código + threat intel + logs CT + bases de datos de activos | crtsh, certspotter, alienvault, virustotal, shodan, github, grep_app, threatminer, anubisdb, subdomaincenter, hackertarget, rapiddns, urlscan, bevigil, hunterhow, urlhaus, circl, bing |
code |
Solo búsqueda de código | github, grep_app |
active |
Solo técnicas activas | zone_transfer, ns_brute, dns_mining, caa_enum, nsec_walk, srv_enum, spider, robots_sitemap, ptr_sweep, asn_sweep, vhost_probe |
full |
Todas las fuentes pasivas y activas disponibles | all |
Agrega o edita perfiles modificando config/profiles.json:
{
"myprofile": {
"description": "My custom set",
"sources": ["crtsh", "github", "shodan"],
"options": {"rate_limit": 5}
}
}Un run-config es un archivo JSON que almacena valores por defecto para los argumentos de CLI, permitiéndote ejecutar escaneos repetibles sin líneas de comandos largas.
python simplerecon.py -d target.com --config config/run_config.example.json
python simplerecon.py -d target.com --config my_scan.jsonPrecedencia (de mayor a menor):
- Flags explícitos de CLI (siempre ganan)
- Valores del archivo JSON de
--config - Valores por defecto integrados de argparse
Solo se aplican las claves presentes en el JSON; las claves desconocidas se ignoran silenciosamente para que los configs mantengan compatibilidad hacia adelante/atrás. Un config mínimo es perfectamente válido - solo necesitas incluir las claves que quieras establecer:
{
"profile": "osint",
"verify_live": true,
"output": "json",
"outfile": "results.json"
}La plantilla comentada en config/run_config.example.json documenta cada clave disponible. Cópiala y edítala para crear tu propio preajuste.
# List-examples shows ready-to-copy command patterns
python simplerecon.py --list-examplesLas fuentes pasivas consultan bases de datos de terceros, APIs e índices públicos. No se envía ningún paquete a la infraestructura del objetivo.
- Seguro de ejecutar durante las fases de recon pasivo
- Invisible para el monitoreo de seguridad del objetivo
- Ejemplos: logs de Certificate Transparency, Shodan, VirusTotal, búsqueda de código en GitHub, Common Crawl
Las fuentes activas se comunican directamente con los servidores DNS del objetivo. El objetivo puede observar este tráfico.
| Módulo | Qué hace | Nivel de detección |
|---|---|---|
zone_transfer |
Intento de AXFR en todos los nameservers | Alto - se conecta al NS del objetivo |
ns_brute |
Descubrimiento de NS secundarios (SOA MNAME + fuerza bruta de nombres comunes) y luego AXFR/IXFR en cada candidato | Alto - consultas DNS al NS del objetivo |
dns_mining |
Consultas de registros SPF / DMARC / MX / TXT | Moderado - consultas DNS al NS del objetivo |
caa_enum |
Minería de registros CAA - el campo iodef: filtra hostnames internos y URLs |
Bajo - una sola consulta DNS por objetivo |
nsec_walk |
Recorrido de la cadena NSEC de DNSSEC para enumerar toda la zona | Alto - consulta directamente al NS autoritativo |
srv_enum |
Enumeración de registros SRV para ~70 prefijos de servicio comunes | Moderado - consultas DNS a resolvers públicos |
spider |
Crawler HTML en anchura (BFS) (profundidad 2, 100 páginas): sigue enlaces <a href> y <link href>; también recopila archivos JS de <script src> y <link rel=modulepreload>; extrae subdominios del contenido JS y sigue referencias //# sourceMappingURL / X-SourceMap a archivos .map |
Alto - solicitudes HTTP directas al objetivo |
robots_sitemap |
Obtiene robots.txt (extrae directivas Sitemap: y hostnames en las rutas) y luego descarga y analiza recursivamente sitemap.xml / <sitemapindex> - extrae todos los hostnames <loc> |
Moderado - HTTP directo al objetivo |
ptr_sweep |
Barrido de PTR (DNS inverso) en bloques /24 que contienen IPs del objetivo | Moderado - consultas DNS a resolvers públicos |
asn_sweep |
Busca el ASN del objetivo vía bgp.he.net, obtiene todos los prefijos CIDR que posee, y luego hace un barrido de PTR sobre cada IP de cada bloque | Moderado - HTTP a bgp.he.net + consultas DNS PTR |
vhost_probe |
Fuerza bruta de virtual-hosts mediante fuzzing del header HTTP Host | Alto - solicitudes HTTP directas al objetivo |
Los módulos activos están incluidos en --sources all. Para ejecutarlos explícitamente:
# 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 activeNote
La transferencia de zona (AXFR) solo tiene éxito si el nameserver del objetivo está mal configurado. Cuando funciona, filtra toda la zona DNS.
El recorrido de zona NSEC solo funciona cuando el dominio usa NSEC (no NSEC3). NSEC3 usa nombres con hash y bloquea la enumeración. El módulo detecta esto automáticamente y termina de forma controlada.
python simplerecon.py --list-sources| Fuente | Requiere clave | Notas |
|---|---|---|
rapiddns |
No | Dataset de DNS |
jldc |
No | Base de datos de subdominios de Anubis |
crtsh |
No | Certificate Transparency |
crtname |
No | Certificate Transparency - agregador crt.name (lista en texto plano) |
certspotter |
No | Certificate Transparency |
merklemap |
Requerido | Agregador de logs CT (alternativa a crt.sh) - requiere suscripción paga |
anubisdb |
No | Base de datos de subdominios de DNS pasivo de jonlu.ca |
subdomaincenter |
No | Índice gratuito de subdominios respaldado por Netcraft |
threatminer |
No | DNS pasivo / threat intel de ThreatMiner |
urlscan |
Opcional | Mayor límite de tasa con clave |
hackertarget |
Opcional | Mayor límite de tasa con clave |
wayback |
No | API CDX de web.archive.org (paginada, hasta 200k entradas) |
commoncrawl |
No | API CDX de Common Crawl - datos de rastreo histórico independientes |
robtex |
No | DNS pasivo |
alienvault |
Opcional | Threat intelligence de OTX |
bufferover |
No | FDNS de Rapid7 vía TLS |
dnsdumpster |
No | Herramienta de recon DNS (web scraping) |
github |
Requerido | Búsqueda de código para referencias de dominio codificadas |
grep_app |
No | Búsqueda de código en GitHub vía grep.app |
virustotal |
Requerido | Endpoint de subdominios de VT |
securitytrails |
Requerido | Historial DNS |
censys |
Requerido | Búsqueda de certificados |
shodan |
Requerido | Búsqueda de dominios DNS |
grayhatwarfare |
Requerido | Buckets de nube pública (AWS/Azure/GCP) |
leakix |
Opcional | Servicios expuestos y activos en la nube |
fullhunt |
Requerido | Índice completo de hosts y subdominios de Internet |
chaos |
Requerido | Base de datos de subdominios actualizada continuamente por ProjectDiscovery |
c99 |
Requerido | Buscador de subdominios de C99.nl |
netlas |
Requerido | Descubrimiento de activos y subdominios a escala de Internet |
onyphe |
Requerido | Motor de búsqueda de ciberdefensa |
greynoise |
Requerido | Datos de ruido de Internet / escáneres pasivos |
fofa |
Opcional | Búsqueda de activos de Internet FOFA (scraping gratuito como respaldo sin clave) |
pulsedive |
Opcional | Búsqueda de observables de threat-intel |
intelx |
Requerido | Búsqueda de datos filtrados de IntelligenceX |
publicwww |
Requerido | Búsqueda de código fuente en toda la web pública |
bevigil |
Requerido | OSINT de BeVigil - subdominios extraídos de apps móviles |
hunterhow |
Requerido | Base de datos de activos globales de Hunter.how |
urlhaus |
No | Base de datos de URLs maliciosas URLhaus de abuse.ch - sin autenticación necesaria |
circl |
No (autenticación básica opcional) | DNS pasivo de CIRCL - nivel gratuito público; la clave da mayor tasa |
bing |
No | Búsqueda de Bing - scraping anti-bot con rotación de UA multi-plantilla |
| Fuente | Requiere clave | Notas |
|---|---|---|
zone_transfer |
No | Transferencia de zona DNS (AXFR) en nameservers públicos |
ns_brute |
No | Descubrimiento de NS secundarios (SOA MNAME + fuerza bruta de 16 prefijos) + AXFR/IXFR en todos los candidatos |
dns_mining |
No | Minería de registros SPF / DMARC / MX |
caa_enum |
No | Minería del campo iodef: de CAA - filtra hostnames internos de valores mailto: y https:// |
nsec_walk |
No | Recorrido de zona NSEC de DNSSEC |
srv_enum |
No | Enumeración de registros SRV (~70 prefijos de servicio) |
spider |
No | Crawler HTML BFS + minero de JS: sigue <a href> / <link href> (profundidad 2, 100 páginas), recopila archivos JS de <script src>, extrae subdominios del contenido JS, sigue referencias de sourcemap .map |
robots_sitemap |
No | Directivas de robots.txt + extracción recursiva de hostnames <loc> de sitemap.xml / <sitemapindex> (máx. 20 sitemaps, profundidad 2) |
ptr_sweep |
No | Barrido de PTR (DNS inverso) en bloques /24 que contienen IPs del objetivo |
asn_sweep |
No | Resuelve el ASN del objetivo vía bgp.he.net → obtiene todos los prefijos IPv4 que posee (≤/20) → barrido de PTR |
vhost_probe |
No | Fuerza bruta de virtual-hosts mediante fuzzing del header HTTP Host (lista incorporada de más de 130 palabras) |
| Wordlist | Tamaño | Caso de uso |
|---|---|---|
subdomains-top1million-5000.txt |
5k | Escaneo inicial rápido |
subdomains-top1million-20000.txt |
20k | Bug bounty estándar |
subdomains-top1million-110000.txt |
110k | Enumeración exhaustiva |
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.txtPor defecto la herramienta usa 6 resolvers incorporados (Google, Cloudflare, Quad9, OpenDNS). Para fuerza bruta a gran escala, suministra una lista más grande:
# 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 acepta:
- Una ruta de archivo local (
config/resolvers.txt, una IP por línea, se admiten comentarios con#, se acepta el formatoip:port) - Una URL
http://ohttps://(descargada automáticamente víahttpx)
La lista se deduplica y se mezcla (shuffle) automáticamente para distribuir la carga entre todos los resolvers.
Otras fuentes públicas de resolvers:
# 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--tld-brute descubre registros activos del dominio objetivo bajo otros TLDs (p. ej. target.net, target.io, target.com.br). Útil para protección de marca, detección de typosquatting y mapeo del portafolio completo de dominios de un objetivo.
# 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.jsonLa herramienta elimina el TLD actual del objetivo (manejando automáticamente TLDs compuestos como .co.uk y .com.br), y luego resuelve {base}.{tld} para cada entrada de la wordlist. Solo se devuelven las variantes que resuelven en DNS.
Los resultados aparecen en un campo tld_variants separado en la salida JSON/CSV/NDJSON y se imprimen en la terminal al final de cada ejecución.
La wordlist por defecto es assets/wordlist/tlds.txt. Edítala o suministra tu propio archivo con --tld-brute FILE.
--learn-words analiza los nombres de subdominio ya descubiertos (de fuentes pasivas y activas) y deriva candidatos de fuerza bruta dirigidos a partir de los patrones que encuentra - sin usar ninguna 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| Patrón | Entrada de ejemplo | Candidatos generados |
|---|---|---|
| Secuencias numéricas | api1, api2 |
api3 … api9 (llena huecos y extiende secuencias) |
| Familias de entornos | 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 … |
| Saltos de versión | app-v2 |
app-v1, app-v3 |
| Combinaciones de tokens | dev, api, eu cada uno en ≥2 nombres |
pares faltantes del producto cruzado: dev-eu, api-eu … |
El aprendiz solo devuelve candidatos nuevos - nombres que aún no están en el conjunto descubierto. Al combinarse con --brute, ambos conjuntos de palabras se combinan antes de la resolución DNS, de modo que solo se ejecuta una pasada de fuerza bruta.
El nivel de verbosidad -v 3 expone elementos recopilados durante la enumeración que quedan fuera del dominio objetivo. Útil para mapear infraestructura de socios, descubrir activos relacionados y comprender el ecosistema más amplio de un objetivo. (El nivel 3 también habilita el registro de vista previa del cuerpo 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| Categoría | Fuente | Ejemplos |
|---|---|---|
| Hosts externos | Las 46 fuentes, vía _filter() |
partner.com, cdn.cloudfront.net, internal.corp encontrados en SANs de certificados o en código |
| IPs | --verify-live (resueltas por subdominio) |
1.2.3.4, 2606:4700:: |
| URLs | Rastreo de spider (páginas + JS + .map), además de wayback, commoncrawl, urlscan, alienvault |
https://target.com/api/v1, https://target.com/static/app.js |
txt - secciones adjuntas (encabezados suprimidos con --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" de nivel 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 - líneas adicionales con el 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 - filas extra con type = extra_host, extra_ip, extra_url.
# 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'Convierte el resultado plano de la enumeración en una topología de red navegable: un grafo JSON (nodos + aristas) que puedes canalizar hacia otras herramientas, y una página HTML interactiva para la triage visual. El grafo se construye enteramente a partir de los datos ya recopilados durante la ejecución - sin solicitudes ni escaneos adicionales.
Los tres flags operan de forma independiente y se pueden combinar:
| Flag | Rol | La salida aterriza en | ¿Combinable? |
|---|---|---|---|
-o html |
Formato de salida principal - reemplaza a txt/json/csv/ndjson |
stdout o --outfile |
Un solo -o a la vez |
--network-html FILE |
Artefacto secundario - siempre escribe la visualización HTML en FILE |
FILE (cualquier ruta) |
Sí - funciona junto con cualquier -o |
--network-map |
Inyecta un bloque "network" (nodos/aristas) en la salida JSON |
Dentro del documento JSON | Solo tiene sentido con -o json; se habilita automáticamente con -o html/--network-html |
# 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| Tipo de nodo | Construido a partir de | Notas |
|---|---|---|
domain |
objetivo del escaneo | uno por objetivo |
subdomain |
result.subdomains |
coloreado según live.status (2xx/3xx/4xx/5xx/ninguno) |
ip |
live[sub].ips (necesita --verify-live) |
compartido entre subdominios que comparten una IP |
cloud |
live[sub].cloud |
un nodo por proveedor (AWS / Cloudflare / GCP / …) |
cname |
live[sub].cname |
solo cuando el CNAME apunta fuera de la zona del objetivo |
tld_variant |
result.tld_variants (necesita --tld-brute) |
enlazado de vuelta al dominio raíz |
| Relación de arista | Dirección |
|---|---|
has_subdomain |
domain → subdomain |
resolves_to |
subdomain → ip |
hosted_on |
subdomain → cloud |
cname_to |
subdomain → cname |
tld_variant_of |
tld_variant → domain |
{
"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}
}
}Archivo único autocontenido. Carga vis-network 9.1.9 desde unpkg.com (CDN - requiere internet al abrirlo). Características:
- Layout de fuerza dirigida (force-directed) con zoom, paneo, botones de navegación, controles de teclado
- Clic en un nodo → panel de detalle con estado HTTP, título, header del servidor
- Leyenda con conteos por tipo y clave de colores de estado
- Todos los objetivos de una ejecución multi-objetivo se combinan en un solo 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.jsonEn lugar de una única sonda de subdominio aleatorio (poco confiable bajo balanceo de carga DNS), la herramienta lanza --wildcard-tests N sondas y confirma el wildcard solo cuando ≥ ceil(N/2) resuelven. La unión de todas las IPs devueltas se usa como conjunto de filtro durante la fuerza bruta.
python simplerecon.py -d target.com --brute wordlist.txt --wildcard-tests 5Después de la fuerza bruta masiva con resolvers públicos económicos, se revalida solo contra Google/Cloudflare. Elimina los falsos positivos causados por envenenamiento DNS en resolvers no confiables.
python simplerecon.py -d target.com --brute wordlist.txt --validate-resolversDurante --verify-live, la herramienta realiza un handshake SSL crudo en el puerto 443 y extrae los Subject Alternative Names del certificado del servidor. Los hostnames recién descubiertos se agregan de vuelta al conjunto de subdominios.
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 hostLos registros NSEC forman una lista enlazada ordenada de todos los nombres de la zona DNS. La fuente nsec_walk recorre la cadena para enumerar toda la zona sin una transferencia de zona. Solo funciona cuando el dominio usa NSEC (no NSEC3 - el módulo lo detecta e informa automáticamente).
python simplerecon.py -d target.com --sources nsec_walk -v
# Example domain with NSEC: nlnetlabs.nlConsulta ~70 prefijos SRV comunes (_http._tcp, _ldap._tcp, _kerberos._tcp, _autodiscover._tcp, _sip._tcp, etc.). Los registros SRV frecuentemente revelan hostnames internos no encontrados por fuentes pasivas.
python simplerecon.py -d target.com --sources srv_enum -vLa lista de prefijos está en config/srv_prefixes.json - edítala para agregar servicios específicos del dominio.
Después de enumerar target.com, los subdominios descubiertos como api.target.com se usan ellos mismos como objetivos de enumeración para encontrar entradas más profundas (v2.api.target.com, internal.api.target.com, etc.).
python simplerecon.py -d target.com --recursive --recursive-depth 2Busca en GitHub archivos de código fuente que contengan referencias al dominio objetivo (subdominios codificados en configs, archivos .env, scripts de CI). Requiere un github_token en config/api_keys.json.
python simplerecon.py -d target.com --sources github -vCuando --verify-live está habilitado, cada host activo se verifica en busca de señales de takeover mediante tres métodos independientes:
| Método | Cómo | Campo |
|---|---|---|
| Fingerprint de cuerpo | El cuerpo de la respuesta se compara contra ~30 firmas de servicios | takeover |
| Cadena de CNAME | Se recorre la cadena completa de CNAME; el sufijo se compara contra 21 servicios | takeover: "cname:<service>" |
| WAF / CDN | Los headers de respuesta se comparan contra 11 proveedores | 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]
Servicios detectables para takeover (cuerpo + 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
Todos los fingerprints están definidos en verify/live_check.py (_TAKEOVER_CNAME, _TAKEOVER_BODY, _WAF_HEADERS) - edita ahí para agregar nuevos servicios.
Important
Una coincidencia positiva es un candidato, no un takeover confirmado. Siempre verifica la propiedad del CNAME antes de reportarlo.
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------------------------------------------------------------
[*] 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
...
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
}
}python simplerecon.py -d target.com --verify-live -o csv --outfile results/target.csvdomain,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,,,,,,,,,,
Una línea JSON compacta por subdominio - diseñado para streaming y canalización con jq.
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"}Ejemplos de 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'python simplerecon.py -d target.com -o txt --outfile results/target.txtpython simplerecon.py -d target.com --verify-live -o html --outfile results/target.htmlGenera una página HTML autocontenida que renderiza la topología descubierta como un grafo interactivo (nodos: domain / subdomain / IP / cloud / CNAME / variante TLD - aristas: has_subdomain, resolves_to, hosted_on, cname_to, tld_variant_of). Carga vis-network desde una CDN, por lo que necesita acceso a internet al abrirse. Los nodos de subdominio se colorean según el estado HTTP (verde 2xx, naranja 3xx, rojo 4xx, morado 5xx, gris inalcanzable). Consulta Mapeo de Red para más detalles.
python simplerecon.py -d target.com --verify-live -o markdown --outfile report.mdGenera un documento Markdown estructurado con métricas resumen, tablas de hosts activos, candidatos a takeover, hashes de cuerpo duplicados, todos los subdominios, variantes de TLD, contribuciones por fuente y extras - listo para pegar en issues de GitHub, wikis, o entregar a clientes. Consulta Informe en Markdown para más detalles.
-o markdown produce un informe de reconocimiento completo como un único archivo .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 -| Sección | Aparece cuando |
|---|---|
| Encabezado (fecha, fuentes, totales) | Siempre |
| Tabla de métricas resumen | Siempre |
| ⚠ Candidatos a Takeover | Cuando --verify-live detecta algún takeover |
| Tabla de Hosts Activos (estado, título, servidor, cloud, WAF, ms) | --verify-live |
| Hashes de Cuerpo Duplicados | ≥2 hosts comparten el mismo hash de respuesta |
| Todos los Subdominios (bloque de código) | Siempre |
| Variantes de TLD | --tld-brute |
| Extras (hosts, IPs, URLs) | -v 3 |
| Contribuciones por Fuente | Siempre |
La sección duplicate_bodies marca los hosts que devolvieron contenido de respuesta idéntico - una señal confiable de DNS wildcard o granjas de CDN que no fueron detectadas por el filtro de wildcard.
Persiste cada ejecución en un archivo SQLite, compara nuevos hallazgos contra ejecuciones pasadas, y lee los datos almacenados de vuelta - todo con el módulo estándar sqlite3 de Python (sin dependencias adicionales). Hay dos almacenes:
- Base de datos de resultados por objetivo -
--db FILE(una ruta que tú eliges). Contiene únicamente resultados de recon (subdominios, URLs, extras, variantes de TLD), y también es la fuente de comparación para--db-news. Archivos diferentes son almacenes independientes. - Base de datos de sistema fija -
config/system.db(resuelta en relación con la instalación, nunca se pasa como parámetro). Contiene datos de sistema/log: el log de historial de comandos (una fila por ejecución) y los trabajos del scheduler de watch (ver Monitoreo Continuo).
Ej.: inurl.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 3Una ejecución con --db almacena los subdominios descubiertos (más metadatos de live-check cuando se usa --verify-live), variantes de TLD, URLs recopiladas (etiquetadas con la fuente que las encontró), y - en -v 3 - hosts externos e IPs. La línea de comando ejecutada se registra por separado, en el log de comandos fijo de config/system.db (en cada ejecución, incluso sin --db).
--db-news compara la ejecución actual contra todo lo almacenado en --db y muestra (y guarda) solo los valores no vistos antes - monitoreo continuo integrado, que reemplaza la receta manual de 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.jsonLee los datos almacenados de vuelta como líneas planas aptas para pipes (sin ejecutar enumeraciones):
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 -silentSolo-adición (append-only), con clave domain (sin tabla de ejecución/padre — la auditoría de comandos vive en config/system.db).
--db por objetivo (solo resultados):
| Tabla | Contenido |
|---|---|
subdomains |
subdominios descubiertos + campos de live-check (status, title, ips, cloud, takeover, waf, …) |
tld_variants |
variantes de TLD activas de --tld-brute |
extras |
hosts externos e IPs (guardados en -v 3) |
urls |
URLs recopiladas, cada una etiquetada con su fuente de origen |
config/system.db fija (log de sistema + scheduler):
| Tabla | Contenido |
|---|---|
command_history |
una fila por ejecución - command, targets, timestamp |
watch_jobs |
trabajos del scheduler de cron - command, schedule, last_run (ver Monitoreo Continuo) |
Las URLs se recopilan de spider, wayback, commoncrawl, urlscan y alienvault (toda URL dentro del alcance que encuentren) y se almacenan independientemente del nivel de verbosidad (a diferencia de los hosts/IPs de extras, que necesitan -v 3). Consulta cualquier cosa directamente con sqlite3:
sqlite3 recon.db 'SELECT url, source FROM urls ORDER BY source'
sqlite3 config/system.db 'SELECT DISTINCT command, timestamp FROM command_history'Note
Las bases de datos por objetivo creadas por versiones anteriores se migran automáticamente en el primer uso - las tablas anteriores history/scan_id (o las más antiguas scans/command_history) se pliegan en el esquema plano de solo-resultados sin pérdida de datos.
Un scheduler de cron incorporado: registra comandos de recon una vez, y luego ejecuta un daemon que los dispara según el horario. Los trabajos (y sus horarios) viven en el config/system.db fijo — no se necesita cron/systemd externo. Sin dependencias adicionales (matcher de cron de 5 campos hecho a medida).
Agrega --watch-add "<cron>" a cualquier comando normal. El comando (sin --watch-add) se almacena junto con el horario:
# 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 --quietEl horario es una expresión cron estándar de 5 campos (minuto hora día-del-mes mes día-de-la-semana) que admite *, listas (0,15,30), rangos (9-17) y pasos (*/30).
python simplerecon.py --watch # no --db needed; Ctrl-C to stopCada minuto el daemon recarga watch_jobs, y se lanza cada trabajo cuyo cron coincide con el minuto actual. Los trabajos que vencen en el mismo minuto se ejecutan en paralelo (cada uno como su propio subproceso, persistiendo en su propio --db). Los trabajos recién agregados se detectan en vivo sin reiniciar el daemon.
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 una tabla alineada:
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
Ejemplo
# Cron
* * * * *
│ │ │ │ └─── Día de la semana (0 - 6, donde 0 = domingo)
│ │ │ └───── Mes (1 - 12)
│ │ └─────── Día del mes (1 - 31)
│ └───────── Hora (0 - 23)
└─────────── Minuto (0 - 59)
Cuando el daemon dispara un trabajo, imprime el comando programado, p. ej.
[+] [watch] 22:45 fired job #1: /abs/simplerecon.py -d target.com --profile fast --db target.db --quiet.
Tip
Ejecuta --watch-add y --watch desde el mismo directorio de trabajo para que las rutas relativas dentro del comando almacenado (p. ej. --db target.db) se resuelvan de forma consistente.
Cuando --verify-live está habilitado, cada host activo también se resuelve a sus direcciones IP y se detecta el proveedor de nube (fingerprinting) - ambos campos aparecen en todos los formatos de salida.
python simplerecon.py -d target.com --verify-live -o json --outfile out.jsonOrden de detección:
- Cadena de CNAME - comparada contra sufijos de proveedores conocidos (
*.amazonaws.com,*.azurewebsites.net,*.run.app, etc.) - Rangos CIDR de IP - las direcciones IP se comparan contra los rangos publicados de AWS, Azure, GCP, Cloudflare, Fastly
Proveedores 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")'Enruta todas las solicitudes HTTP (fuentes pasivas, fuentes activas, --verify-live) a través de un 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:9050python simplerecon.py -d target.com --user-agent 'Mozilla/5.0 (compatible; MyScanner/1.0)'# 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# 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,virustotalpython 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 200python simplerecon.py -d target.com -o txt --outfile subs.txt
nmap -iL subs.txt -p 80,443,8080,8443 -T4 --openpython simplerecon.py -d target.com --no-banner \
| httpx -silent \
| nuclei -t cves/ -silent# Find potential subdomain takeovers
python simplerecon.py -d target.com --no-banner \
| dnsx -silent -cname -resp \
| grep -E 'amazonaws|azurewebsites|github.io|herokuapp'python simplerecon.py -d target.com --verify-live -o txt --outfile subs.txt
eyewitness --web -f subs.txt --no-prompt -d screenshots/String-x (aka strx) es una herramienta de automatización modular que usa un marcador de posición {STRING}. Se combina naturalmente con SimpleReconDomain vía 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" -pmTodas las fuentes heredan de BaseSource en sources/base.py. Coloca el archivo en sources/passive/ o sources/active/ - no hace falta editar ningún otro archivo.
El nombre de la clase debe ser el nombre de archivo en title-case (p. ej. myservice.py → clase Myservice), y NAME debe ser igual al nombre de archivo sin .py.
El método fetch() de una fuente devuelve los subdominios dentro del alcance que encontró. Más allá de eso, la clase base permite que cualquier fuente alimente tres canales de datos que fluyen hacia la salida (los extras se muestran en -v 3) y hacia el almacén SQLite (--db):
| Dato | Cómo aportarlo | Dónde aterriza |
|---|---|---|
| Dominios (subdominios dentro del alcance) | return self._filter(found, domain) |
conjunto de resultados - todos los formatos de salida |
| Hosts externos e IPs | automático - _filter() enruta los elementos descartados fuera de alcance a self.extras['hosts'] / self.extras['ips'] (las IPs se detectan automáticamente) |
extras (se muestra en -v 3; tabla extras en --db) |
| URLs | self._add_url(url, domain) - verificado contra el alcance, deduplicado, con tope (_MAX_URLS_PER_SOURCE) |
extras['urls'] + la tabla urls (atribuida a la fuente, persistida en cada ejecución con --db) |
Regla general: enruta siempre los hallazgos crudos a través de self._filter(...) (normaliza mayúsculas/minúsculas, elimina *., y recolecta gratis los hosts/IPs fuera de alcance), y llama a self._add_url(...) para cualquier URL completa que encuentres. Rara vez necesitarás escribir directamente en self.extras.
# 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)Agrega la clave a config/api_keys.json:
{ "myservice": "your-api-key-here" }Usa asyncio.wait_for + run_in_executor para las llamadas DNS bloqueantes, para evitar cuelgues:
# 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)Este proyecto está licenciado bajo la Apache License - consulta el archivo LICENSE para más detalles.
MrCl0wn
- 🌐 Blog: http://blog.mrcl0wn.com
- 🐙 GitHub: @MrCl0wnLab
- 🐦 Twitter: @MrCl0wnLab
- 📧 Email: mrcl0wnlab@\gmail.com
¡Las contribuciones de cualquier tipo son bienvenidas!
⭐ ¡Si este proyecto te fue útil, considera darle una estrella!
💡 ¡Las sugerencias y comentarios siempre son bienvenidos!
💀 ¡Hacker, a hackear!







