
Escáner de vulnerabilidades en tiempo de ejecución: detecta CVEs en los servicios que realmente se ejecutan en un host y los clasifica según su exposición a la red.
# OffSeq Threat Finder
[](https://github.com/offseq/threat-finder/actions/workflows/ci.yml)
[](https://crates.io/crates/threat-finder)
[](https://docs.rs/threat-finder)
[](#license)
**threat-finder** encuentra el software vulnerable _realmente en ejecución_ en un host — no lo que afirma un manifiesto — y te indica qué hallazgos son **accesibles desde la red**.
Resuelve cada servicio en ejecución (y, con `--scope all`, cada paquete del sistema operativo instalado) a un [Package-URL](https://github.com/package-url/purl-spec) exacto y lo compara con el catálogo de [OffSeq Radar](https://radar.offseq.com) usando reglas de versionado nativas del ecosistema, de modo que las compilaciones con backport/corregidas no se marcan falsamente.

```text
Vulnerability summary (highest risk first):
openssh-server@1:8.9p1-3ubuntu0.6 — 1 finding(s) [PUBLIC tcp 0.0.0.0:22]
[ACT-NOW 92] HIGH CVE-2024-6387 [KEV] regreSSHion: remote code execution in OpenSSH
→ fix: 1:8.9p1-3ubuntu0.10 https://radar.offseq.com/threat/…
[email protected] — 1 finding(s)
[SCHEDULE 41] MED CVE-2023-44487 HTTP/2 Rapid Reset
2 confirmed finding(s) across 2 asset(s); 1 exposed, 1 known-exploited.
```
## Instalación
```sh
brew install offseq/tap/threat-finder # Homebrew (macOS/Linux), prebuilt
cargo binstall threat-finder # prebuilt binary, no toolchain
cargo install threat-finder # from source
```
Los archivos precompilados para Linux/macOS (x86_64 + arm64) y Windows (x86_64) están en la [página de lanzamientos](https://github.com/offseq/threat-finder/releases/latest).
Se requiere Rust ≥ 1.87 para compilar desde el código fuente. Se admiten Linux, macOS, los BSD y Windows.
## Inicio rápido
```sh
export OFFSEQ_API_KEY=... # from https://radar.offseq.com/console
threat-finder
```
Escanea los servicios en ejecución, imprime un resumen ordenado por riesgo y escribe el informe JSON completo en `/tmp/threats.json`. Añade `--scope all` para escanear también todos los paquetes del sistema operativo instalados.
## Uso
```sh
threat-finder [OPTIONS]
```
| Flag | Descripción |
|------|-------------|
| `-o, --output <PATH>` | Escribe el informe JSON en `PATH` (por defecto: pregunta, o `/tmp/threats.json`) |
| `--json` | Imprime el informe JSON en stdout en lugar de un archivo |
| `--scope <SCOPE>` | `running` (predeterminado) o `all` (+ cada paquete del sistema operativo instalado) |
| `--severity <LEVEL>` | Solo informa de hallazgos en/por encima de `critical\|high\|medium\|low` |
| `--strict` | Descarta los hallazgos sin confirmación de coordenadas (solo informa de los confirmados) |
| `--fail-on <WHAT>` | Sale con `5` si existen hallazgos coincidentes: `any\|critical\|high\|medium\|low\|kev\|exposed` |
| `--sarif <PATH>` | También escribe un informe SARIF 2.1.0 (para interfaces de code-scanning) |
| `--include <GLOB>` / `--exclude <GLOB>` | Filtra activos por nombre glob (repetible) |
| `-q, --quiet` | Suprime el banner, el progreso y el resumen |
| `--no-color` | Desactiva los colores ANSI |
| `-y, --yes` | Asume los valores predeterminados, nunca pregunta (CI/cron) |
| `--reset` | Vuelve a introducir la clave API, ignorando la guardada |
| `--register` | Registra el host escaneado en Radar para monitorización continua (sin preguntar) |
| `--no-register` | No registra ni pregunta sobre la monitorización en esta ejecución |
| `--host-name <NAME>` | Nombre de host descriptivo para enviar con un registro |
| `--unregister` | Elimina el inventario de este host de Radar y sale |
| `--windows-missing-updates` | (Windows) También lista las actualizaciones de seguridad pendientes del Agente de Windows Update (escaneo en línea; ejecutar como administrador) |
| `-h, --help` / `-V, --version` | Ayuda / versión |
```sh
# CI: solo hallazgos high+, JSON a stdout, sin preguntas
OFFSEQ_API_KEY=… threat-finder --yes --json --severity high > report.json
# Falla la compilación solo cuando un servicio expuesto a la red tiene un CVE con exploit conocido
OFFSEQ_API_KEY=… threat-finder --yes --quiet --fail-on exposed
```
**Códigos de salida:** `0` correcto · `1` error de búsqueda/IO · `2` sin clave API · `3` sistema operativo no compatible · `4` límite de tasa/cuota, o se requiere acceso a la API (actualización necesaria) · `5` umbral `--fail-on` alcanzado.
**Clave API** (obtén una en la [Consola Radar](https://radar.offseq.com/console)),
se resuelve en este orden:
1. Variable de entorno `OFFSEQ_API_KEY` (lo mejor para CI/cron).
2. La clave guardada en `$XDG_CONFIG_HOME/offseq-rust/config.toml` (`0600`),
a menos que se indique `--reset`.
3. Una solicitud interactiva y oculta en una TTY — luego se guarda para la próxima vez.
Sin modo interactivo (`--yes` / sin TTY) y sin clave disponible se sale con `2`.
Define `OFFSEQ_CONFIG_DIR` para anular la ubicación de la configuración (por defecto: el directorio de configuración por usuario del SO) — útil para contenedores/CI que necesitan una ruta determinista.
## Cómo funciona
**Coincidencia por coordenadas exactas.** Cada activo se convierte en un purl que lleva su _versión_ completa (epoch + revisión de la distribución) y un calificador `?distro=`, p. ej.
`pkg:deb/ubuntu/openssh-server@1:8.9p1-3ubuntu0.6?distro=jammy`. El inventario se compara en llamadas por lotes `POST /match/batch` (una solicitud por fragmento del tamaño de un nivel) **en el servidor, con reglas de versionado nativas del ecosistema** (dpkg/rpm/apk/semver) — de modo que una compilación con backport y corregida como `1.18.0-6+deb11u3` no se marca correctamente, y no hay adivinanzas de versiones en el cliente. Los hallazgos se dividen según el indicador `confirmed` de la API: los hallazgos confirmados se notifican; las coincidencias de coordenadas cuya versión no puede confirmarse se muestran por separado como **no confirmados / triaje** (excluidos del recuento, de `byCve` y de `--fail-on`; elimínalos con `--strict`).
**Correlación de exposición a la red.** Los escáneres de manifiestos (Trivy, Grype, osv-scanner) leen listas de paquetes; los escáneres externos (Nessus, OpenVAS) necesitan un segundo host. Esta herramienta asigna el proceso de cada servicio en ejecución a los sockets en los que está **escuchando** (`/proc/net` en Linux, `lsof` en los otros Unix, `Get-NetTCPConnection` / `netstat` en Windows) y clasifica la alcanzabilidad: `loopback` / `private` / `public`. Un servicio vulnerable en `0.0.0.0` es un riesgo muy distinto de uno en `127.0.0.1`: los hallazgos se ordenan con los expuestos primero y `--fail-on exposed` condiciona el CI exactamente a eso. No se envían paquetes. Los hallazgos también incluyen **KEV** de CISA y **EPSS**.
**Priorización según exposición.** Cada hallazgo recibe un `riskScore` (0–100) y una banda de `decision` estilo SSVC — **`act-now` · `soon` · `schedule` · `track`** — combinada a partir de la severidad, EPSS, KEV y la **exposición a la red** del activo propietario. El resumen encabeza cada línea con una insignia estilo `[ACT-NOW 92]` y ordena por ella, de modo que los pocos problemas públicos y con exploit conocido floten a la cima de un host ruidoso. La puntuación usa la misma fórmula localmente y en el servidor (la vista de monitorización de Radar añade un pequeño bonus cuando un hallazgo KEV ha superado su fecha límite — una fecha que la CLI no tiene), y aparece en `--json` y SARIF (`properties`).
## Monitorización continua
Un escaneo puntual es un momento en el tiempo. Registra un host una vez y Radar seguirá vigilando: cuando un **CVE recién publicado** afecta a una de sus coordenadas, recibes una alerta (correo + Consola), priorizada por exposición — sin necesidad de volver a escanear.
Después de un escaneo interactivo, la herramienta pregunta:
```text
Add these 42 services to Radar for continuous monitoring & alerts? [Y/n/never]
```
`Y` registra este host; `n` omite esta ejecución; `never` recuerda tu elección (se guarda en la configuración y no se te volverá a preguntar). En un nuevo escaneo también informa de **deriva** (`+added / -removed / ~changed`) y de cualquier hallazgo **nuevo desde tu último escaneo**. Gestiona tus hosts — activa/desactiva la monitorización, consulta hallazgos, da de baja — en **Inventory** de la [Consola Radar](https://radar.offseq.com/console).
Para automatización, omite la pregunta: `--register` registra de forma no interactiva (desactivado por defecto en CI), `--no-register` opta por no participar, `--host-name <NAME>` etiqueta el host y `--unregister` lo elimina. Un id estable por host y tu preferencia sobre la pregunta viven en `$XDG_CONFIG_HOME/offseq-rust/config.toml`. El registro nunca cambia el código de salida — un contratiempo de monitorización no hará fallar tu escaneo. Requiere un plan Basic/Pro/Enterprise (o Consola Pro).
## Alcance y cobertura
`--scope running` (predeterminado) escanea los servicios en vivo — el conjunto pequeño y de alta señal cuya exposición puede correlacionarse. `--scope all` además enumera **cada paquete del sistema operativo instalado** (`dpkg`/`rpm`/`pacman`/`apk`/`brew`/`pkg`/`pkg_info`), expandiendo la superficie comparada 10–50×. Un paquete que también respalda un proceso en ejecución y expuesto conserva esa exposición (los activos se deduplican y fusionan por coordenada). El kernel se cubre como su paquete (`linux-image…`) con `--scope all`.
> `--scope all` puede producir cientos–miles de paquetes. En el nivel gratuito (15 consultas/hora) esto provocará limitación de tasa; la herramienta avisa cuando el inventario supera el presupuesto. (Las consultas ya van por lotes; una caché local de resultados está en la hoja de ruta.)
## Soporte por sistema operativo
| SO | Descubrimiento | Fuente de coordenadas |
|----|-----------|-------------------|
| Linux (systemd) | `ListUnits` → `/proc/<pid>/exe` | dpkg / rpm / pacman / apk |
| Linux (SysV/OpenRC) | `service --status-all` / `rc-status` | base de datos de paquetes |
| macOS | `launchctl list` → `ps` (solo terceros) | Homebrew |
| FreeBSD / DragonFly | `service -e` | `pkg` |
| OpenBSD | `rcctl ls started` | `pkg_info` |
| NetBSD | estado de `/etc/rc.d` | `pkg_info` |
| Solaris / illumos | `svcs` → `svcprop` | sonda (`--version`) |
| Windows | `Win32_Service` → `Get-NetTCPConnection` / `netstat` | registro / winget / Appx / Chocolatey / Scoop → CPE; npm / pip / dotnet → purl; la compilación del SO → CPE |
Cuando ningún paquete es propietario de un binario, la versión recurre a una sonda `--version` endurecida (solo ruta absoluta, entorno saneado). En macOS, los servicios del sistema de Apple (`com.apple.*`, rutas protegidas por SIP) se omiten — están cubiertos por la versión del SO, y sondear cientos de ellos no tiene sentido.
En **Windows**, todo se recopila mediante el `powershell.exe` integrado (sin runtime adicional, sin administrador para el inventario principal). Las aplicaciones instaladas se asignan a CPE de NVD mediante una tabla de nombres curada (las aplicaciones no asignadas recurren a una búsqueda por nombre en lugar de una suposición errónea); la edición/compilación del SO se convierte en un CPE de SO con calificador de características (p. ej. `windows_11_23h2`); y `--windows-missing-updates` añade un aviso opcional del Agente de Windows Update sobre parches de seguridad pendientes.
## Salida
JSON, con claves deterministas (ordenadas) y sin marca de tiempo, para que los informes se diferencien limpiamente:
- **`services`** — `pkg@version` → hallazgos confirmados (`cveId`, `severity`, `cvssScore`, `epss`, `kev`, `riskScore`, `decision`, `confirmed`, `matchedRange`, `matchBasis`, `fixedVersions`, `remediation`, `cwes`, `references`, `radarUrl`), primero los de mayor riesgo.
- **`unconfirmed`** — coincidencias de coordenadas cuya versión no pudo confirmarse (triaje).
- **`assets`** — `pkg@version` → `{ exe, versionSource, exposed, reachability, listeners }` (`versionSource` = `package-db` | `probe`; `reachability` cubre TCP **y** UDP).
- **`byCve`** — cada CVE agrupado en todos los activos afectados ("parchea una vez, arregla muchos").
- **`errors`** — fallos de búsqueda por activo, de modo que un fallo nunca se lea como "limpio".
- **`registration`** — presente cuando la ejecución registró el host: `host_id`, `monitoring`, `drift`, `summary`, `newSinceLastCount`.
- **`meta`** — `{ tool, version, schemaVersion }` (`schemaVersion` 2).
También hay disponible un informe SARIF 2.1.0 (`--sarif`) para interfaces de code-scanning.
## El ecosistema OffSeq
| | |
|---|---|
| [**OffSeq**](https://offseq.com) | Auditorías de seguridad en la UE, monitorización de amenazas, CISO como servicio, cumplimiento NIS2 |
| [**Radar**](https://radar.offseq.com) | Inteligencia de amenazas en tiempo real — el catálogo contra el que compara threat-finder |
| [**Consola Radar**](https://radar.offseq.com/console) | Suscripciones, feeds personalizados y tu `OFFSEQ_API_KEY` |
| [**API Radar**](https://radar.offseq.com/api-docs) | Documentación REST para el endpoint `/match` usado aquí |
| [**Radar Threats**](https://radar.offseq.com/threats) | Base de datos buscable de CVE / malware / actores de amenazas |
| [**Radar Feeds**](https://radar.offseq.com/feeds) | Feeds personalizados que agregan CISA, CIRCL, ThreatFox, … |
| [**Radar Pricing**](https://radar.offseq.com/pricing) | Desde el nivel gratuito hasta Enterprise |
| [**Breach**](https://breach.offseq.com) | Monitorización de filtraciones de datos y credenciales expuestas en la dark web |
| [**Veil**](https://veil.offseq.com) | Esteganografía PNG del lado del cliente (AES-256-GCM) |
| [**Guard**](https://offseq.com/guard/) | Analista de seguridad y cumplimiento web con IA |
| [**Training**](https://training.offseq.com) | Cursos de seguridad y privacidad acreditados por PECB |
## Desarrollo
```sh
cargo build --release
cargo test # unit tests
cargo test -- --ignored # + macOS live-discovery smoke test
cargo clippy --all-targets
```
El motor es una crate de biblioteca (`find_threats`) con una abstracción `Collector` (servicios en ejecución y paquetes del SO hoy; lockfiles / contenedores / SBOM a continuación), de modo que el binario es una CLI delgada sobre ella.
## Licencia
Con doble licencia bajo [MIT](https://github.com/offseq/threat-finder/blob/main/LICENSE) o [Apache-2.0](https://github.com/offseq/threat-finder/blob/main/LICENSE), a tu elección.