El módulo anterior terminó señalando una palabra que apareció en sus seis lecciones sin desarrollarse nunca: verificar. El registro de riesgos afirma que el puerto 5432 está expuesto, el catálogo de controles promete un escaneo externo mensual que nadie ha ejecutado (C-10, «planificado») y el riesgo R-07 habla de «servicios olvidados» sin que nadie sepa cuántos hay hoy. Esta lección convierte esas afirmaciones en mediciones: vas a aprender a encontrar lo que está expuesto, decidir qué se arregla primero y demostrar que se arregló, con herramientas de coste cero que caben en el presupuesto de Nimbus.

⚠️ Advertencia legal y de autorización — léela antes de ejecutar nada

Todo lo que hay en esta lección se ejecuta exclusivamente sobre sistemas propios o con autorización escrita, explícita y vigente.

  • Escanear puertos o lanzar un escáner de vulnerabilidades contra una máquina ajena sin autorización puede constituir delito en España (arts. 197 bis y 264 del Código Penal). La curiosidad no es una eximente.
  • En la nube debes cumplir además la política de pruebas del proveedor, y si el sistema lo opera un tercero —la consultora de A-19, el proveedor de correo, la pasarela—, la autorización viene de quien lo opera, no solo de quien lo contrata.

Todo el material trabaja sobre el rango propio de Nimbus (10.20.0.0/16 en oficina, 10.30.0.0/16 en la nube) y su dominio nimbusreservas.example. No hay exploits ni payloads: cada hallazgo va con su corrección. (La interpretación penal concreta depende del caso; ante una prueba con impacto sobre terceros, consulta asesoría jurídica.)

Contenido

  1. La gestión de vulnerabilidades como proceso continuo
  2. El mapa de herramientas: qué encuentra cada familia
  3. Descubrimiento de red con nmap
  4. Escáner de vulnerabilidades: Greenbone/OpenVAS
  5. Dependencias y contenedores: pip-audit y trivy
  6. Análisis estático de código y escaneo de secretos
  7. Priorización: CVSS, EPSS, KEV y exposición
  8. Falsos positivos, falsos negativos y validación
  9. Integración en el ciclo y métricas del proceso
  10. El informe de vulnerabilidades

  1. La gestión de vulnerabilidades como proceso continuo

Casi todas las PYMES que «hacen gestión de vulnerabilidades» hacen en realidad escaneo puntual: una vez al año alguien lanza una herramienta, exporta un PDF de 300 páginas y lo archiva. Eso no reduce el riesgo: genera un documento. La diferencia es un ciclo cerrado con verificación.

flowchart LR
    I["0. INVENTARIO\nQue tengo (A-01..A-22).\nSin esto no hay cobertura"] --> D["1. DESCUBRIR\nnmap, escaner, SCA,\nSAST, secretos, cloud"]
    D --> P["2. PRIORIZAR\nCVSS + EPSS + KEV\n+ exposicion + criticidad"]
    P --> R["3. REMEDIAR\nParche, configuracion,\nmitigacion o aceptacion"]
    R --> V["4. VERIFICAR\nReescanear y CERRAR\ncon evidencia"]
    V --> N["5. INFORMAR\nMetricas y tendencia\n(KPI de 04-03)"]
    N --> D
    V -.->|"si no se corrige"| E["EXCEPCION con caducidad\nal registro de riesgos 04-01"]

Cinco reglas separan el proceso del teatro:

  • El paso 0 no es opcional. Un escaneo sobre 12 máquinas cuando tienes 18 no encuentra nada en las otras 6: la cobertura es la primera métrica, no la última.
  • Lo alimentan varias herramientas, no una. Ninguna familia ve más del 30 % del problema.
  • Priorizar no es ordenar por CVSS. Es la parte que más valor aporta y la que casi nadie hace bien (§7).
  • Un hallazgo se cierra cuando el reescaneo ya no lo encuentra, no cuando alguien dice que lo arregló: esa es la evidencia auditable de 04-03. Y lo que no se corrige se convierte en excepción con caducidad (04-02), no en una fila que envejece en silencio.

Precisión de 01-01: una versión de OpenSSL con CVE es una vulnerabilidad; que ese servicio escuche en Internet es la exposición; el riesgo combina ambas con la criticidad del activo. Una crítica en una máquina apagada no es una urgencia; una media en la API pública que llega a A-01 sí.


  1. El mapa de herramientas: qué encuentra cada familia

Familia Qué encuentra / qué no ve Libres Comercial Dónde
Descubrimiento de red Hosts, puertos, servicios y versiones · no ve nada dentro de la aplicación nmap, masscan Runzero, Censys §3 y 05-04
Escáner de infraestructura CVE de sistema y servicios · no ve fallos de negocio ni código propio Greenbone/OpenVAS, Nuclei Nessus, Qualys §4
Escáner web dinámico (DAST) Inyecciones, XSS, cabeceras · no ve código no alcanzable por la interfaz OWASP ZAP, Nikto, testssl.sh Burp Pro, Invicti 05-05 y 05-03
Composición de software (SCA) CVE en dependencias · no ve fallos en tu propio código pip-audit, grype, osv-scanner Snyk, Mend §5
Análisis estático (SAST) Patrones inseguros en el código propio · no ve lo que depende del entorno semgrep, bandit, CodeQL Checkmarx, Fortify §6 y 05-05
Escáner de secretos · de contenedores Claves en el historial de git · CVE de la imagen y malas prácticas del Dockerfile gitleaks · trivy, grype GitGuardian · Aqua §5-§6, 05-05 y 05-07
Configuración cloud e IaC Buckets públicos, permisos excesivos, cifrado ausente · no ve la aplicación prowler, ScoutSuite, checkov, tfsec Wiz, Orca 05-07
Configuración del sistema Desviación frente a una línea base CIS · no ve CVE de aplicación lynis, OpenSCAP Tenable 05-06

