Background
The PrinterBug (also known as SpoolSample) is a coercion technique that has been part of the Active Directory attacker toolkit since 2018. It abuses the MS-RPRN (Print System Remote Protocol) to force a Domain Controller to authenticate outbound to an attacker-controlled machine using its own machine account credentials. The resulting Net-NTLMv2 hash can then be relayed or cracked to escalate privileges.
The mechanism is a single RPC call: RpcRemoteFindFirstPrinterChangeNotificationEx. This function instructs a remote Spooler service to deliver printer change notifications to a specified host. When called against a DC, the DC's machine account initiates an outbound NTLM-authenticated connection toward the attacker's listener — precisely the coercion primitive needed to relay or capture credentials.
What Microsoft Changed in Server 2025
With Windows Server 2025, Microsoft introduced a direct mitigation: remote access to \\pipe\spoolss is now blocked. This named pipe is the transport endpoint traditionally used by MS-RPRN. Tooling that binds to the Spooler over SMB — such as the classic printerbug.py — fails against Server 2025 Domain Controllers because the pipe is no longer remotely accessible.
This was a meaningful hardening step for the named pipe transport. The TCP transport, however, was left intact.
The Bypass: ncacn_ip_tcp
RPC in Windows supports multiple transport bindings. The Spooler service does not exclusively listen on \\pipe\spoolss — it also registers itself on a dynamic TCP port through the Endpoint Mapper (EPM, port 135). This registration is present and reachable on Server 2025.
The Endpoint Mapper is an RPC service that maps interface UUIDs to active transport endpoints. By querying port 135 for the MS-RPRN UUID (12345678-1234-ABCD-EF00-0123456789AB), an attacker can retrieve the dynamic TCP port the Spooler is listening on. Binding to MS-RPRN over ncacn_ip_tcp is then straightforward, and the named pipe restriction is entirely sidestepped.
The attack flow is as follows:
- Query EPM on port 135 of the target DC to resolve the MS-RPRN TCP endpoint
- Bind to MS-RPRN over TCP using valid domain credentials
- Call
RpcRemoteFindFirstPrinterChangeNotificationEx, specifying an attacker-controlled host as the notification target - The DC's machine account initiates an outbound NTLM-authenticated connection to the listener
- Capture the Net-NTLMv2 hash
Capturing the Hash via EPM Proxy
The PrinterBug callback targets port 135 (EPM) on the listener host. Rather than implementing a full RPC endpoint, the capture component runs an EPM proxy that intercepts the incoming request and rewrites the endpoint map response — redirecting the DC to a dedicated capture service on a secondary port. The NTLM handshake is then intercepted at that service.
To maximize compatibility, the NTLM challenge can be pre-seeded by probing the target DC's own Spooler at startup. This extracts the exact NTLM challenge structure the DC expects — including flags, target name, DNS domain, and target info attributes — so the fabricated challenge is indistinguishable from a legitimate one.
The captured hash takes the standard Net-NTLMv2 format:
MACHINE$::DOMAIN:<challenge>:<NTProofStr>:<blob>
This can be fed directly into Hashcat for offline cracking, or used as input for NTLM relay attacks.
Prerequisites
- The Print Spooler service must be running on the target DC (enabled by default on all Server versions including 2025)
- Valid domain credentials are required to authenticate the initial RPC bind
- The attacker must be able to reach port 135 and the dynamic Spooler port on the DC
- The DC must have network access to the attacker's listener on port 135
Impact
Capturing a Domain Controller machine account hash enables a range of high-impact post-exploitation paths:
- NTLM relay to LDAP/LDAPS: Modify AD objects, add users to privileged groups, configure RBCD
- Relay to AD CS (ESC8): If an HTTP-based certificate enrollment endpoint exists, relay the DC's authentication to obtain a machine certificate, then use PKINIT to retrieve the DC's TGT — effectively achieving domain compromise
- Relay to SMB: Execute code on systems where the machine account has administrative access
Offline cracking of machine account passwords is generally impractical (passwords are 120-character random strings), so relay is the primary exploitation path. Combined with a misconfigured AD CS environment or unconstrained delegation targets, this bypass provides a direct path to domain takeover.
Mitigation
Disable the Print Spooler on Domain Controllers. This is the only reliable fix. The Spooler service has no legitimate role on a DC, and as long as it runs, the TCP binding is exposed regardless of named pipe restrictions. Stop the service and set it to disabled.
If disabling Spooler is not immediately feasible, the focus should shift to limiting the blast radius of a successful coercion rather than trying to block the coercion itself:
- Enable EPA (Extended Protection for Authentication) on LDAP. This binds NTLM authentication to the underlying TLS channel, breaking cross-protocol relay from RPC to LDAP/LDAPS. Without EPA, a captured DC machine account hash can be relayed directly to LDAP for privilege escalation.
- Harden AD CS against ESC8. Disable HTTP-based enrollment endpoints where possible, or enforce EPA on IIS-hosted certificate authority web interfaces. This closes the most impactful relay path, which goes from Spooler coercion to a machine certificate to a DC TGT via PKINIT.
- Audit unconstrained delegation. Hosts configured with unconstrained delegation are high-value relay targets for DC machine account tickets. Restrict delegation to constrained or resource-based constrained delegation where possible.
The \\pipe\spoolss block in Server 2025 addressed one transport while leaving another open. Disabling the Spooler eliminates the primitive entirely; everything else is damage control.
The PoC is available at github.com/e1abrador/printerbug_tcp.