intermediate Por Mathias Paulenko

Plantilla de Informe de Escaneo de Vulnerabilidades

Una plantilla para resumir hallazgos de escaneos de vulnerabilidades, incluyendo cobertura de activos, distribucion por severidad y seguimiento de remediacion.

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Descripcion General

Un Informe de Escaneo de Vulnerabilidades resume los hallazgos de escaneos automatizados de infraestructura, aplicaciones y servicios cloud. Ayuda a equipos de seguridad, gerentes de ingenieria y ejecutivos a comprender la exposicion actual, priorizar la remediacion y rastrear el progreso a lo largo del tiempo. Esta plantilla soporta tanto audiencias tecnicas como ejecutivas.

Cuando Usar

  • For alternatives, see Dependency Vulnerability Report Template.

  • Despues de un escaneo programado de vulnerabilidades en produccion o staging.

  • Antes de un lanzamiento o auditoria de cumplimiento.

  • Durante revisiones de seguridad mensuales o trimestrales.

  • Al incorporar un nuevo activo o servicio al programa de escaneo.

  • Despues de una brecha o incidente para evaluar sistemas expuestos.

Prerequisitos

  • Un escaner de vulnerabilidades configurado y ejecutado, como Nessus, Qualys o Tenable.
  • Un inventario de activos con duenos, ambientes y clasificaciones de criticidad.
  • Un esquema de clasificacion de severidad, como CVSS o tu propia matriz de riesgo.
  • SLAs de remediacion definidos por severidad.

Solucion

Plantilla

1. Resumen Ejecutivo

CampoValor
Periodo de reporte2026-06-01 a 2026-06-30
Activos totales escaneados142
Activos con hallazgos38
Hallazgos criticos3
Hallazgos altos12
Hallazgos medios47
Hallazgos bajos89
Tasa de remediacion78% de hallazgos cerrados del periodo anterior
Tendencia generalMejorando

2. Alcance y Cobertura del Escaneo

Grupo de ActivosActivos EscaneadosCobertura %Tipo de EscaneoDueno
Servidores de produccion54100%Escaneo de red autenticadoEquipo de plataforma
Cargas de trabajo cloud3695%Escaneo basado en agenteEquipo cloud
Aplicaciones web18100%DASTSeguridad de aplicaciones
Contenedores2480%Escaneo de registro + runtimeEquipo DevOps
Bases de datos10100%Escaneo de configuracionEquipo DBA

3. Hallazgos por Severidad

SeveridadCantidadAbiertosEn ProgresoCerradosPromedio de Dias para Remediar
Critico31112
Alto124357
Medio4715122021
Bajo8930253445

4. Principales Hallazgos Criticos

HallazgoCVEActivos AfectadosSeveridadExploit DisponibleRemediacionDuenoFecha Limite
OpenSSL sin parchearCVE-2026-XXXXapi-01, api-02CriticoSiActualizar a 3.0.9Equipo backend2026-07-02
Servicio RDP expuestoN/Ajump-host-legacyCriticoSiDeshabilitar RDP, usar bastionEquipo de red2026-07-01
Cuenta admin por defectoN/Astaging-dbCriticoNoRemover o renombrar cuentaEquipo DBA2026-07-03

5. Seguimiento de Remediacion

ID HallazgoTituloSeveridadDuenoAbiertoFecha LimiteEstadoNotas
VULN-042OpenSSL sin parchearCriticoEquipo backend2026-06-152026-07-02En progresoParche en staging para release
VULN-043Puerto SMB expuestoAltoEquipo de red2026-06-202026-07-05AbiertoRegla de firewall pendiente de aprobacion
VULN-044Version TLS obsoletaMedioEquipo de plataforma2026-06-102026-07-10En progresoConfig probada en staging
VULN-045Header de seguridad faltanteBajoEquipo frontend2026-06-252026-08-01AbiertoProgramado para el siguiente sprint

6. Analisis de Tendencias

PeriodoCriticoAltoMedioBajoTotalCerradosTasa de Remediacion
2026-035186211019516082%
2026-04415559817214584%
2026-05214509215813082%
2026-06312478915112078%

Explicacion

El informe convierte la salida cruda del escaner en una historia estructurada: que se escaneo, que se encontro, quien lo esta corrigiendo y que tan rapido. El resumen ejecutivo da a la direccion una vista rapida, mientras que las tablas detalladas proporcionan elementos útiles a los ingenieros. El analisis de tendencias muestra si el programa de seguridad esta mejorando o quedando atras.