La columna importante es «qué no ve». El IDOR que Nimbus corrigió con WHERE tenant_id no lo encuentra ninguna de estas familias: no es una versión antigua ni un patrón evidente, es un fallo de autorización que exige entender el negocio. Por eso el escaneo se complementa con revisión de código (05-05) y pruebas manuales (05-03), y por eso ningún escáner puede firmar que un sistema es seguro.


  1. Descubrimiento de red con nmap

nmap responde a la pregunta que Nimbus no sabe contestar: ¿qué hay escuchando ahí fuera y ahí dentro?

# 1) Que maquinas estan vivas en la VLAN de usuarios de Valencia
sudo nmap -sn 10.20.10.0/24 -oA /var/log/nimbus-scan/oficina-hosts

# 2) Superficie completa de la VPC, ejecutado DESDE el bastion
sudo nmap -sS -sV --version-intensity 5 -p- --max-retries 2 -T4 --open \
     -oA /var/log/nimbus-scan/vpc-interno 10.30.10.0/24 10.30.20.0/24 10.30.90.0/24

# 3) La misma infraestructura vista desde INTERNET
nmap -Pn -sV --top-ports 1000 --reason -oA /var/log/nimbus-scan/externo \
     api.nimbusreservas.example preprod.nimbusreservas.example
Opción Qué hace Por qué importa aquí
-sn / -sS Descubrir hosts sin escanear puertos / SYN scan que no completa la conexión El primero se compara con el inventario de 01-04; el segundo es rápido y fiable, y requiere privilegios
-sV Interroga el servicio para deducir producto y versión Convierte «5432 abierto» en «PostgreSQL 14.7», que es lo que se cruza con las CVE
-p- / --top-ports 1000 Los 65.535 puertos / los más usados El python -m http.server escuchaba en 8000, fuera del top 1000; pero un -p- externo tarda horas y se reserva para la pasada mensual
-Pn No comprobar si el host vive antes de escanear Obligatorio desde Internet: casi todo bloquea ICMP y sin -Pn no encontrarías nada
-T4 Plantilla de temporización agresiva (0-5) Equilibrio en red propia; T5 pierde precisión y T1-T2 son para no disparar detecciones
--reason / -oA Por qué clasifica así cada puerto / guarda en los tres formatos Distingue «filtrado» de «cerrado»; y sin la salida anterior no hay diferencial, que es el 90 % del valor

Salida real del escaneo externo:

Nmap scan report for preprod.nimbusreservas.example (203.0.113.47)
PORT     STATE    SERVICE    REASON   VERSION
22/tcp   open     ssh        syn-ack  OpenSSH 8.9p1 Ubuntu 3ubuntu0.1
443/tcp  open     ssl/http   syn-ack  nginx 1.18.0 (Ubuntu)
5432/tcp open     postgresql syn-ack  PostgreSQL DB 14.7 - 14.9
6379/tcp open     redis      syn-ack  Redis key-value store 6.0.16
8000/tcp open     http       syn-ack  SimpleHTTPServer 0.6 (Python 3.10.6)

Nmap scan report for api.nimbusreservas.example (203.0.113.20)
443/tcp  open     ssl/http   syn-ack  nginx 1.24.0
22/tcp   filtered ssh        no-response

Los hallazgos de 01-04, ahora medidos y con fecha: 5432 accesible desde Internet confirma R-03 (solo lo protege una contraseña); 6379 con Redis sin autenticación es R-07; 8000 es el python -m http.server olvidado 94 días; y el contraste entre 22/tcp open en preproducción y filtered en producción demuestra que en producción sí hay una regla y en preproducción no. A-22 es hoy el eslabón más débil de Nimbus.

Hacen falta los dos puntos de vista y miden cosas distintas. El externo (mensual automatizado, control C-10) mide la superficie que ve un atacante de Internet, es decir, la exposición. El interno (trimestral, desde el bastión y desde la VLAN de usuarios) mide lo que alcanza quien ya está dentro o un portátil comprometido, y por tanto revela fallos de segmentación y movimiento lateral: es el que habría descubierto, antes del incidente, que desde la red de administración se alcanzaba la zona de datos sin control intermedio (día 1-4 de 02-06).


  1. Escáner de vulnerabilidades: Greenbone/OpenVAS

nmap dice qué hay; el escáner dice qué le pasa a lo que hay. Greenbone Community Edition (el antiguo OpenVAS) es gratuito y suficiente para Nimbus. Solo hace dos cosas, y conviene no atribuirle magia:

  1. Comparar versiones contra una base de vulnerabilidades. Es rápido y es la fuente de casi todos los falsos positivos: las distribuciones parchean sin subir el número visible (backporting).
  2. Comprobaciones activas: envía una petición y observa la respuesta —si acepta TLS 1.0, si un panel responde con credenciales por defecto—. Más lento y mucho más fiable.
