Files
fn_registry/python/functions/cybersecurity/grab_service_banner.md
T
egutierrez 935008ec3f 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>
2026-06-14 15:12:07 +02:00

5.4 KiB

name, kind, lang, domain, version, purity, signature, description, tags, params, output, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path
name kind lang domain version purity signature description tags params output uses_functions uses_types returns returns_optional error_type imports tested tests test_file_path file_path
grab_service_banner function py cybersecurity 1.0.0 impure def grab_service_banner(host: str, port: int, timeout_s: float = 3.0, send_probe: bool = True) -> dict Captura el banner de un servicio TCP y lo identifica heuristicamente sin nmap -sV. Abre un socket TCP a host:port, opcionalmente envia un probe (HEAD / HTTP/1.0 para puertos web), lee hasta ~4096 bytes con timeout y reconoce el servicio (ssh, ftp, smtp, http, mysql/mariadb, redis, pop3, imap, telnet, ...) por heuristica sobre el banner, extrayendo producto y version best-effort. Complementa a un port scan: el scan dice si el puerto esta abierto, esta funcion dice QUE servicio y version hablan detras. Solo stdlib (socket, re, struct). NO lanza: devuelve dict status ok/error con campo raw (repr del banner crudo).
recon
cybersecurity
banner-grab
service-detection
network
name desc
host Hostname o IP del objetivo (ej. 'scanme.nmap.org', '127.0.0.1'). Vacio devuelve status error.
name desc
port Puerto TCP a sondear (ej. 22 ssh, 80 http, 3306 mysql, 6379 redis). Fuera del rango 1..65535 o no convertible a int devuelve status error.
name desc
timeout_s Timeout en segundos tanto de conexion como de lectura del socket. Default 3.0. Subirlo para hosts lentos; bajarlo para barridos rapidos de muchos puertos.
name desc
send_probe Si True (default) y el puerto esta en el mapa interno de probes (puertos HTTP tipicos: 80/8080/8000/8888/8081/8008), envia 'HEAD / HTTP/1.0\r\n\r\n' para provocar respuesta de servicios web que no emiten banner pasivo. Para el resto de puertos no envia nada e intenta leer el banner pasivo (SSH/FTP/SMTP/POP3/IMAP emiten banner solo con conectar). False nunca envia probe (captura siempre pasiva).
dict de estado. ok: {status:'ok', host, port:int, service:str (ssh|ftp|smtp|http|mysql|redis|pop3|imap|telnet|ftp-or-smtp|unknown), product:str (best-effort, p.ej. OpenSSH/nginx/Postfix/MySQL; '' si no se extrae), version:str (best-effort, p.ej. '8.9p1'; '' si no se extrae), banner:str (banner decodificado y .strip()), raw:str (repr() del banner crudo en bytes, seguro para guardar)}. error: {status:'error', error:str, host, port}. Nunca lanza excepciones.
false error_py_core
true
test_identifica_ssh_de_banner_local
test_host_vacio_devuelve_error
test_port_fuera_de_rango_devuelve_error
python/functions/cybersecurity/grab_service_banner_test.py python/functions/cybersecurity/grab_service_banner.py

Ejemplo

import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from cybersecurity import grab_service_banner

# 1) Identificar el servicio SSH del host oficial de pruebas de nmap (legal).
res = grab_service_banner("scanme.nmap.org", 22, timeout_s=5)
if res["status"] == "ok":
    print(res["service"])   # "ssh"
    print(res["product"])   # "OpenSSH"
    print(res["version"])   # "8.9p1" (o similar)
    print(res["banner"])    # "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1"
else:
    print("error:", res["error"])

# 2) Identificar un servidor web: el probe HTTP provoca respuesta con Server:.
res = grab_service_banner("scanme.nmap.org", 80, timeout_s=5)
print(res["service"], res["product"], res["version"])  # http nginx 1.18.0

Tambien via fn run tras indexar:

./fn run grab_service_banner_py_cybersecurity

(El smoke del modulo sondea scanme.nmap.org:22 y tolera fallos de red.)

Cuando usarla

Usala cuando YA sabes que un puerto esta abierto (p.ej. tras un escaneo de puertos) y quieres identificar el servicio y su version de forma rapida para un puerto concreto, sin levantar nmap -sV. Encaja como segundo paso de recon: primero localizas los puertos abiertos de un host, luego compones esta funcion sobre cada puerto interesante para etiquetar QUE habla detras (ssh, http, mysql, redis, ...) y guardar el banner como evidencia en la nota OSINT.

Gotchas

  • Funcion impura: abre una conexion TCP real al objetivo. Solo sondea hosts propios o con autorizacion explicita; conectar a servicios de terceros sin permiso puede ser ilegal.
  • TLS/HTTPS implicito (443, 993, 995, 465, ...): el servicio espera un handshake TLS antes de hablar, asi que el banner plano que captura esta funcion NO funciona ahi — devolvera bytes binarios ilegibles o timeout, con service:"unknown". Para esos puertos hay que envolver el socket en TLS (ssl.SSLSocket) primero; esta funcion no lo hace.
  • Banner pasivo no garantizado: algunos servicios no emiten nada hasta completar un handshake especifico del protocolo. Para esos casos banner puede venir vacio y service:"unknown" aunque el puerto este abierto. El probe HTTP solo cubre los puertos web listados en el mapa interno; otros protocolos quedarian sin probe activo.
  • Decodificacion best-effort: el banner se decodifica utf-8 y cae a latin-1, lo que puede dar mojibake en bytes no textuales (handshakes binarios como MySQL). Por eso raw guarda el repr() de los bytes crudos como fuente fiable.
  • La identificacion es heuristica (regex/substring): puede equivocarse o quedar como service:"unknown". product/version son best-effort y pueden ser "".
  • Nunca lanza: revisa siempre res["status"] antes de leer service/banner. Puerto cerrado/filtrado/inalcanzable devuelve status:"error".