feat(recon): grupo de reconocimiento de red + servicios + fingerprint web
Añade el capability group `recon` (dominio cybersecurity + pipelines, Python),
con la política de archivado OSINT y página madre docs/capabilities/recon.md.
Lookups y sondeo (wrappers de CLI):
- whois_lookup, rdap_lookup, dns_records, ping_host, traceroute_host, nmap_scan
- save_scan_to_osint (sink común) + recon_osint (pipeline one-shot scan+archivado)
Escaneo de puertos/servicios nativo (stdlib, sin nmap ni sudo):
- scan_tcp_ports: connect-scan TCP concurrente (open/closed/filtered)
- grab_service_banner: banner grab + identificación de servicio/versión real
- identify_port_service: puro, puerto -> servicio IANA esperado (~120 puertos)
- scan_port_services: pipeline one-shot (scan -> identify + banner por puerto abierto)
Fingerprint de tecnología web (estilo Wappalyzer), patrón pura/impura:
- fetch_http_fingerprint: GET stdlib, recoge headers/html/cookies (solo nombres)
- detect_web_tech: puro, matchea ~50 firmas regex -> tecnologías por categoría
- fingerprint_web_stack: pipeline one-shot url -> tecnologías
Todas devuelven dict {status} sin lanzar. Tests: 43 verdes, sin red externa.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,25 +3,25 @@ name: whois_lookup
|
||||
kind: function
|
||||
lang: py
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
version: "2.0.0"
|
||||
purity: impure
|
||||
signature: "def whois_lookup(dominio: str, timeout_s: float = 15.0) -> dict"
|
||||
description: "Recoleccion OSINT pasiva de datos de registro de dominio via RDAP (reemplazo moderno de WHOIS sobre HTTP/JSON). Consulta https://rdap.org/domain/<dominio> con http_get_json y normaliza registrar, fechas de creacion/expiracion/ultimo cambio, nameservers, estados y entidades. Devuelve {found: False} si el dominio no existe (404)."
|
||||
tags: [osint-passive, whois, rdap, recon, cybersecurity]
|
||||
signature: "def whois_lookup(target: str, timeout_s: int = 30) -> dict"
|
||||
description: "Lookup WHOIS de un dominio o IP via el CLI `whois` del sistema (apt). Ejecuta `whois <target>` como subproceso, captura el stdout completo en raw y parsea best-effort (case-insensitive, tolerante a ausencias) registrar, registrant_country, creation_date, expiry_date, updated_date y name_servers. Devuelve siempre un dict {status: ok|error}; nunca lanza excepciones. OSINT pasivo: util para perfilado de dominios, deteccion de typosquatting/phishing y validacion de propiedad."
|
||||
tags: [recon, whois, osint-passive, cybersecurity]
|
||||
params:
|
||||
- name: dominio
|
||||
desc: "Dominio a consultar, ej. organic-machine.com. Vacio lanza RuntimeError."
|
||||
- name: target
|
||||
desc: "Dominio (ej. google.com) o direccion IP a consultar. Vacio devuelve status error."
|
||||
- name: timeout_s
|
||||
desc: "Segundos maximo de espera de la peticion HTTP a rdap.org (default 15.0)."
|
||||
output: "dict normalizado con found (bool), registrar, creation_date, expiration_date, last_changed, nameservers (lista), status (lista), entities (lista de {handle, roles}) y raw (RDAP completo). Si el dominio no existe (HTTP 404) devuelve {found: False}."
|
||||
uses_functions: ["http_get_json_py_infra"]
|
||||
desc: "Segundos maximo de espera del subproceso whois (default 30)."
|
||||
output: "dict. En exito: {status: 'ok', target, raw (stdout completo del whois, SIEMPRE presente), registrar, registrant_country, creation_date, expiry_date, updated_date, name_servers (lista de strings en minusculas)}. Campos no encontrados quedan None; name_servers vacio = []. Para IPs varios campos de dominio quedan None. En fallo: {status: 'error', error: str, target}."
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
error_type: "error_py_core"
|
||||
imports: []
|
||||
tested: true
|
||||
tests: ["test_normaliza_respuesta_rdap", "test_dominio_no_encontrado_404", "test_otro_error_http_se_propaga", "test_sin_registrar_ni_fechas", "test_dominio_vacio_lanza_error"]
|
||||
tests: ["test_parsea_campos_comunes", "test_campos_ausentes_quedan_none", "test_raw_siempre_presente", "test_target_vacio_devuelve_error"]
|
||||
test_file_path: "python/functions/cybersecurity/whois_lookup_test.py"
|
||||
file_path: "python/functions/cybersecurity/whois_lookup.py"
|
||||
---
|
||||
@@ -33,36 +33,52 @@ import sys, os
|
||||
sys.path.insert(0, os.path.join("python", "functions"))
|
||||
from cybersecurity import whois_lookup
|
||||
|
||||
info = whois_lookup("organic-machine.com")
|
||||
if info["found"]:
|
||||
print(info["registrar"]) # 'Example Registrar Inc.'
|
||||
print(info["creation_date"]) # '2020-01-15T10:00:00Z'
|
||||
print(info["expiration_date"]) # '2027-01-15T10:00:00Z'
|
||||
print(info["nameservers"]) # ['ns1.example.net', 'ns2.example.net']
|
||||
print(info["status"]) # ['client transfer prohibited']
|
||||
info = whois_lookup("google.com")
|
||||
if info["status"] == "ok":
|
||||
print(info["registrar"]) # 'MarkMonitor Inc.'
|
||||
print(info["registrant_country"]) # 'US'
|
||||
print(info["creation_date"]) # '1997-09-15T04:00:00Z'
|
||||
print(info["expiry_date"]) # '2028-09-14T04:00:00Z'
|
||||
print(info["name_servers"]) # ['ns1.google.com', 'ns2.google.com', ...]
|
||||
# info["raw"] tiene el texto whois completo para guardar en OSINT
|
||||
else:
|
||||
print("dominio no registrado")
|
||||
print("fallo:", info["error"])
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Usala para obtener metadatos de registro de un dominio sin depender del CLI
|
||||
`whois` (no instalado): edad del dominio, fecha de expiracion (dominios a
|
||||
punto de caducar), registrar y nameservers autoritativos. Util en perfilado
|
||||
pasivo, deteccion de dominios recien creados (typosquatting/phishing) y
|
||||
validacion de propiedad.
|
||||
Usala cuando necesites los datos de registro crudos de un dominio o IP via el
|
||||
CLI `whois` clasico: registrar, pais del registrante, edad del dominio (fecha
|
||||
de creacion), fecha de expiracion (dominios a punto de caducar) y nameservers.
|
||||
Ideal en perfilado pasivo OSINT, deteccion de dominios recien creados
|
||||
(typosquatting / phishing) y para conservar el texto WHOIS completo (`raw`)
|
||||
como evidencia. Para datos estructurados JSON modernos, prefiere
|
||||
`rdap_lookup_py_cybersecurity`; ambas se complementan.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- RDAP no esta uniformemente desplegado en todos los TLD: algunos devuelven
|
||||
campos vacios o ni siquiera responden. Por eso los campos opcionales pueden
|
||||
quedar `None` y `nameservers`/`status`/`entities` listas vacias.
|
||||
- rdap.org actua como bootstrap y redirige al servidor RDAP autoritativo del
|
||||
TLD; depende de su disponibilidad.
|
||||
- El registrante (`entities` con rol distinto de `registrar`) suele estar
|
||||
redactado por privacy/GDPR: casi siempre solo veras `handle` y `roles`, sin
|
||||
datos personales.
|
||||
- Un dominio no registrado devuelve `{"found": False}` (HTTP 404); cualquier
|
||||
otro error HTTP (rate limit 429, 5xx) se propaga como `RuntimeError`.
|
||||
- Las fechas se devuelven tal cual las da RDAP (ISO 8601 UTC), sin parsear a
|
||||
objetos `datetime`.
|
||||
- IMPURA: hace red. El servidor WHOIS del TLD/registrar puede tardar o fallar;
|
||||
por eso hay `timeout_s` (default 30) y los timeouts devuelven
|
||||
`{"status": "error", ...}` sin lanzar.
|
||||
- El formato WHOIS **no esta estandarizado**: varia por TLD y por registrar. El
|
||||
parseo es best-effort con multiples labels alternativos (`Creation Date` /
|
||||
`created` / `Registered on`, etc.). Cualquier campo puede quedar `None` aunque
|
||||
el dato exista bajo un label que no contemplamos — el texto completo siempre
|
||||
esta en `raw`.
|
||||
- Para **IPs**, muchos campos de dominio (registrar, creation_date,
|
||||
name_servers) no existen y quedan `None` / `[]`; lo relevante esta en `raw`
|
||||
(rango, org, abuse contact).
|
||||
- Muchos registros estan redactados por privacy/GDPR: el registrante personal
|
||||
raramente aparece; suele verse solo el registrar.
|
||||
- `whois` a menudo escribe en stdout incluso con codigo de retorno != 0
|
||||
(avisos, rate-limit parciales). Solo se considera error duro cuando el stdout
|
||||
esta totalmente vacio.
|
||||
- Requiere el binario `whois` instalado (`apt install whois`). Si falta, devuelve
|
||||
status error claro en vez de lanzar.
|
||||
|
||||
## Capability growth log
|
||||
|
||||
- v2.0.0 (2026-06-14) — reescrita sobre el CLI `whois` (antes RDAP via HTTP). Nueva
|
||||
firma `(target: str, timeout_s: int = 30)`, acepta dominios e IPs, estilo dict
|
||||
`{status: ok|error}` sin excepciones (alineado con el grupo recon). La variante
|
||||
RDAP/JSON ahora vive en `rdap_lookup_py_cybersecurity`.
|
||||
|
||||
Reference in New Issue
Block a user