# Definir objetivo y tarea, y lanzarla; todo automatizable desde cron
gvm-cli socket --xml '<create_target><name>VPC produccion</name>
  <hosts>10.30.10.0/24,10.30.20.0/24</hosts></create_target>'
gvm-cli socket --xml '<create_task><name>Mensual autenticado</name>
  <config id="daba56c8-73ec-11df-a475-002264764cea"/><target id="TARGET_ID"/></create_task>'
gvm-cli socket --xml '<start_task task_id="TASK_ID"/>'

El config id es la plantilla de escaneo (Full and fast equilibra cobertura y tiempo). Un escáner que hay que lanzar a mano se lanza dos veces al año: se programa o no existe.

Autenticado frente a no autenticado

No autenticado Autenticado
Cómo funciona Sondea desde fuera y deduce Entra por SSH y lee el sistema por dentro
Qué ve Servicios expuestos y sus banners Todos los paquetes, parches, configuración, usuarios
Hallazgos por servidor Linux 8-15 60-150
Falsos positivos Muchos (por backporting) Pocos: compara la versión real del paquete
Riesgo Ninguno La credencial del escáner es un activo crítico

Encuentra mucho más porque deja de adivinar: sin credenciales no ve una biblioteca vulnerable que no escucha en ningún puerto, y la mayoría de las vulnerabilidades del sistema son exactamente eso. Para Nimbus: cuenta svc-scanner sin sudo, clave Ed25519 dedicada (03-06), origen restringido y rotación semestral.

== Informe Greenbone · Escaneo mensual autenticado · 2026-04-06 ==
6 hosts analizados · 1h42m · 3 Alto · 11 Medio · 24 Bajo · 63 Log

[10.0] 10.30.90.7   Redis sin autenticacion accesible en red
       Comprobacion ACTIVA: se ejecuto INFO sin credenciales      QoD 99%
       Solucion: requirepass + bind 127.0.0.1 + grupo de seguridad
[9.8]  10.30.20.5   PostgreSQL accesible desde 0.0.0.0/0
       Comprobacion ACTIVA: conexion establecida desde origen externo  QoD 98%
[7.5]  10.30.10.11  OpenSSL 3.0.2-0ubuntu1.9 - CVE-2026-1000
       Comprobacion de VERSION del paquete (escaneo autenticado)   QoD 97%
[5.3]  10.30.10.11  nginx 1.18.0 - multiples CVE
       Comprobacion de VERSION por banner remoto                   QoD 30%
       ^-- baja confianza: probable backport de la distribucion

Tres claves de lectura: QoD (Quality of Detection) es el campo más útil y el más ignorado —99 % con comprobación activa es un hecho, 30 % por banner es una hipótesis, así que se empieza por QoD alto y no por CVSS alto—; los 63 «Log» no son ruido, son inventario para 01-04; y un informe sin hallazgos solo significa que el escáner no encontró nada de lo que sabe buscar: el IDOR de A-04 nunca aparecerá aquí.


  1. Dependencias y contenedores

El 80 % del código que ejecuta la API no lo escribió Iván: son dependencias. Es la superficie que más crece y la más barata de medir.

pip-audit --requirement requirements.txt --format json --output /tmp/pip-audit.json --strict

--requirement audita las dependencias fijadas del proyecto (sin ella auditas la máquina) y --strict falla también si algo no se pudo analizar: tratar un «no lo sé» como «está bien» es la raíz de los falsos negativos.

Found 3 known vulnerabilities in 2 packages
Name      Version  ID                   Fix Versions
jinja2    3.1.2    GHSA-h5c8-rqwp-cp95  3.1.3
requests  2.28.1   GHSA-j8r2-6x86-q33q  2.31.0
requests  2.28.1   GHSA-9wx4-h78v-vm56  2.32.0

La de requests es la relevante: la API llama a la pasarela y al proveedor de correo con esa biblioteca. La de jinja2 solo afecta a plantillas del panel interno. Misma severidad nominal, prioridad muy distinta.

trivy image --severity HIGH,CRITICAL --ignore-unfixed \
            --scanners vuln,secret,misconfig --format table \
            registry.nimbus.example/api:2026.04.1

--severity HIGH,CRITICAL filtra el ruido (una imagen base típica arroja 300 hallazgos bajos, e incluirlos garantiza que no se mire ninguno); --ignore-unfixed oculta lo que aún no tiene parche, y es la opción más discutible —política de Nimbus: usarla en el CI para no bloquear por algo irresoluble y no usarla en el informe mensual—; y --scanners vuln,secret,misconfig añade secretos en las capas y malas prácticas del Dockerfile: tres herramientas por el precio de una.

registry.nimbus.example/api:2026.04.1 (debian 12.4)   Total: 14 (HIGH 12, CRITICAL 2)
┌───────────┬───────────────┬──────────┬───────────────┬──────────────┐
│ libssl3   │ CVE-2026-1000 │ CRITICAL │ 3.0.11-1      │ 3.0.13-1     │
│ zlib1g    │ CVE-2026-1001 │ HIGH     │ 1:1.2.13.dfsg │ 1:1.2.13-1+u1│
│ perl-base │ CVE-2026-1002 │ HIGH     │ 5.36.0-7      │ (no fijado)  │
└───────────┴───────────────┴──────────┴───────────────┴──────────────┘
Secret  api/.env.sample  AWS Access Key ID  (linea 4)   <-- revisar

