← All posts

Auditing Windows Optional Features: The Bloat That Widens Your Attack Surface

RSAT, TFTP, SMBv1, and the Windows optional components sitting enabled on your endpoints for no reason - how to inventory them and shut the unneeded ones down.

Every Windows install ships with a menu of optional components — some enabled by default, some switched on years ago by an admin who needed them once. Each one is code on disk, and a surprising number expose network listeners, script interpreters, or legacy protocols. Attack surface reduction usually gets framed as "harden what you run." The cheaper, more durable win is stop running what you don't need at all. This is a walkthrough of how to inventory Windows optional features and disable the ones that only exist to widen your attack surface.

Two different feature stores, and why both matter

Windows tracks add-on components in two overlapping places, and you have to check both:

A machine can look clean in one view while carrying real risk in the other. SMBv1 and the PowerShell 2.0 engine live in the DISM list, not the Capabilities list — miss it and you've audited half the surface.

Inventory first

Before you disable anything, get a complete picture across both stores. On a single machine:

# DISM-style Windows features that are enabled
Get-WindowsOptionalFeature -Online |
  Where-Object State -eq 'Enabled' |
  Sort-Object FeatureName |
  Select-Object FeatureName

# Installed capabilities (RSAT, OpenSSH, legacy media, etc.)
Get-WindowsCapability -Online |
  Where-Object State -eq 'Installed' |
  Select-Object Name

Export this to a baseline. The goal isn't to nuke everything that's enabled — it's to explain every enabled component. Anything you can't justify is a candidate for removal.

The usual offenders

These are the components that repeatedly turn up enabled with no operational reason:

Disabling safely

Remove the two highest-value offenders and reboot to confirm nothing you depend on breaks:

# Kill the PowerShell 2.0 downgrade path
Disable-WindowsOptionalFeature -Online `
  -FeatureName MicrosoftWindowsPowerShellV2Root -NoRestart

# Remove SMBv1 entirely
Disable-WindowsOptionalFeature -Online `
  -FeatureName SMB1Protocol -NoRestart

# Drop a capability you can't justify (example: TFTP)
Get-WindowsCapability -Online |
  Where-Object Name -like '*TFTP*' |
  Remove-WindowsCapability -Online
Always stage this. Disabling .NET 3.5 or a virtualization feature can break a line-of-business app. The point of the inventory step is to make removal a deliberate, reversible decision — not a blind sweep.

Why this drifts back

The hard part isn't the one-time cleanup — it's that optional features creep back. A developer enables WSL. A troubleshooting session turns on the Telnet client. A reimaged laptop ships with an older default set. Point-in-time hardening decays, and "we disabled SMBv1 last year" is not the same as "SMBv1 is disabled right now, on all 60 machines."

Where WinSentinel fits

On a single machine, WinSentinel is free and unrestricted — all 33 audit modules, no limits. It enumerates both the Capabilities and DISM feature stores, flags risky components like SMBv1, the PowerShell 2.0 engine, TFTP/Telnet clients, and stray inbound listeners, and folds them into your security score with concrete remediation steps. You get the full inventory-and-justify workflow above without hand-writing the queries.

For organizations, WinSentinel Pro adds fleet orchestration on top: a central node that rolls up optional-feature posture across every endpoint, alerts when a machine re-enables a component you'd disabled (drift detection), and maps the findings into compliance rollups so an auditor sees "SMBv1 disabled fleet-wide" rather than a spreadsheet of hopes. The audit itself is the same everywhere — Pro is about seeing and enforcing it across many machines at once.

The takeaway

Optional features are the cheapest attack surface to eliminate because you're not hardening a control — you're removing code that had no reason to run. Inventory both feature stores, justify every enabled component, disable the rest, and then keep watching, because the list will try to grow back. A component that isn't installed can't be exploited, downgraded to, or relayed through.