When Wazuh detects vulnerabilities on your Windows systems and WSUS has hundreds of pending updates, the key question is: what do I patch first? This post connects the Wazuh → WSUS flow to prioritize and deploy critical patches in a controlled way.
Following Monitoring Active Directory and Office365 with Wazuh and WSUS: how to repair the SUSDB database, here is the next step: what to approve when Wazuh triggers CVE alerts.
Context: more CVEs, more need to prioritize
More vulnerabilities are published every day — from researchers, bug bounty, or AI-assisted tools — and WSUS piles up dozens of unapproved updates. Approving everything at once is risky; leaving everything for “the monthly cycle” does not work when there is a critical CVE with active exploitation (as in Wazuh 4.9.1 and Mirai).
The idea: use Wazuh to see which systems are affected and WSUS to deploy only what is needed, in the right order.
How Wazuh detects vulnerabilities
Wazuh’s Vulnerability Detector module scans the installed software inventory and matches it against CVE databases (NVD, OVAL, etc.). On Windows agents, the agent sends installed package information (including Windows updates) and the manager generates alerts when there is a known CVE for that version.
In the Wazuh dashboard you can see:
- Which hosts have vulnerabilities
- Severity (Critical, High, Medium, Low)
- CVE ID and description
- Affected package/component name
If you don’t have Vulnerability Detector enabled yet, in the manager’s
ossec.conf:<vulnerability-detector> <enabled>yes</enabled> <provider name="msu"> <enabled>yes</enabled> </provider> </vulnerability-detector>Then:
wazuh-control restart
Limitations and false positives: what the Vulnerability Detector does not validate
The Vulnerability Detector works from inventory: it matches installed software to CVE databases. It does not validate whether the component is actually exploitable in your setup (whether the service is running, whether the affected feature is in use, etc.). That can produce alerts that don’t justify urgent patching:
- Supersedence: An update may be replaced by a newer one. WSUS often marks “superseded”; approving the old revision does not fix the CVE if a newer KB already replaces it. Check in WSUS that the KB you approve is not superseded.
- Packages not really active: The inventory says “installed”, but the role or feature may be disabled. A CVE in a component you don’t run still appears; prioritize those that affect services you do use.
- Disabled features: Same as above: if the CVE affects a feature you don’t have enabled, the real risk is lower. Don’t stop patching, but don’t treat everything as critical just because of CVSS.
Wazuh uses NVD/MSU feeds in offline mode (it syncs local copies). Update those feeds regularly so newly published CVEs are in the Vulnerability Detector; otherwise alerts will be delayed.
Avoiding unnecessary patching reduces reboots, testing, and operational risk. Checking supersedence and component context before approving in pilot saves work and avoids approving the wrong patch.
Prioritization criteria
| Priority | Severity | Action |
|---|---|---|
| 1 | Critical (CVSS 9–10), RCE, active exploitation | Approve and deploy in immediate maintenance window |
| 2 | High (CVSS 7–8.9), no known exploitation | Approve in pilot group, then production in 24–48 h |
| 3 | Medium | Usual patch cycle (monthly or per policy) |
| 4 | Low | Include in next maintenance cycle |
Practical rule: exposed servers (Internet, DMZ) and DCs first; internal user workstations after.
Prioritize by real exposure, not just CVSS. A CVE with CVSS 7 on a domain controller or a server exposed to the Internet is usually more urgent than a CVSS 9 on an internal workstation that doesn’t offer services. CVE severity matters, but asset context (what it does, who can reach it) should raise or lower priority. In practice: same severity → first those that are exposed or critical for the domain (DCs, identity servers, perimeter).
Practical flow: from Wazuh to WSUS
Step 1 — Identify the CVE and the Microsoft KB
When Wazuh alerts on a CVE on Windows, you need the Microsoft Knowledge Base (KB). Search in:
- Microsoft Security Response Center
- NVD → the CVE page often has the link to Microsoft’s advisory
Example: CVE-2025-XXXX → KB5041234.
Step 2 — Find the update in WSUS
On the WSUS server, with PowerShell:
# Connect to WSUS server
[reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | Out-Null
$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer("localhost", $false, 8530)
# Search by Knowledge Base ID
$kbId = "5041234"
$updates = $wsus.GetUpdates() | Where-Object { $_.Title -match "KB$kbId" }
$updates | Select-Object Title, UpdateClassificationTitle, CreationDate, IsApproved | Format-Table -AutoSize
Step 3 — Create approval groups (pilot vs production)
Before mass approval, create a pilot group with a few machines:
# Create Piloto-Patches group if it doesn't exist
$computerGroups = $wsus.GetComputerTargetGroups()
$pilotoGroup = $computerGroups | Where-Object { $_.Name -eq "Piloto-Patches" }
if (-not $pilotoGroup) {
$pilotoGroup = $computerGroups.Add("Piloto-Patches")
}
Assign 2–3 test machines to that group from the WSUS console (or via GPO).
Step 4 — Approve for pilot group first
# Get the update by KB
$update = $wsus.GetUpdates() | Where-Object { $_.Title -match "KB5041234" } | Select-Object -First 1
if ($update) {
# Approve for installation in Piloto-Patches
$update.Approve("Install", $pilotoGroup)
Write-Host "Update approved for Piloto-Patches group"
} else {
Write-Host "Update not found. Verify WSUS has synced with Microsoft."
}
Step 5 — Wait and validate
- Check in Wazuh that pilot group machines no longer appear as vulnerable after the patch.
- Check Event Viewer (Windows Update) on one of the pilots.
- If all is well, approve for All Computers or the production group.
# Once validated in pilot, approve for all
$allComputers = $computerGroups | Where-Object { $_.Name -eq "All Computers" }
$update.Approve("Install", $allComputers)
Summary script to approve by KB
Save something like Approve-WsusUpdateByKB.ps1:
param(
[Parameter(Mandatory=$true)]
[string]$KBId,
[string]$TargetGroup = "Piloto-Patches",
[switch]$WhatIf
)
[reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | Out-Null
$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer("localhost", $false, 8530)
$update = $wsus.GetUpdates() | Where-Object { $_.Title -match "KB$KBId" } | Select-Object -First 1
$group = $wsus.GetComputerTargetGroups() | Where-Object { $_.Name -eq $TargetGroup }
if ($update -and $group) {
if ($WhatIf) {
Write-Host "[WhatIf] Would approve KB$KBId for $TargetGroup (no changes made)"
} else {
$update.Approve("Install", $group)
Write-Host "KB$KBId approved for $TargetGroup"
}
} else {
Write-Host "Error: update or group not found"
}
Usage:
.\Approve-WsusUpdateByKB.ps1 -KBId "5041234" -TargetGroup "Piloto-Patches"
# Dry-run (no approval, just shows what would happen):
.\Approve-WsusUpdateByKB.ps1 -KBId "5041234" -TargetGroup "Piloto-Patches" -WhatIf
Rollback and controlled deployment
WSUS can break things (incompatibilities, unexpected reboots, drivers). A controlled deployment means having a rollback strategy and clear limits:
- Uninstall approval: If a patch causes issues in pilot, in WSUS you can approve the update as Uninstall for the affected group. Important: Uninstall only applies if the patch is already installed on the machines; if it hasn’t been deployed yet, Decline the update for that group and it won’t be installed. Knowing when to use Uninstall vs Decline avoids getting stuck with a bad patch in production.
- Deadline without aggressive auto-reboot: When approving, you can set a deadline (cutoff date/time for installation). Avoiding very short deadlines reduces reboots during work hours. Combine with maintenance windows in GPO so the reboot happens when planned.
- Deployment rings: You already use pilot → production. In large environments an extra ring helps: e.g. Pilot (2–3 machines) → Non-critical servers (or a specific OU) → Rest. That limits impact if a patch fails and gives you time to unapprove before it reaches the whole estate.
You don’t need to implement everything on day one; with pilot + ability to unapprove/Uninstall and reasonable deadlines you already gain a lot. If something goes wrong in pilot, don’t promote to production and recheck supersedence or the correct KB.
Maintenance and references
- Schedule Invoke-WsusServerCleanup periodically (as in the SUSDB repair post) so WSUS doesn’t fill up with obsolete revisions.
- Keep products and classifications aligned with what you actually use.
- If Wazuh triggers many new alerts at once — e.g. after hundreds of CVEs in open source libraries are announced — always prioritize those affecting Windows and network components on your servers.
Useful links
- Wazuh Vulnerability Detector
- Microsoft Security Update Guide
- WSUS PowerShell (Microsoft.UpdateServices)
Conclusion
With Wazuh you identify which systems are vulnerable and with WSUS you control which patches are deployed and in what order. Prioritizing by severity and real exposure (DCs and exposed servers first), checking supersedence and false positives from inventory, using a pilot group and mapping CVE → KB avoids deploying blindly. Adding rollback (unapprove or Uninstall), reasonable deadlines, and deployment rings reduces impact when a patch breaks something — which in WSUS happens more often than we’d like.