Vulnerabilidad presente ≠ vulnerabilidad explotable. perl-base está en la imagen porque lo arrastra la base de Debian, pero la API nunca ejecuta Perl: está presente y no es alcanzable. Dos conceptos lo formalizan:

  • Alcanzabilidad: ¿hay un camino de ejecución desde el código de Nimbus hasta la función vulnerable? Analizarlo descarta hasta el 70 % de los hallazgos.
  • VEX (Vulnerability Exploitability eXchange): documento firmado en el que declaras, por CVE y producto, si te afecta (affected, not_affected, fixed, under_investigation) y por qué.

Nimbus emite su VEX junto al SBOM de 04-04: cuando una clínica pregunte por una CVE famosa, la respuesta será un documento y no una llamada de urgencia. La respuesta a un hallazgo no explotable no es ignorarlo, es documentarlo. Y la vía más eficaz para reducir su número es cambiar a una imagen base mínima o distroless (05-07), que elimina de golpe el 80 % de estas líneas.


  1. Análisis estático de código y escaneo de secretos

bandit -r app/ -ll -f json -o /tmp/bandit.json
semgrep --config=p/python --config=p/security-audit --sarif -o /tmp/semgrep.sarif app/

-ll limita a severidad media o superior; --sarif produce el formato estándar que GitHub muestra en su pestaña de seguridad. Hallazgo real en el código de Nimbus:

>> Issue: [B501:request_with_no_cert_validation] Requests call with verify=False
   Severity: High  Confidence: High  CWE-295  app/integrations/pasarela.py:44
# ANTES — el `verify=False` que apareció en el módulo 2. Desactiva la validación
# del certificado: cualquiera en la ruta puede interponerse y leer o alterar la
# peticion de pago. TLS sin validacion no protege de nada (03-05).
r = requests.post(URL_PASARELA, json=payload, verify=False, timeout=10)

# DESPUÉS — validacion activa y confianza acotada de forma explicita.
r = requests.post(
    URL_PASARELA,
    json=payload,
    verify="/etc/ssl/certs/ca-certificates.crt",  # cadena de confianza explicita
    timeout=(3.05, 10),                            # conexion y lectura por separado
)
r.raise_for_status()

El SAST acierta aquí porque verify=False es un patrón textual inequívoco, y falla con el IDOR porque saber que falta WHERE tenant_id exige entender el modelo de datos. Regla: encuentra errores de patrón, no errores de razonamiento. En 05-05 escribirás una regla semgrep propia que sí detecta el patrón concreto de Nimbus.

gitleaks detect --source . --log-opts="--all" --report-format json --report-path /tmp/gitleaks.json

--log-opts="--all" es la opción crítica: sin ella solo mira el árbol actual; con ella recorre todos los commits y ramas. Un secreto borrado al día siguiente sigue en el historial y sigue siendo válido.

Finding:  DATABASE_URL=postgresql://nimbus_api:[email protected]:5432/nimbus
RuleID:   postgres-connection-string
File:     deploy/docker-compose.override.yml
Commit:   a91f3c7 (2024-11-02) Author: [email protected]

Es el riesgo R-06 y la escalada del día 5 de 02-06. Corrección en este orden: (1) rotar la credencial hoy —está comprometida desde 2024 y borrar el fichero no la invalida; es el paso urgente y el que casi todos posponen—; (2) moverla al gestor de secretos (03-06); (3) reescribir el historial con git filter-repo, que es cosmética comparada con el paso 1; y (4) gitleaks como hook de pre-commit y paso bloqueante del CI (control C-12).


  1. Priorización: CVSS, EPSS, KEV y exposición

Aquí el proceso se gana el sueldo. Un escaneo de Nimbus produce ~180 hallazgos y Lucía tiene 440 h/año para todo. Ordenar por CVSS descendente es el error más extendido del sector.

Grupo CVSS Qué mide Quién lo calcula
Base Gravedad intrínseca e invariable El fabricante o el NVD (9.8 por ejecución remota sin autenticación)
Temporal (Amenaza en v4) Si hay exploit público y si hay parche Se actualiza con el tiempo: 9.8 baja a 8.5 sin exploit conocido
Ambiental Tu entorno: criticidad del activo y controles presentes Solo tú: 9.8 en una máquina aislada baja a 4.0

El grupo ambiental es el que convierte una lista genérica en una lista tuya, y el único que nadie puede calcular por ti. A él se suman dos señales externas:

  • EPSS (FIRST): probabilidad de que una CVE sea explotada en los próximos 30 días, de 0 a 1, actualizada a diario. Más del 90 % de las CVE tienen EPSS < 0,05; una minoría concentra casi toda la explotación real.
  • KEV (CISA): catálogo de vulnerabilidades con explotación confirmada en el mundo real. No es predicción, es un hecho observado; la lista es corta y consultarla es gratis.

Regla operativa de Nimbus: todo lo que esté en KEV y sea accesible se trata como crítico, tenga el CVSS que tenga. Una de 7.5 en KEV es más urgente que una de 9.8 con EPSS 0,001.

Prioridad Criterio Plazo
P0 Emergencia En KEV y expuesto a Internet, o compromiso activo 24 h, fuera de ventana si hace falta
P1 Crítica CVSS ≥ 9.0 o EPSS ≥ 0,10 sobre activo crítico accesible 7 días (coincide con C-16)
P2 Alta CVSS 7.0-8.9 sobre activo crítico, o KEV no expuesto 30 días
P3 Media CVSS 4.0-6.9, o alta sobre activo no crítico 90 días
P4 Baja CVSS < 4.0 o no alcanzable (documentado en VEX) Siguiente ciclo o excepción con caducidad

