# Capability: recon Reconocimiento de red para OSINT desde el registry: lookups de registro (WHOIS/RDAP), DNS, sondeo de disponibilidad y ruta (ping/traceroute), escaneo de puertos y servicios, y fingerprint de la tecnologia web de un sitio (estilo Wappalyzer). El escaneo de puertos tiene dos caminos: el wrapper pesado de `nmap` (perfiles, scripts NSE, versiones), y un **camino nativo en Python puro** (`scan_tcp_ports` + `grab_service_banner` + `identify_port_service`, solo stdlib, sin nmap ni sudo) para escaneo rapido y portable. El fingerprint web sigue el mismo patron pura/impura: `fetch_http_fingerprint` recoge las señales (headers, html, cookies) y `detect_web_tech` (pura) matchea firmas para identificar servidor, CMS, frameworks JS, analytics y CDN. La mayoria de funciones son Python impuras, wrappean CLIs del sistema (`whois`, `rdap`, `dig`, `ping`, `traceroute`, `nmap`) o usan sockets/urllib stdlib, y devuelven siempre un dict `{status: ok|error}` sin lanzar excepciones. El grupo cierra el bucle con un **sink comun** que archiva cada escaneo en el ecosistema OSINT (nota Obsidian + registro DuckDB) y pipelines one-shot que escanean y guardan en una sola llamada. Comparte tag y dominio (`cybersecurity`) con el grupo `osint-passive` (recoleccion no intrusiva desde fuentes publicas), del que reutiliza primitivas. La regla de operacion es la misma del project `osint`: **todo escaneo se archiva en OSINT**. ## Funciones | ID | Firma | Que hace | |---|---|---| | `whois_lookup_py_cybersecurity` | `whois_lookup(target, timeout_s=30) -> dict` | Lookup WHOIS via el CLI `whois`. Captura el `raw` completo y parsea best-effort registrar, registrant_country, creation_date, expiry_date, updated_date, name_servers. Acepta dominio o IP. | | `rdap_lookup_py_cybersecurity` | `rdap_lookup(target, timeout_s=30) -> dict` | Lookup RDAP (reemplazo JSON moderno de WHOIS) via el CLI openrdap `rdap`. Devuelve `data` (dict JSON), `handle`, `ldhName` y el `raw`. Acepta dominio, IP o ASN (`AS15169`). | | `dns_records_py_cybersecurity` | `dns_records(domain, record_types=None, timeout_s=20) -> dict` | Registros DNS via `dig +short` (default A, AAAA, MX, NS, SOA, TXT, CNAME). Devuelve `records` (dict por tipo) y `raw` legible por bloque para el vault. | | `ping_host_py_cybersecurity` | `ping_host(host, count=4, timeout_s=30) -> dict` | Sondeo ICMP via `ping`. Devuelve `loss_pct`, `rtt_avg_ms` (y min/max), `packets_sent`/`recv`, `raw`. Host filtrado = `status:ok` con `loss_pct=100`, no error. | | `traceroute_host_py_cybersecurity` | `traceroute_host(host, max_hops=30, timeout_s=60) -> dict` | Traza la ruta via `traceroute`. Devuelve `hops` (lista de `{hop, hosts:[{name, ip, rtt_ms}]}`) y `raw`. Hops filtrados (`* * *`) = `hosts: []`. | | `nmap_scan_py_cybersecurity` | `nmap_scan(target, profile="quick", ports=None, extra_args=None, out_dir=None, timeout_s=1800) -> dict` | Escaneo de puertos/servicios via `nmap` por perfiles (salida XML parseada). Devuelve `open_ports`, `hosts_up`, `xml_path`, `raw`, `elapsed_s`. Funcion estrella del grupo. | | `scan_tcp_ports_py_cybersecurity` | `scan_tcp_ports(host, ports="common", timeout_s=1.0, workers=100) -> dict` | **Connect-scan TCP nativo (stdlib, sin nmap ni sudo).** Escanea puertos en paralelo con threads y clasifica cada uno en open/closed/filtered. `ports` acepta lista, preset `"common"`, rango `"1-1024"` o CSV. Devuelve `open` (lista de ints), `ip`, `raw`. NO detecta version de servicio. | | `grab_service_banner_py_cybersecurity` | `grab_service_banner(host, port, timeout_s=3.0, send_probe=True) -> dict` | **Banner grab nativo (stdlib, sin nmap -sV).** Abre socket TCP, lee el banner e identifica el servicio real (ssh, http, ftp, smtp, mysql, redis, pop3, imap, telnet...) extrayendo `product` y `version` best-effort. Dice QUE habla detras de un puerto abierto. TLS/HTTPS no da banner plano. | | `identify_port_service_py_cybersecurity` | `identify_port_service(port, proto="tcp") -> dict` | **Pure.** Mapea un puerto a su servicio IANA well-known esperado por convencion (`{service, description, known}`) desde una tabla embebida (~120 puertos). No sondea en vivo: dice que se ESPERA, no que hay. | | `save_scan_to_osint_py_cybersecurity` | `save_scan_to_osint(target, scan_type, raw, summary=None, vault_dir="~/Obsidian/osint", service_url="http://127.0.0.1:8771", tool=None) -> dict` | **Sink OSINT.** Archiva un scan: nota Markdown tipada en el vault (capa critica) + POST a `osint_db` para registro DuckDB (best-effort). Devuelve `note_path`, `registered`, `scan_id`. | | `recon_osint_py_pipelines` | `recon_osint(target, scan_type="whois", save=True, profile="quick", ...) -> dict` | **Pipeline one-shot.** Ejecuta un scan del tipo pedido y lo archiva en OSINT en una sola llamada (compone la funcion de scan + `save_scan_to_osint`). El camino canonico para recon + archivado. | | `scan_port_services_py_pipelines` | `scan_port_services(host, ports="common", timeout_s=1.0, workers=100, grab_banners=True, banner_timeout_s=3.0, save=True) -> dict` | **Pipeline one-shot nativo.** Escanea puertos y, por cada abierto, devuelve servicio esperado (IANA) + servicio/version real del banner. Compone `scan_tcp_ports` + `identify_port_service` + `grab_service_banner` (+ sink OSINT). Reemplaza el patron scan→identify→grab sin nmap. | | `fetch_http_fingerprint_py_cybersecurity` | `fetch_http_fingerprint(url, timeout_s=15.0, verify_tls=True, max_html_bytes=500000, user_agent=None) -> dict` | **Fetch de señales web (stdlib).** GET con UA de navegador, sigue redirects, descomprime gzip. Devuelve `headers` (lowercase), `cookies` (solo NOMBRES, sin valores), `html`, `title`, `server`, `status_code`, `final_url`, `raw`. Capa impura del fingerprint web. | | `detect_web_tech_py_cybersecurity` | `detect_web_tech(headers, html="", cookies=None, final_url="") -> dict` | **Pure. Detector de tecnologia web estilo Wappalyzer.** Matchea ~50 firmas embebidas (regex) contra headers/html/cookies → `technologies[{name, category, version, confidence, evidence}]`, `by_category`, `count`. Cubre server, lenguaje, CMS, frameworks JS, librerias, analytics, CDN, e-commerce, WAF. | | `fingerprint_web_stack_py_pipelines` | `fingerprint_web_stack(url, timeout_s=15.0, verify_tls=True, max_html_bytes=500000, save=True) -> dict` | **Pipeline one-shot = Wappalyzer del registry.** url → tecnologias detectadas. Compone `fetch_http_fingerprint` + `detect_web_tech` (+ sink OSINT). El camino canonico para fingerprint web. | ### OSINT pasivo relacionado Estas funciones llevan tambien el tag `recon` (y `osint-passive`): recoleccion no intrusiva desde fuentes publicas, sin tocar al objetivo. Utiles antes o junto al escaneo de red. Pagina madre completa: `docs/capabilities/osint-passive.md`. | ID | Firma | Que hace | |---|---|---| | `build_search_dorks_py_cybersecurity` | `build_search_dorks(target, tipo="persona", extra_domains=None) -> list` | **Pure.** Genera dorks de buscador (frase exacta, `site:`, `filetype:`, leaks/pastebin) segun el tipo de target. Sin red. | | `enum_subdomains_crtsh_py_cybersecurity` | `enum_subdomains_crtsh(dominio, timeout_s=20.0) -> list` | Enumera subdominios desde Certificate Transparency (crt.sh). Dedup, ordenado, sin wildcards. | | `enumerate_username_sites_py_cybersecurity` | `enumerate_username_sites(username, timeout_s=8.0, sites=None) -> list` | Comprueba si un username existe en ~12 sitios publicos (estilo sherlock ligero) por codigo HTTP. | | `guess_email_formats_py_cybersecurity` | `guess_email_formats(nombre, apellidos, dominio) -> list` | **Pure.** Genera candidatos de email comunes (nombre.apellido, inicial+apellido, ...). Sin red. | | `enrich_org_passive_py_cybersecurity` | `enrich_org_passive(dominio) -> dict` | Orquestador: perfil pasivo de una organizacion componiendo whois + dns + subdominios crt.sh. | ## Ejemplo canonico end-to-end **1. One-shot (preferido): escanear y archivar en una llamada.** El pipeline corre el scan y lo guarda en OSINT (nota + registro DuckDB) por ti. ```bash cd /home/enmanuel/fn_registry ./fn run recon_osint ejemplo.com whois ``` Equivalente desde Python (cuando necesitas el dict de resultado): ```bash python/.venv/bin/python3 - <<'PYEOF' import sys sys.path.insert(0, "python/functions") from pipelines.recon_osint import recon_osint res = recon_osint("ejemplo.com", scan_type="whois", save=True) print(res["status"], res.get("note_path"), res.get("registered")) PYEOF ``` **2. Manual atomico + sink.** Cuando quieres controlar el scan (perfil, puertos, summary propio) y guardarlo aparte. La funcion de scan se importa, no se reescribe. ```bash cd /home/enmanuel/fn_registry python/.venv/bin/python3 - <<'PYEOF' import sys sys.path.insert(0, "python/functions") from cybersecurity import dns_records from cybersecurity.save_scan_to_osint import save_scan_to_osint scan = dns_records("ejemplo.com") # 1. escanear if scan["status"] == "ok": saved = save_scan_to_osint( # 2. archivar en OSINT "ejemplo.com", "dns", scan["raw"], summary={"A": scan["records"].get("A"), "MX": scan["records"].get("MX")}, tool="dig", ) print(saved["note_path"], "registered:", saved["registered"]) PYEOF ``` **3. nmap largo en segundo plano.** Los perfiles pesados tardan de minutos a horas: lanzalos en background con `out_dir` (conserva el XML) y `timeout_s` alto, y archiva al terminar. ```bash cd /home/enmanuel/fn_registry # El pipeline one-shot tambien sirve para nmap; lanzar en background por la duracion: nohup ./fn run recon_osint scanme.nmap.org nmap --profile full-tcp --timeout-s 7200 \ > /tmp/recon-fulltcp.log 2>&1 & ``` > `scanme.nmap.org` es el host oficial de pruebas de nmap (legal escanear). Cualquier otro objetivo de terceros exige autorizacion. **4. Escaneo nativo de servicios de puertos (sin nmap), one-shot.** Cuando no quieres depender de `nmap`/sudo o buscas un barrido rapido y portable: el pipeline `scan_port_services` escanea los puertos y, por cada abierto, dice el servicio esperado por convencion (IANA) y el servicio/version real leido del banner. ```bash cd /home/enmanuel/fn_registry python/.venv/bin/python3 - <<'PYEOF' import sys sys.path.insert(0, "python/functions") from pipelines.scan_port_services import scan_port_services res = scan_port_services("scanme.nmap.org", ports="common", save=True) print(res["status"], "abiertos:", res.get("open_ports")) for s in res.get("services", []): print(f" {s['port']}: esperado={s['expected_service']} real={s.get('actual_service')} version={s.get('version')}") PYEOF ``` Las primitivas tambien sirven sueltas: `scan_tcp_ports(host, ports)` para solo el estado de los puertos, `grab_service_banner(host, port)` para identificar un servicio concreto, e `identify_port_service(port)` (pura) para el servicio esperado por convencion. **5. Fingerprint de tecnologia web (Wappalyzer del registry), one-shot.** Identifica el stack de un sitio — servidor, lenguaje, CMS, frameworks JS, analytics, CDN — desde el HTML + cabeceras + cookies, sin ejecutar JS. El pipeline `fingerprint_web_stack` hace fetch + matching de firmas en una llamada. ```bash cd /home/enmanuel/fn_registry python/.venv/bin/python3 - <<'PYEOF' import sys sys.path.insert(0, "python/functions") from pipelines.fingerprint_web_stack import fingerprint_web_stack res = fingerprint_web_stack("https://example.com", save=True) print(res["status"], "->", res.get("count"), "tecnologias") for t in res.get("technologies", []): print(f" {t['name']} [{t['category']}] v={t['version']!r} ({t['confidence']})") PYEOF ``` Las dos capas tambien sueltas: `fetch_http_fingerprint(url)` para inspeccionar cabeceras+html+cookies crudos de una URL, y `detect_web_tech(headers, html, cookies)` (pura) para matchear firmas sobre señales ya recogidas (testeable sin red). > Limite: un fetch estatico NO ejecuta JavaScript. Una SPA que monta su framework en runtime (React/Vue con HTML inicial vacio) puede no detectarse. Para esos casos, recoger el DOM renderizado via el grupo `browser` (CDP) y pasar ese html a `detect_web_tech`. ## Integracion OSINT Cada escaneo guardado acaba en **dos sitios**, y por eso `save_scan_to_osint` (y el pipeline `recon_osint`) son el cierre obligatorio del grupo: 1. **Nota Markdown en el vault** `~/Obsidian/osint` bajo `dominios//recon/-.md`. Frontmatter tipado (`tipo: scan-red`, `scan_tipo`, `target`, `slug`, `fecha`, `herramienta`, `tags: [scan-red, , recon]`) y el `raw` del scan en un bloque de codigo. Es la **capa critica**: si falla, el sink devuelve `status:error`. 2. **Fila en la tabla DuckDB `network_scans`** (schema `main`) del service `osint_db`, via `POST http://127.0.0.1:8771/api/scan`. Columnas: `id, target, target_slug, scan_type, tool, scan_ts, note_path, summary(JSON), created_at`. Es la **capa best-effort**: si el service esta caido o no expone el endpoint, el sink degrada a solo-nota con `registered=False` + `register_warning`, sin romper. El re-ingest del vault NO borra esta tabla. **REGLA: todo escaneo se guarda en OSINT.** No hay scans "sueltos". O usas el pipeline `recon_osint` (scan + archivado en 1 call), o llamas la funcion de scan atomica y a continuacion `save_scan_to_osint` con su `raw`. El slug del target se deriva con `re.sub(r"[^a-z0-9._-]+", "-", target.lower())`. ## Escaneos nmap utiles para segundo plano Los perfiles pesados de `nmap_scan` deben lanzarse en background (`&` / `nohup` / `run_in_background`) por su duracion. Pasa `out_dir` para conservar el XML y sube `timeout_s`. | Perfil | Flags nmap | Cuando usarlo | Duracion | |---|---|---|---| | `full-tcp` | `-p- -T4` | Mapear los 65535 puertos TCP (no solo el top 1000). Cuando buscas servicios en puertos no estandar. | Minutos a horas → background | | `vuln` | `-sV --script vuln -T4` | Correr los scripts NSE de vulnerabilidades sobre los servicios detectados. Fase posterior a un service scan. | Largo, ruidoso → background | | `udp-top` | `-sU --top-ports 100 -T4` | Descubrir servicios UDP (DNS, SNMP, NTP...). UDP es lento y suele requerir sudo. | Largo → background | | `service` | `-sV -sC -T4` | Deteccion de version + scripts default sobre puertos abiertos. A veces tolerable en primer plano. | Medio (puede ir a background) | | `aggressive` | `-A -T4` | OS + version + scripts + traceroute de golpe. Muy detectable; el `-O` interno puede pedir sudo. | Largo, ruidoso → background | Perfiles ligeros que SI corren bien en primer plano: `quick` (`-T4 -F`, top 100), `top1000` (`-T4`), `discovery` (`-sn`, ping sweep de una subred → puebla `hosts_up`), `os` (`-O`, requiere sudo). ## Prerequisitos - **CLIs instaladas** en el PATH: `whois` (`apt install whois`), `rdap` (openrdap, normalmente en `~/go/bin/rdap` — `go install github.com/openrdap/rdap/cmd/rdap@latest`), `dig` (`dnsutils`/`bind-utils`), `ping` (`iputils-ping`), `traceroute`, `nmap`. Si falta el binario, la funcion devuelve `status:error` con la instruccion de instalacion, nunca lanza. - **Privilegios**: los perfiles de nmap `os` (-O), `udp-top` (-sU) y parte de `aggressive` requieren sudo/root; sin privilegios nmap cae a connect-scan TCP y esos modos quedan incompletos (estas funciones no usan sudo). - **Service `osint_db` vivo** en `http://127.0.0.1:8771` para el registro estructurado en `network_scans`. Si esta caido, los scans siguen guardandose como nota (solo se pierde la fila DuckDB hasta el siguiente re-registro). Ver memoria `osint-duckdb-stack`. ## Fronteras (que NO cubre) - **No es un framework de explotacion.** Es reconocimiento: identifica superficie (puertos, servicios, versiones, registro, ruta). No explota vulnerabilidades, no hace fuerza bruta de credenciales, no entrega payloads. Para eso, herramientas dedicadas fuera del registry. - **Solo hosts autorizados o propios.** Escanear infraestructura de terceros sin permiso explicito puede ser delito. `scanme.nmap.org` es el unico host de terceros legal por defecto (es el host oficial de pruebas de nmap). - **No evade deteccion.** No implementa tecnicas de evasion de IDS/WAF, fragmentacion, decoys ni timing de sigilo; `-T4` es ruidoso a proposito. Un objetivo que defienda activamente puede detectar y filtrar el escaneo. - **No cubre OSINT pasivo de personas** (dorks, usernames, emails) mas alla de listar las funciones afines: esas viven en el grupo `osint-passive`. El render BD→nota y el grafo del vault son de `obsidian`/`duckdb`.