Skip to content

Latest commit

 

History

History
1603 lines (1197 loc) · 68.4 KB

File metadata and controls

1603 lines (1197 loc) · 68.4 KB

(SRDomain) Simple Recon - Domain v2.0.0

Herramienta de enumeración de dominios pasiva y activa para flujos de trabajo de OSINT y reconocimiento.

Português English Español

Simple Recon URL

Python Versión Linux macOS

Licencia Colaboradores Issues abiertos Forks Stars

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.

Tabla de Contenidos


Instalación

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

Dependencias (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>)

Docker

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

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


Claves de API

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:

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

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

Captura de pantalla

Ejemplos de Contexto OSINT

Bug 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.json

Enumeració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.json

Fuerza 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 40

Recon 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.json

Recorrido de zona DNSSEC (requiere NSEC, no NSEC3):

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

Descubrimiento de activos a partir de una lista de dominios:

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

Modo silencioso - envía los subdominios directamente a otra herramienta por pipe:

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

Todos los 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

Captura de pantalla


Perfiles

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}
  }
}

Preajustes de Run-Config

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

Precedencia (de mayor a menor):

  1. Flags explícitos de CLI (siempre ganan)
  2. Valores del archivo JSON de --config
  3. 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-examples

Módulos Pasivos vs Activos

Pasivo

Las 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

Activo

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 active

Note

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.


Fuentes

python simplerecon.py --list-sources

Fuentes Pasivas (40)

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

Fuentes Activas (11)

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)

Captura de pantalla


Fuerza Bruta de DNS

Wordlists Recomendadas

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

Resolvers DNS Personalizados

Por 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 formato ip:port)
  • Una URL http:// o https:// (descargada automáticamente vía httpx)

La lista se deduplica y se mezcla (shuffle) automáticamente para distribuir la carga entre todos los resolvers.

Otras fuentes públicas de resolvers:

Ejemplo Completo de Fuerza 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

Fuerza Bruta de TLD

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

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


Aprendizaje de Wordlist - Candidatos Derivados de Patrones

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

Qué patrones se detectan

Patrón Entrada de ejemplo Candidatos generados
Secuencias numéricas api1, api2 api3api9 (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.


Extras - Hosts Externos, IPs y URLs

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

Qué se recopila

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

Salida por formato

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.

Recetas de 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'

Mapeo de Red - Visualización en JSON de Grafo y HTML

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.

Referencia de flags - tres ejes

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

Ejemplos 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 del grafo

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

Forma del JSON (con --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}
  }
}

Visor HTML

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

Captura de pantalla

Recetas de jq para el bloque del 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 Avanzadas

Detección de Wildcard con Múltiples Sondas (PureDNS)

En 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 5

Validación en Dos Pasadas con Resolvers de Confianza (PureDNS)

Despué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-resolvers

Extracción de SAN de Certificados TLS (Amass)

Durante --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 host

Recorrido de Zona DNSSEC NSEC (Amass)

Los 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.nl

Enumeración de Registros SRV (Amass)

Consulta ~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 -v

La lista de prefijos está en config/srv_prefixes.json - edítala para agregar servicios específicos del dominio.

Enumeración Recursiva (Subfinder)

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 2

Búsqueda de Código en GitHub

Busca 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 -v

Detección de Subdomain Takeover

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

Recetas de jq para triage 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 Salida

Terminal (por defecto)

------------------------------------------------------------
[*] 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

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'

TXT

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

HTML - Mapa de red interactivo

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

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

Markdown - Informe de reconocimiento legible para humanos

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

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


Informe en Markdown

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

Secciones incluidas

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.


Base de Datos - Persistencia SQLite

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

Captura de pantalla

Ej.: inurl.db

Guardar 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

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

Solo lo nuevo desde la última ejecución - --db-news

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

Inspeccionar la base de datos - --db-list

Lee 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 -silent

Captura de pantalla

Esquema

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


Monitoreo Continuo (--watch)

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

Registrar un trabajo - --watch-add "CRON"

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 --quiet

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

Ejecutar el scheduler - --watch

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

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

Gestionar trabajos

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)

Captura de pantalla

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.


Uso Avanzado

Detección de IP y Proveedor de Nube

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

Orden de detección:

  1. Cadena de CNAME - comparada contra sufijos de proveedores conocidos (*.amazonaws.com, *.azurewebsites.net, *.run.app, etc.)
  2. 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")'

Soporte de proxy

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:9050

User-Agent personalizado

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

Excluir fuentes

# 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

Objetivos por 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 - sondeo 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 - escaneo de puertos

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

nuclei - escaneo de vulnerabilidades

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

dnsx - resolución DNS y seguimiento de 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 - capturas de pantalla

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

String-x - enriquecimiento y automatización

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" -pm

Creación de un Nuevo Módulo

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

Qué puede aportar una fuente

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.

Nueva Fuente Pasiva

# 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" }

Nueva Fuente Activa

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)

📄 LICENCIA

Este proyecto está licenciado bajo la Apache License - consulta el archivo LICENSE para más detalles.

👨‍💻 AUTOR

MrCl0wn


Contribuciones ✨

¡Las contribuciones de cualquier tipo son bienvenidas!

Lista de colaboradores

⭐ ¡Si este proyecto te fue útil, considera darle una estrella!

💡 ¡Las sugerencias y comentarios siempre son bienvenidos!

💀 ¡Hacker, a hackear!