Los plazos cuentan desde el descubrimiento, no desde la publicación de la CVE. Y si un P1 no llega a plazo no se reetiqueta a P2: se abre una excepción firmada (04-02). Rebajar la prioridad para que el cuadro de mando quede verde es la forma más común de corromper el proceso.

"""Ordena hallazgos combinando gravedad, explotacion real y exposicion."""

HALLAZGOS = [   # id, cvss, epss, kev, expuesto, activo, criticidad(1-5)
    ("CVE-2026-1000",  9.8, 0.72,  True,  True,  "A-04 API",       5),
    ("CVE-2026-1002",  8.1, 0.004, False, False, "A-04 (perl)",    5),
    ("REDIS-SIN-AUTH",10.0, 0.90,  True,  True,  "A-22 preprod",   2),
    ("CVE-2026-1010",  7.5, 0.31,  True,  False, "A-01 BD",        5),
    ("CVE-2026-1044",  9.1, 0.002, False, False, "A-14 portatiles",3),
]

def prioridad(cvss, epss, kev, expuesto, criticidad):
    base = cvss / 10.0                                   # 1) gravedad normalizada
    explotacion = 1.0 if kev else min(1.0, epss * 3)     # 2) KEV es HECHO; EPSS, matiz
    exposicion = 3.0 if expuesto else 1.0                # 3) alcanzable triplica urgencia
    importancia = criticidad / 5.0                       # 4) inventario de 01-04
    return round(base * (0.3 + 0.7 * explotacion) * exposicion * importancia, 3)

filas = [(prioridad(c, e, k, x, cr), i, a) for i, c, e, k, x, a, cr in HALLAZGOS]
for p, ident, activo in sorted(filas, reverse=True):
    plazo = "24 h" if p >= 2.0 else "7 dias" if p >= 1.0 else "30 dias" if p >= 0.4 else "90 dias"
    print(f"{p:>6}  {ident:<16} {activo:<18} -> {plazo}")
 2.940  CVE-2026-1000    A-04 API           -> 24 h
 2.250  CVE-2026-1010    A-01 BD            -> 24 h
 1.200  REDIS-SIN-AUTH   A-22 preprod       -> 7 dias
 0.257  CVE-2026-1002    A-04 (perl)        -> 90 dias
 0.230  CVE-2026-1044    A-14 portatiles    -> 90 dias

Lo que revela el resultado: REDIS-SIN-AUTH tiene el CVSS más alto (10.0) y cae al tercer puesto por vivir en un activo de criticidad 2; CVE-2026-1044 tiene 9.1 —más que la 1010, de 7.5— y queda última porque ni está en KEV, ni es alcanzable, ni afecta a un activo crítico. Ordenar por CVSS habría puesto a Lucía a trabajar tres días en las dos vulnerabilidades menos urgentes. Aviso honesto: que A-22 tenga criticidad 2 es la clasificación documentada, y el inventario admite que está «a revisar». Si preproducción tiene copia de datos reales, su criticidad es 5 y el Redis sube al primer puesto: revisar esa celda es más rentable que cualquier escaneo.


  1. Falsos positivos, falsos negativos y validación

Definición Coste Ejemplo en Nimbus
Falso positivo Reporta algo que no existe o no aplica Tiempo y pérdida de credibilidad del proceso nginx 1.18.0 marcado pese al backport de Ubuntu
Falso negativo No reporta algo que sí existe Riesgo real no gestionado El IDOR, que ningún escáner detectó

Validación de un hallazgo, en orden de coste: (1) comprobar la versión real del paquete (dpkg -l | grep nginx) resuelve el 60 % de los falsos positivos de infraestructura; (2) leer el aviso de la distribución (USN de Ubuntu, DSA de Debian), que dice si esa versión ya está corregida; (3) comprobar la alcanzabilidad; (4) reproducir de forma no destructiva solo si persiste la duda —verificar que un puerto responde o que falta una cabecera, nunca explotar en producción: esa conversación es de 05-03—; y (5) documentar la decisión, apta o no, porque un falso positivo cerrado sin registrar reaparece cada mes y cuesta lo mismo cada vez.

Un escáner nunca sustituye al criterio. No sabe que A-01 contiene historiales que revelan información de salud, ni que A-19 tiene acceso permanente, ni que preproducción quizá tenga datos reales, ni puede encontrar un fallo de autorización. Mide versiones y configuraciones: el riesgo lo pone el contexto, y el contexto lo pone una persona. Eso se escribe en la declaración de limitaciones del informe (§10).


  1. Integración en el ciclo y métricas del proceso

Momento Qué se ejecuta Duración ¿Bloquea?
Cada pull request gitleaks, semgrep, pip-audit < 3 min Sí: secretos y críticas nuevas
Cada despliegue trivy sobre la imagen final < 2 min Sí, según umbral
Semanal ZAP baseline contra preproducción (05-05) 20 min No: informa
Mensual (C-10) nmap externo + Greenbone autenticado + prowler (05-07) 2-4 h No: hallazgos con dueño
Tras cambios de infra. nmap externo diferencial 10 min No: alerta de puerto nuevo

El último es el más barato y el más infravalorado: comparar el escaneo de hoy con el de ayer y avisar solo de lo nuevo es lo que habría detectado el http.server el mismo día, y no 94 días después.

