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:
@@ -0,0 +1,111 @@
|
||||
---
|
||||
name: detect_web_tech
|
||||
kind: function
|
||||
lang: py
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "def detect_web_tech(headers: dict, html: str = '', cookies: list[str] | None = None, final_url: str = '') -> dict"
|
||||
description: "Detector de tecnologia web estilo Wappalyzer: identifica el stack tecnologico de un sitio (web fingerprint) matcheando una tabla de firmas regex embebida contra las cabeceras HTTP, el HTML, los nombres de cookies y la URL final. Detecta servidor (nginx, Apache, IIS, LiteSpeed, Caddy), lenguaje (PHP, ASP.NET, Java, Python, Ruby, Node.js), CMS (WordPress, Drupal, Joomla, Shopify, Wix, Squarespace, Ghost), frameworks JS (React, Vue, Angular, Svelte, Next.js, Nuxt), librerias (jQuery, Bootstrap, Lodash, Modernizr), analytics/tag (Google Analytics, GTM, Facebook Pixel, Hotjar, Matomo), CDN (Cloudflare, Fastly, Akamai, CloudFront, jsDelivr, unpkg), ecommerce (WooCommerce, Magento, PrestaShop, Shopify) y WAF/seguridad (Cloudflare, Sucuri, Imperva Incapsula). Pieza pura del detector: no toca la red, recibe las senales ya recogidas por fetch_http_fingerprint."
|
||||
tags: [recon, cybersecurity, web-recon, wappalyzer, fingerprint, tech-detection, cms, stack]
|
||||
params:
|
||||
- name: headers
|
||||
desc: "dict de cabeceras de respuesta HTTP con claves en minusculas (tal como las devuelve fetch_http_fingerprint en su campo headers). Valores string. Si las claves vienen en mayusculas se normalizan internamente."
|
||||
- name: html
|
||||
desc: "HTML de la pagina como string. Default '' para detectar solo por cabeceras y cookies. De aqui se extraen meta generator y src de los <script>."
|
||||
- name: cookies
|
||||
desc: "lista de NOMBRES de cookies (no valores). Default None -> []. Ej: ['PHPSESSID', 'wordpress_logged_in']."
|
||||
- name: final_url
|
||||
desc: "URL final tras redirects, para firmas basadas en host/path. Opcional, default ''."
|
||||
output: "dict con technologies (lista de {name, category, version, confidence, evidence} ordenada deterministicamente por categoria y nombre), by_category (dict categoria -> lista de nombres) y count (entero). confidence es 'high' para match directo de header/meta/cookie/url y 'medium' para HTML generico, script src o tecnologia implicada. version es best-effort (a menudo ''). Para entrada vacia devuelve technologies [], by_category {}, count 0. Nunca lanza."
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: true
|
||||
tests: ["test_nginx_por_header_con_version", "test_wordpress_por_html_y_meta_implica_php", "test_php_por_cookie", "test_cloudflare_por_header", "test_entrada_vacia", "test_entrada_vacia_explicita_headers_y_html", "test_determinismo", "test_count_y_by_category_consistentes", "test_headers_claves_mayusculas_se_normalizan", "test_jquery_por_script_src_es_medium"]
|
||||
test_file_path: "python/functions/cybersecurity/detect_web_tech_test.py"
|
||||
file_path: "python/functions/cybersecurity/detect_web_tech.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from cybersecurity import detect_web_tech
|
||||
|
||||
# Senales fake de un sitio WordPress sobre nginx.
|
||||
headers = {
|
||||
"server": "nginx/1.24.0",
|
||||
"x-powered-by": "PHP/8.2",
|
||||
}
|
||||
html = (
|
||||
'<html><head>'
|
||||
'<meta name="generator" content="WordPress 6.4">'
|
||||
'</head><body><link href="/wp-content/themes/x/style.css"></body></html>'
|
||||
)
|
||||
cookies = ["PHPSESSID", "wordpress_logged_in_abc"]
|
||||
|
||||
result = detect_web_tech(headers, html=html, cookies=cookies)
|
||||
# result["count"] == 3
|
||||
# result["by_category"] == {
|
||||
# "cms": ["WordPress"],
|
||||
# "programming-language": ["PHP"],
|
||||
# "web-server": ["nginx"],
|
||||
# }
|
||||
# nginx -> version "1.24.0", confidence "high", evidence "header server: nginx/1.24.0"
|
||||
# WordPress -> version "6.4", confidence "high", evidence "meta generator: WordPress 6.4"
|
||||
# PHP -> version "8.2", confidence "high", evidence "header x-powered-by: PHP/8.2"
|
||||
```
|
||||
|
||||
Flujo real componiendo con la capa impura hermana (recoleccion -> matching):
|
||||
|
||||
```python
|
||||
from cybersecurity import fetch_http_fingerprint, detect_web_tech
|
||||
|
||||
# Capa impura: recoge las senales con un GET real (red).
|
||||
fp = fetch_http_fingerprint("https://example.com")
|
||||
# fp = {"headers": {...lowercase...}, "html": "...", "cookies": [...], "final_url": "..."}
|
||||
|
||||
# Capa pura: identifica el stack sobre las senales recogidas (sin red).
|
||||
tech = detect_web_tech(
|
||||
fp["headers"],
|
||||
html=fp.get("html", ""),
|
||||
cookies=fp.get("cookies"),
|
||||
final_url=fp.get("final_url", ""),
|
||||
)
|
||||
for t in tech["technologies"]:
|
||||
print(t["category"], t["name"], t["version"], t["confidence"])
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando ya tienes los headers + html (+ cookies/URL) de una URL — recogidos por
|
||||
`fetch_http_fingerprint_py_cybersecurity` — y quieres saber el stack tecnologico
|
||||
del sitio: servidor, lenguaje, CMS, frameworks JS, librerias, analytics, CDN,
|
||||
ecommerce y WAF. Usala como pieza de matching pura y testeable. Para el flujo
|
||||
one-shot `url -> tecnologias` (recoger + detectar en una llamada) usa el pipeline
|
||||
`fingerprint_web_stack`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- La tabla `SIGNATURES` es un **subconjunto curado** de lo que cubre Wappalyzer
|
||||
(~50 tecnologias), no es exhaustiva. Para ampliarla, anade entradas nuevas a la
|
||||
constante `SIGNATURES` del modulo siguiendo el formato documentado en el codigo
|
||||
(matchers `headers`/`html`/`meta_generator`/`cookies`/`script_src`/`url`,
|
||||
opcionales `version_group` e `implies`).
|
||||
- La deteccion por HTML generico puede dar **falsos positivos**: un sitio que
|
||||
mencione "wordpress" o "woocommerce" en su texto/blog puede matchear sin usarlo
|
||||
realmente. Por eso esos matches tienen `confidence: "medium"` mientras que
|
||||
header/meta/cookie directos son `"high"`.
|
||||
- Las **SPAs cargan los frameworks por JS en runtime**. Un fetch estatico (sin
|
||||
ejecutar JavaScript) ve el HTML inicial, que en muchas SPAs esta casi vacio
|
||||
(`<div id="root"></div>`). React/Vue/Angular pueden NO detectarse si el HTML
|
||||
servido no contiene aun sus marcadores. Para esos casos hace falta renderizar
|
||||
con un navegador headless, fuera del alcance de esta funcion pura.
|
||||
- Las **versiones son best-effort**: solo se extraen cuando el regex que disparo
|
||||
tiene un group de version y este matcheo. A menudo quedan en `""`.
|
||||
- Es PURA y determinista: misma entrada -> misma salida. Para entrada vacia
|
||||
(`headers={}, html=""`) devuelve `technologies: [], count: 0` y NUNCA lanza ni
|
||||
reporta status/error.
|
||||
Reference in New Issue
Block a user