Variantes

  • Informe de dashboard ejecutivo: Resumen de una pagina con graficos y postura de riesgo.
  • Informe tecnico de escaneo: Detalles completos de hallazgos con CVSS, paquetes afectados y comandos de remediacion.
  • Informe de configuracion cloud: Enfocado en malconfiguraciones cloud de herramientas como Prowler o CloudSploit.
  • Informe DAST: Hallazgos especificos de aplicaciones web de escaneres en vivo.
  • Informe mensual de cumplimiento: Mapea hallazgos a marcos de control y rastrea cumplimiento de SLA.

Lo que funciona

  • Incluye la cobertura de activos para que los interesados sepan que no se escaneo.
  • Prioriza por explotabilidad e impacto de negocio, no solo por CVSS.
  • Asigna cada hallazgo a un dueno nombrado con fecha limite.
  • Rastrea el estado de remediacion semanalmente hasta el cierre.
  • Vuelve a probar despues de la remediacion para confirmar la correccion.
  • Compara tendencias a traves de periodos para medir mejora.
  • Documenta riesgos aceptados con justificacion y vencimiento.

Errores Comunes

  • Reportar solo conteos de vulnerabilidades sin contexto.
  • No rastrear que activos fueron inalcanzables o no escaneados.
  • Asignar hallazgos a equipos sin capacidad o contexto.
  • Cerrar hallazgos sin verificar la correccion.
  • Ignorar hallazgos medios y bajos hasta que se acumulan.
  • No incluir datos de tendencia o comparacion historica.

Soluciones Avanzadas

Pipeline automatizado de escaneo de vulnerabilidades con Trivy y GitHub Actions

Programa escaneos automatizados de imagenes de contenedor y genera reportes estructurados:

# .github/workflows/vuln-scan.yml
name: Vulnerability Scan Report
on:
  schedule:
    - cron: "0 2 * * 1"  # Weekly Monday 2am
  workflow_dispatch:

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: "myregistry/app:latest"
          format: "json"
          output: "trivy-report.json"
          severity: "CRITICAL,HIGH,MEDIUM"
          exit-code: "0"  # Don't fail the workflow
      - name: Parse and summarize findings
        run: |
          python scripts/parse_vuln_report.py \
            --input trivy-report.json \
            --output vuln-summary.md \
            --format markdown
      - name: Upload report
        uses: actions/upload-artifact@v4
        with:
          name: vulnerability-report
          path: |
            trivy-report.json
            vuln-summary.md
# scripts/parse_vuln_report.py
import json
import argparse
from collections import Counter
from datetime import datetime

def parse_trivy_report(input_file: str, output_file: str) -> None:
    """Parse Trivy JSON report and generate a markdown summary."""
    with open(input_file) as f:
        data = json.load(f)

    severity_counts = Counter()
    findings_by_severity = {"CRITICAL": [], "HIGH": [], "MEDIUM": [], "LOW": []}

    for result in data.get("Results", []):
        target = result.get("Target", "unknown")
        for vuln in result.get("Vulnerabilities", []):
            severity = vuln.get("Severity", "UNKNOWN")
            severity_counts[severity] += 1
            if severity in findings_by_severity:
                findings_by_severity[severity].append({
                    "target": target,
                    "cve": vuln.get("VulnerabilityID", "N/A"),
                    "package": vuln.get("PkgName", "N/A"),
                    "installed": vuln.get("InstalledVersion", "N/A"),
                    "fixed": vuln.get("FixedVersion", "N/A"),
                })

    with open(output_file, "w") as f:
        f.write(f"# Vulnerability Scan Report - {datetime.now().strftime('%Y-%m-%d')}\n\n")
        f.write("## Executive Summary\n\n")
        f.write(f"| Metric | Value |\n|--------|-------|\n")
        f.write(f"| Total findings | {sum(severity_counts.values())} |\n")
        f.write(f"| Critical | {severity_counts.get('CRITICAL', 0)} |\n")
        f.write(f"| High | {severity_counts.get('HIGH', 0)} |\n")
        f.write(f"| Medium | {severity_counts.get('MEDIUM', 0)} |\n")
        f.write(f"| Low | {severity_counts.get('LOW', 0)} |\n\n")

        for severity in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
            findings = findings_by_severity[severity]
            if not findings:
                continue
            f.write(f"## {severity.title()} Findings ({len(findings)})\n\n")
            f.write("| Target | CVE | Package | Installed | Fixed |\n")
            f.write("|--------|-----|---------|-----------|-------|\n")
            for finding in findings[:20]:  # Top 20 per severity
                f.write(
                    f"| {finding['target']} | {finding['cve']} | "
                    f"{finding['package']} | {finding['installed']} | "
                    f"{finding['fixed']} |\n"
                )
            if len(findings) > 20:
                f.write(f"\n*...and {len(findings) - 20} more*\n")
            f.write("\n")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--input", required=True)
    parser.add_argument("--output", required=True)
    args = parser.parse_args()
    parse_trivy_report(args.input, args.output)