# .github/workflows/seguridad.yml
name: Analisis de seguridad
on:
  pull_request:
  push: { branches: [main] }
  schedule: [{ cron: "0 5 * * 1" }]   # lunes: captura CVE nuevas SIN cambios de codigo
permissions: { contents: read, security-events: write }

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }      # historial COMPLETO: sin esto gitleaks no ve el pasado

      - uses: gitleaks/gitleaks-action@v2   # BLOQUEANTE: un secreto nunca se negocia

      - name: Dependencias Python
        run: |
          pip install pip-audit
          pip-audit -r requirements.txt --strict --format json -o pip-audit.json || true
          # `|| true` deja que el umbral lo aplique NUESTRA politica, no el binario
      - run: python ci/umbral.py pip-audit.json --fallar-si CRITICAL,HIGH-KEV

      - run: docker build -t nimbus/api:${{ github.sha }} .
      - uses: aquasecurity/trivy-action@master
        with:
          image-ref: nimbus/api:${{ github.sha }}   # la imagen de ESTE commit, no :latest
          severity: CRITICAL,HIGH
          ignore-unfixed: true        # no bloquear por lo que aun no tiene parche
          exit-code: "1"              # 1 = build roto si queda algo sobre el umbral
          format: sarif
          output: trivy.sarif
      - if: always()                  # publicar TAMBIEN cuando el paso anterior falla
        uses: github/codeql-action/upload-sarif@v3
        with: { sarif_file: trivy.sarif }

El principio de diseño: bloquear lo accionable e informar de lo que no lo es. Un pipeline que rompe el build por hallazgos que nadie puede corregir termina desactivado en dos semanas, y entonces no protege de nada. Y el criterio de fallo lo decide Nimbus (ci/umbral.py), no una herramienta que cambia de versión: externalizarlo es ceder el gobierno del riesgo.

Métrica Tipo Objetivo Qué revela al fallar
Cobertura del escaneo KPI 100 % Mírala primero: el resto de métricas mienten si no es 100 %. Es la que más sorpresas da: suele revelar que el 30 % no cubierto era preproducción
MTTR por prioridad KRI P1 ≤ 7 d · P2 ≤ 30 d Capacidad insuficiente o proceso de parcheo roto
Críticas abiertas fuera de plazo KRI 0 Es la cifra del comité: si crece, hay que invertir o aceptar formalmente
Tasa de reapertura y % cerrados con reescaneo KPI < 5 % · 100 % Se cierra sin verificar: «cerrado» significa «alguien dijo que sí»

  1. El informe de vulnerabilidades

Destinatario Qué necesita Extensión
Marta (dirección) Riesgo de negocio, tendencia y qué decisión se le pide 1 página: semáforo, 3 cifras, 1 petición
Lucía (sistemas) Qué toca, en qué máquina, con qué comando y para cuándo Tabla ordenada por prioridad
Iván (desarrollo) Fichero, línea, corrección y prueba de regresión Issues en el repositorio, no un PDF
Cliente o auditor (06-04) Que existe proceso, cadencia y evidencia 1-2 páginas sin detalle explotable

Estructura del informe mensual: resumen ejecutivo, alcance y limitaciones, metodología y versiones de las herramientas, hallazgos priorizados, tendencia, cerrados y verificados, y anexo técnico. Las limitaciones son lo que protege honestamente al lector: dicen qué no se escaneó y qué fallos estas herramientas no encuentran.

### VUL-2026-014 · PostgreSQL de produccion accesible desde Internet

- **Prioridad:** P0 (KEV no · CVSS 9.8 · **expuesto** · activo A-01, criticidad 5)
- **Activo:** `db-prod-1` (10.30.20.5) · **Riesgo asociado:** R-03 (04-01)
- **Descubierto:** 2026-04-06, escaneo externo mensual (C-10) · **Plazo:** 2026-04-07

**Descripcion.** El grupo de seguridad permite 5432/tcp desde `0.0.0.0/0`. Cualquier
equipo de Internet puede intentar autenticarse; la unica proteccion es la contrasena
del rol `nimbus_api`, sin limite de intentos.

**Evidencia.**
    $ nmap -Pn -p 5432 -sV 203.0.113.47
    5432/tcp open postgresql PostgreSQL DB 14.7 - 14.9   (syn-ack)

**Impacto en el negocio.** Acceso a los datos de 40 clinicas, incluidos historiales
que revelan indirectamente informacion de salud. Equivale al dia 5 de 02-06. Brecha
notificable bajo RGPD (06-03).

**Correccion.** Restringir la regla de entrada a `10.30.10.0/24`. Sin ventana de
mantenimiento: la API conecta por la ruta interna. **Esfuerzo: 20 minutos.**
**Verificacion.** Reescaneo externo de 5432; estado esperado `filtered`, mas prueba
de humo de la API. **Dueno:** Lucia · **Estado:** Abierto

Los cinco elementos que convierten un hallazgo en accionable: evidencia reproducible, impacto de negocio y no solo técnico, corrección concreta con esfuerzo estimado, criterio de verificación explícito y un dueño con nombre. El error más frecuente es entregar el volcado de la herramienta: 300 páginas sin priorizar que garantizan que no se corrija nada, porque nadie sabe por dónde empezar.


