<# .SYNOPSIS ¿Qué software tiene CVE críticos en cada computadora y cuál actualizo primero? .DESCRIPTION Consulta las tablas de Microsoft Defender Vulnerability Management con búsqueda avanzada y lista, por dispositivo y software, los CVE de severidad crítica, cuántos tienen exploit público y qué actualización se recomienda. Pone primero lo que ya se puede explotar. Solo lectura: el POST a runHuntingQuery es una consulta. .PARAMETER RutaCsv Ruta del CSV de salida. Si se omite, solo se muestra en pantalla. .EXAMPLE .\get-vulnerabilidades-criticas.ps1 -RutaCsv .\vulnerabilidades.csv .NOTES Permisos: ThreatHunting.Read.All Rol mínimo: Lector de seguridad Licencia: Microsoft Defender for Endpoint Plan 2 o Microsoft Defender Vulnerability Management (complemento o independiente), o Microsoft Defender for Servers. Microsoft 365 Business Premium por sí solo no alcanza: Defender for Business no incluye búsqueda avanzada Licencia del código: MIT. Se entrega TAL CUAL, sin garantía. Pruébalo primero en un tenant de pruebas. #> [CmdletBinding()] param( [string]$RutaCsv ) $ErrorActionPreference = 'Stop' $permisos = @('ThreatHunting.Read.All') if (-not (Get-Command Connect-MgGraph -ErrorAction SilentlyContinue)) { throw 'Falta el módulo. Instálalo con: Install-Module Microsoft.Graph.Authentication -Scope CurrentUser' } Connect-MgGraph -Scopes $permisos -NoWelcome $consulta = @' DeviceTvmSoftwareVulnerabilities | where VulnerabilitySeverityLevel =~ "Critical" | join kind=leftouter (DeviceTvmSoftwareVulnerabilitiesKB | project CveId, CvssScore, IsExploitAvailable) on CveId | summarize CveCriticos = dcount(CveId), ConExploit = countif(IsExploitAvailable == 1), CvssMaximo = max(todouble(CvssScore)), ListaCve = make_set(CveId), Actualizaciones = make_set(RecommendedSecurityUpdate) by DeviceName, OSPlatform, SoftwareVendor, SoftwareName, SoftwareVersion '@ $respuesta = Invoke-MgGraphRequest -Method POST -Uri 'https://graph.microsoft.com/v1.0/security/runHuntingQuery' -Body (@{ Query = $consulta } | ConvertTo-Json) -ContentType 'application/json; charset=utf-8' if (@($respuesta.results).Count -ge 100000) { Write-Warning 'Se alcanzó el tope de 100,000 filas de la consulta: pueden faltar resultados. Reduce el periodo o filtra más.' } $filas = foreach ($r in $respuesta.results) { [pscustomobject]@{ Prioridad = if ([int]$r.ConExploit -gt 0) { 'Urgente: hay exploit público' } else { 'Alta' } Dispositivo = $r.DeviceName Sistema = $r.OSPlatform Fabricante = $r.SoftwareVendor Software = $r.SoftwareName Version = $r.SoftwareVersion CveCriticos = [int]$r.CveCriticos ConExploit = [int]$r.ConExploit CvssMaximo = $r.CvssMaximo ListaCve = @($r.ListaCve) -join ', ' Actualizaciones = @($r.Actualizaciones | Where-Object { $_ }) -join ', ' } } $filas = $filas | Sort-Object @{ Expression = 'ConExploit'; Descending = $true }, @{ Expression = 'CveCriticos'; Descending = $true }, Dispositivo $equipos = @($filas | Select-Object -ExpandProperty Dispositivo -Unique).Count $urgentes = @($filas | Where-Object ConExploit -GT 0).Count Write-Host "Software con CVE críticos: $(@($filas).Count) registros en $equipos dispositivos. Con exploit público: $urgentes" if ($RutaCsv) { # UTF-8 con BOM para que Excel en español muestre bien los acentos. $filas | Export-Csv -Path $RutaCsv -NoTypeInformation -Encoding utf8BOM Write-Host "CSV guardado en $RutaCsv" } else { $filas | Format-Table -AutoSize }