Prowler para escaneo de configuracion AWS

Escanea entornos AWS en busca de malconfiguraciones y genera un reporte alineado con cumplimiento:

#!/bin/bash
set -euo pipefail

# Run Prowler against AWS account
prowler aws \
  --output-formats json html \
  --output-directory ./prowler-reports \
  --quiet

# Extract critical findings
jq '[.Controls[] | select(.Status == "FAIL" and .Severity == "critical")] | length' \
  prowler-reports/*.json

# Generate summary by service
jq -r '.Controls[] | select(.Status == "FAIL") | .ControlID + " - " + .CheckTitle' \
  prowler-reports/*.json > aws-findings-summary.txt

echo "=== AWS Misconfiguration Summary ==="
echo "Critical failures: $(jq '[.Controls[] | select(.Status == "FAIL" and .Severity == "critical")] | length' prowler-reports/*.json)"
echo "High failures: $(jq '[.Controls[] | select(.Status == "FAIL" and .Severity == "high")] | length' prowler-reports/*.json)"
echo "Medium failures: $(jq '[.Controls[] | select(.Status == "FAIL" and .Severity == "medium")] | length' prowler-reports/*.json)"

Priorizacion basada en riesgo con puntuacion EPSS

Combina CVSS con Exploit Prediction Scoring System (EPSS) para priorizar la remediacion:

import requests
from dataclasses import dataclass
from typing import List

@dataclass
class VulnerabilityPriority:
    cve: str
    cvss_score: float
    epss_score: float
    is_exploited: bool
    is_internet_facing: bool
    has_sensitive_data: bool

    @property
    def priority_score(self) -> float:
        """Calculate composite priority score."""
        base = self.cvss_score * 10  # Scale CVSS to 0-100
        epss_boost = self.epss_score * 30  # EPSS probability weight
        exploited_boost = 25 if self.is_exploited else 0
        exposure_boost = 15 if self.is_internet_facing else 0
        data_boost = 10 if self.has_sensitive_data else 0
        return round(base + epss_boost + exploited_boost + exposure_boost + data_boost, 1)

    @property
    def priority_label(self) -> str:
        if self.priority_score >= 80:
            return "P0 - Immediate"
        elif self.priority_score >= 60:
            return "P1 - Urgent"
        elif self.priority_score >= 40:
            return "P2 - High"
        elif self.priority_score >= 20:
            return "P3 - Medium"
        else:
            return "P4 - Low"

def get_epss_score(cve_id: str) -> float:
    """Fetch EPSS score from FIRST.org API."""
    try:
        resp = requests.get(
            f"https://api.first.org/data/v1/epss?cve={cve_id}",
            timeout=10,
        )
        data = resp.json()
        if data.get("data"):
            return float(data["data"][0]["epss"])
    except Exception:
        pass
    return 0.0

# Example: Prioritize a list of vulnerabilities
vulns = [
    VulnerabilityPriority("CVE-2026-1234", 9.8, get_epss_score("CVE-2026-1234"), True, True, True),
    VulnerabilityPriority("CVE-2026-5678", 7.5, get_epss_score("CVE-2026-5678"), False, False, False),
    VulnerabilityPriority("CVE-2026-9012", 6.5, get_epss_score("CVE-2026-9012"), False, True, True),
]

vulns.sort(key=lambda v: v.priority_score, reverse=True)
for v in vulns:
    print(f"{v.cve}: {v.priority_label} (score: {v.priority_score})")

Preguntas frecuentes

Que se debe considerar critico mas alla de CVSS 9.0?
Un hallazgo critico debe considerar tambien la explotabilidad, la exposicion a internet, la sensibilidad de los datos y si existe un exploit publico. Una vulnerabilidad CVSS 7 en un servicio publico...
Como manejamos hallazgos que no pueden parchearse?
Documenta un control compensatorio, acepta el riesgo con fecha de vencimiento y monitorea cambios. Ejemplos incluyen reglas de WAF, aislamiento de red o monitoreo adicional.
Quien debe recibir este informe?
Equipo de seguridad, gerentes de ingenieria, CISO, oficial de cumplimiento y duenos de activos. El resumen ejecutivo es util para la direccion; las tablas detalladas son para equipos de remediacion.