Errores Comunes y Consejos

  • Escanear una vez al año y llamarlo gestión de vulnerabilidades. El inventario cambia cada semana; mejor mensual, automatizado y revisado.
  • Ordenar por CVSS y empezar por arriba. Sin exposición, EPSS/KEV y criticidad del activo dedicarás el tiempo a lo que menos importa. Es el error más caro de la lección.
  • Escanear sin credenciales «para no molestar». Pierdes entre el 70 y el 85 % de los hallazgos del sistema; el escaneo autenticado es la mejora individual más rentable.
  • Olvidar preproducción. A-22 tiene hoy más puertos abiertos que producción. El atacante no distingue entornos: distingue puertas.
  • Bloquear el CI con umbrales imposibles, o cerrar hallazgos sin verificar. Lo primero acaba con el pipeline desactivado; lo segundo convierte «cerrado» en «alguien dijo que sí». Vigila la tasa de reapertura.
  • Consejo: empieza por el diferencial. Antes de montar Greenbone, automatiza un nmap externo semanal que compare con el anterior y guarda siempre la salida cruda con fecha (-oA, JSON, SARIF): es tu serie histórica, y sin tendencia no hay conversación con dirección. Media hora de trabajo que habría evitado dos de los cuatro hallazgos de 01-04.
  • Consejo: si solo recuerdas una regla, que sea esta. Una CVE en KEV y expuesta se trata hoy.

Ejercicios

Ejercicio 1 — Interpretar un escaneo y decidir el orden de trabajo

Lucía lanza el primer escaneo externo mensual y obtiene:

Nmap scan report for pruebas-2019.nimbusreservas.example (203.0.113.61)
80/tcp   open  http     Apache httpd 2.4.29 ((Ubuntu))
443/tcp  open  ssl/http Apache httpd 2.4.29
3306/tcp open  mysql    MySQL 5.7.33
  1. ¿Qué hay que averiguar sobre pruebas-2019 antes incluso de mirar las versiones?
  2. Ordena las acciones de la primera semana justificando el orden con los criterios de §7.
  3. ¿Qué le falta a este escaneo para que el proceso sea completo?

Ejercicio 2 — Priorizar cinco hallazgos

Ordena y asigna plazo según la política de §7, justificando cada decisión:

# Hallazgo CVSS EPSS KEV Expuesto Activo
A RCE en biblioteca de imágenes del panel interno 9.8 0.02 No No A-04 (crítico)
B Redis sin autenticación en preproducción 10.0 0.90 A-22 (a revisar)
C TLS 1.0 en un subdominio de marketing 5.3 0.01 No Web estática
D Credencial de PostgreSQL en el historial de git desde 2024 A-10, A-01
E Escalada local en el kernel de los portátiles 7.8 0.15 No A-14

Ejercicio 3 — Corregir un pipeline

Identifica al menos cinco problemas en la propuesta de Iván y explica cómo se corrige cada uno.

name: seguridad
on: { workflow_dispatch: {} }
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: gitleaks detect --source .
      - run: trivy image nimbus/api:latest --exit-code 1
      - run: pip-audit

Soluciones

Ejercicio 1

(1) La primera pregunta no es técnica, es de inventario: ¿qué es pruebas-2019, quién lo puso, qué datos contiene y sigue haciendo falta? Ese nombre sugiere un entorno de hace siete años que nadie mantiene y que no está en el inventario de 01-04 ni en el alcance de ningún parcheo. Si nadie lo sabe, la corrección no es parchearlo: es apagarlo tras confirmar que nada depende de él, conservando una copia. Apagar es la corrección más barata, más rápida y más definitiva que existe, y la que menos se considera.

(2) Orden de la primera semana:

  1. MySQL 3306 expuesto (P0, 24 h). Es el patrón de R-03 pero peor: base de datos accesible desde Internet en un host sin mantenimiento desde 2019, con credenciales probablemente débiles. Se cierra en el firewall hoy, antes de investigar nada.
  2. Determinar si contiene datos personales reales (P0, en paralelo). Si los contiene y llevaba años expuesto, deja de ser un hallazgo técnico: es una posible brecha con obligaciones del RGPD (06-03) y activación del plan de 04-05.
  3. Apagar el host (P1, 7 días) una vez confirmado que nada depende de él. Apache 2.4.29 acumula años de CVE: parchearlo es invertir en algo que debe desaparecer.
  4. Añadirlo al inventario y revisar el DNS buscando más subdominios olvidados: que apareciera en un escaneo y no en el inventario significa que la cobertura no era del 100 %.

(3) Qué falta: el escaneo solo mira los puertos habituales de nombres que alguien recordaba. Faltan la enumeración de subdominios desde la zona DNS, un -p- mensual (el http.server del 8000 no sale en --top-ports 1000), escaneo UDP básico, escaneo interno desde la VLAN de usuarios y desde la VPC para medir la segmentación, y las capas que nmap no cubre: escáner autenticado, SCA, SAST y configuración cloud. nmap es el 15 % del proceso.

Ejercicio 2

Orden # Prioridad Plazo Justificación
1.º D P0 24 h No tiene CVSS porque no es una CVE, y es lo más grave: una credencial válida de la base de datos crítica, expuesta desde 2024. No hay que explotar nada, basta con usarla. Es R-06 y el día 5 de 02-06. Rotar hoy; limpiar el historial puede esperar
2.º B P0 24 h KEV + EPSS 0,90 + expuesto: los tres factores al máximo. La baja criticidad nominal de A-22 no lo rescata, porque está «a revisar» y probablemente comparte red o datos con producción. Cerrar el puerto son minutos
3.º E P2 30 días Está en KEV, la explotación es real, pero es escalada local: exige que el atacante ya esté en el portátil. Es el segundo eslabón de la cadena, no el primero. Se agrupa con el parcheo gestionado del parque (05-06)
4.º A P2 30 días 9.8 sobre activo crítico, pero no expuesto y EPSS 0,02. Aquí es donde ordenar por CVSS falla: parece la primera y es la cuarta. Conviene confirmar la alcanzabilidad; si el panel no procesa imágenes de usuario, baja a P3 y se documenta en VEX
5.º C P3 90 días Expuesto pero de bajo impacto: web estática, sin datos ni sesiones. Una línea de nginx (03-05) en el siguiente mantenimiento. Tratarlo como urgente «porque sale en rojo» es justo el error que la política evita

Observación transversal: los dos primeros no requieren parche, sino un cambio de configuración y una rotación de credencial, ambos gratuitos y de minutos. Lo más urgente casi nunca es lo más caro.

Ejercicio 3

  1. workflow_dispatch como único disparador: solo se ejecuta a mano, es decir, casi nunca. Faltan pull_request, push a main y un schedule que capture CVE nuevas sin cambios de código.
  2. checkout sin fetch-depth: 0: gitleaks solo ve el último commit, así que los secretos del historial —el caso real de Nimbus— pasan desapercibidos indefinidamente.
  3. trivy image nimbus/api:latest: escanea una etiqueta que no es la imagen que este cambio construye. Se valida una imagen y se despliega otra; hay que construir y escanear por SHA del commit.
  4. --exit-code 1 sin --severity: bloquea con cualquier hallazgo, incluidos los informativos y los que no tienen parche. Se desactivará en dos semanas.
  5. pip-audit sin -r requirements.txt: audita el entorno del runner, no el proyecto; puede pasar en verde con dependencias vulnerables.
  6. No se publica ningún resultado y no hay permissions explícitas: sin SARIF ni artefactos no hay historial, tendencia ni evidencia para 04-03, y el token opera con más permisos de los necesarios, contra el mínimo privilegio de 01-03.

La versión corregida es la del apartado 9, con fetch-depth: 0, los tres disparadores, permissions acotadas, umbral propio en ci/umbral.py, imagen etiquetada por SHA y publicación SARIF con if: always().


Conclusión

Has convertido el «verificar» del módulo 4 en un proceso medible. Sabes que la gestión de vulnerabilidades es un ciclo —inventariar, descubrir, priorizar, remediar, verificar e informar— y no un PDF anual, y que el paso 0 manda: sin inventario no hay cobertura, y sin cobertura las demás métricas mienten. Conoces el mapa de las nueve familias de herramientas y, sobre todo, qué no ve ninguna: el IDOR de Nimbus no aparece en ningún escáner, porque estas herramientas detectan errores de patrón y no errores de razonamiento.

Manejas nmap opción a opción y sabes por qué el escaneo externo y el interno miden cosas distintas; con él has reencontrado con fecha y evidencia los cuatro hallazgos de 01-04. Sabes qué hace realmente un escáner de vulnerabilidades, por qué el escaneo autenticado encuentra entre cinco y diez veces más, y a leer un informe empezando por el QoD. Con pip-audit y trivy distingues vulnerabilidad presente de vulnerabilidad explotable, con la alcanzabilidad y el documento VEX como forma correcta de decir «no nos afecta» sin perder trazabilidad. Con semgrep, bandit y gitleaks has cerrado el verify=False y has encontrado la credencial de PostgreSQL en el historial, cuya corrección empieza por rotarla y no por borrar el fichero. Y te llevas la parte que separa un proceso útil de una hoja de cálculo inútil: priorizar con CVSS base/temporal/ambiental, EPSS como probabilidad y KEV como hecho observado, la política de plazos P0-P4 y un cálculo que reordena la lista hasta dejar la CVE de 9.1 en el último puesto; validar falsos positivos en cinco pasos; integrarlo todo en un pipeline que bloquea lo accionable e informa del resto; y redactar un hallazgo accionable con evidencia, impacto de negocio, corrección estimada, verificación y dueño.

Pero fíjate en el límite de todo lo anterior: este proceso encuentra puertas mal cerradas; no ve a nadie entrando por ellas. Si mañana un atacante usa la credencial de la consultora a las 22:14, ningún escáner de esta lección se enterará: el puerto está legítimamente abierto y la credencial es válida. Ese es el hueco que dejó veinte días de silencio en el incidente de 02-06 y que el catálogo de 04-03 refleja con ocho controles detectivos de los que solo dos están implantados. En Técnicas de Monitoreo y Detección (05-02) construimos por fin esos controles: qué registrar, dónde centralizarlo, cómo escribir detecciones que se disparen con lo que importa y callen con lo que no, y cómo reducir el tiempo de permanencia del atacante de veinte días a una mañana.

Curso de Fundamentos de Seguridad Informática

Módulo 1: Introducción a la Seguridad Informática

Módulo 2: Ciberseguridad

Módulo 3: Criptografía

Módulo 4: Gestión de Riesgos y Medidas de Protección

Módulo 5: Herramientas y Técnicas de Seguridad

Módulo 6: Buenas Prácticas y Normativas

Módulo 7: Proyecto Final

© Copyright 2026. Todos los derechos reservados