This is a surface-area lab, not PrintNightmare. Target is a Windows Server VM with the Print Spooler running, then stopped. Goal: dump who can start/stop the service, which RPC endpoints spoolsv.exe listens on, and the Point-and-Print registry values. I do not load a driver, I do not call RpcAddPrinterDriverEx, I do not drop a DLL into C:\Windows\System32\spool.
1Figure 1. SYSTEM plus user-influenced paths is the class. Hardening is disable, ACL, Point-and-Print.
2user/RPC -> spoolsv (LocalSystem) -> drivers / spoolss pipe
3stop service => pipe gone; start=disabled on DC
Lab layout
1labs/spool_lab/
2 sc-qc.txt
3 sdshow.txt
4 rpc-endpoints.txt
5 pnp.reg.txt
Service identity:
1C:\lab> sc qc Spooler
2[SC] QueryServiceConfig SUCCESS
3SERVICE_NAME: Spooler
4 TYPE : 110 WIN32_OWN_PROCESS (interactive)
5 START_TYPE : 2 AUTO_START
6 ERROR_CONTROL : 1 NORMAL
7 BINARY_PATH_NAME : C:\Windows\System32\spoolsv.exe
8 LOAD_ORDER_GROUP : SpoolerGroup
9 TAG : 0
10 DISPLAY_NAME : Print Spooler
11 DEPENDENCIES : RPCSS
12 : http
13 SERVICE_START_NAME : LocalSystem
SERVICE_START_NAME : LocalSystem is the whole threat model in one line. Anything this service does with a user-supplied path is LPE-shaped until proven otherwise.
Artifact: service ACL
1C:\lab> sc sdshow Spooler
2D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)
3 (A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)
4 (A;;CCLCSWLOCRRC;;;IU)
5 (A;;CCLCSWLOCRRC;;;SU)
6 (A;;CR;;;AU)
7 (A;;CCLCSWRPWPDTLOCRRC;;;PU)
8S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)
Decode I actually write (SDDL cheat, not a full parser):
| ACE | SID | rights that matter |
|---|---|---|
| SY | Local System | full |
| BA | Built-in Administrators | CC DC … WD WO (change config, start/stop) |
| IU | Interactive Users | CC LC SW LO CR RC (query / interact, not WP = start in this dump — confirm per build) |
| SU | Service logon | similar to IU |
| AU | Authenticated Users | CR (SERVICE_USER_DEFINED_CONTROL) |
| PU | Power Users | includes RP WP DT (start/stop/pause) — legacy, finding if present |
I care whether Authenticated Users or Everyone has RP (start), WP (stop), DC (change config), WD (change DACL), WO (change owner). In this dump AU has CR only. PU having start/stop is a leftover I remove.
1C:\lab> sc stop Spooler
2SERVICE_NAME: Spooler
3 STATE : 3 STOP_PENDING
4# as labuser (not admin):
5[SC] OpenService FAILED 5: Access is denied.
Access denied on stop as a standard user is the control working. Event:
1# System 7036 (service state)
2The Print Spooler service entered the stopped state.
3
4# Microsoft-Windows-PrintService/Admin 808 (lab, after a failed client print)
5The print job was rejected. Win32 error: 5. User: LAB\labuser
6Printer: [REDACTED]
RPC endpoints, conceptual dump
I use rpcinfo-style listing via Sysinternals TcpView / netstat and a read-only rpcdump against 127.0.0.1. I do not call the print APIs.
1C:\lab> netstat -ano | findstr spoolsv
2 TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 880
3 # 135 is RPCSS, not spoolsv; spoolsv registers with RPCSS
4
5C:\lab> tasklist /FI "IMAGENAME eq spoolsv.exe"
6Image Name PID
7spoolsv.exe 880
8
9# rpcdump.py 127.0.0.1 (impacket, information only — no bind to spooler ops)
10# excerpt, UUIDs public:
1112345678-1234-abcd-ef00-0123456789ab v1.0 \\PIPE\\spoolss # MS-RPRN
12ae33069b-a2a8-46ee-a235-ddfd339be281 v1.0 \\PIPE\\spoolss # MS-PAR
Named pipe:
1C:\lab> dir \\.\pipe\spoolss
2 Directory of \\.\pipe\
3spoolss
That pipe existing is expected while the service runs. After sc stop (admin session):
1C:\lab> dir \\.\pipe\spoolss
2The system cannot find the file specified.
Pipe gone, RPC UUIDs gone. Domain coercion class needs this pipe on a remote host. Local LPE class needs the service and a driver/path primitive. Both shrink when Spooler is disabled on DCs and on servers that do not print.
Point-and-Print registry (the policy artifact)
1C:\lab> reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint"
2 NoWarningNoElevationOnInstall REG_DWORD 0x0
3 UpdatePromptSettings REG_DWORD 0x0
4 RestrictDriverInstallationToAdministrators REG_DWORD 0x1
5
6C:\lab> reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PackagePointAndPrint"
7 PackagePointAndPrintServerList (not set)
RestrictDriverInstallationToAdministrators=1 is the post-Nightmare default I want. NoWarningNoElevationOnInstall=1 is a finding: it is the “install a driver from a print server without a prompt” policy that turned a remote share into SYSTEM code load.
I do not set those values to the insecure side to “demonstrate”. I only dump.
Analysis steps
- Does this host need to print? DCs: no. The lab DC gets
sc config Spooler start= disabled. - Service ACL: AU/WD/WO/DC present? Remove.
- Point-and-Print: restrict to admins; do not package-point-and-print from untrusted servers.
- RPC: spoolss pipe listening on a server that is not a print server → disable.
- Patch state: I record
wmic qfe/Get-HotFixfor the Nightmare-era KBs as history; I do not treat “KB installed” as sufficient without the registry above.
1C:\lab> sc config Spooler start= disabled
2[SC] ChangeServiceConfig SUCCESS
3C:\lab> sc qc Spooler | findstr START_TYPE
4 START_TYPE : 4 DISABLED
Sanitized reproduction (denied / crash only)
Standard user adding a printer driver (UI):
1# Settings → Printers → Add driver
2"You do not have permission to install drivers." [REDACTED]
3# no file written under C:\Windows\System32\spool\drivers
Crash analog — I do not fuzz spoolsv. I keep a user-mode toy that copies a path with _snprintf the way old sample code did:
1/* pathcopy.c — lab, not spoolsv */
2#include <stdio.h>
3int main(int argc, char **argv) {
4 char dest[32];
5 _snprintf(dest, sizeof(dest), "%s", argv[1]); /* no NUL guarantee on old CRT */
6 puts(dest);
7}
1> cl /fsanitize=address pathcopy.c
2> pathcopy.exe AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
3AddressSanitizer: stack-buffer-overflow on address 0x[REDACTED]
4WRITE of size 41
5 #0 _snprintf
6 #1 main pathcopy.c:5
That ASAN line is my crash dump. It is not a spooler 0-day.
Failed-auth: remote \\lab-print\spoolss with a bad account (from the member, not a relay):
1System error 1326.
2Event 4625 Logon Type 3 Account labuser Package NTLM
3 Source Network Address: 10.[REDACTED]
4# spoolss never saw a job
Point-and-Print server list, if we must print
When the host is a client that prints to a known server, I set the server list instead of “any print server”:
1C:\lab> reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint" /v Restricted
2# PackagePointAndPrintServerList / RestrictDriverInstallationToAdministrators already dumped
3# Approved server (lab):
4C:\lab> reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint\ListOfServers"
5 lab-print.lab.internal
A list of one FQDN we own is acceptable. A GPO that re-enables NoWarningNoElevationOnInstall “so finance can add a printer” is how the class returned after the emergency KB. I reject that GPO in review.
Mitigation
- DCs and non-print servers: Spooler disabled.
- Print servers: latest CU,
RestrictDriverInstallationToAdministrators=1, Point-and-Print server list = the servers you own. - ACL: drop PU/AU write rights on the service.
- Block inbound RPC to spoolss at the host firewall if the box must run Spooler but must not be a remote print server.
- Monitor 808 / 372 (PrintService) and unexpected writes under
%SystemRoot%\System32\spool\drivers.
1# firewall (lab)
2netsh advfirewall firewall add rule name="lab-block-spoolss" dir=in
3 action=block protocol=tcp localport=135 enable=yes
4# plus named-pipe restrictions via SMB; do not confuse this with "RPC off globally"
Port 135 is shared. Prefer disabling the service over blocking RPCSS.
What I file after this lab
spoolsv.exeLocalSystem, AUTO_START, depends RPCSS- SDDL: AU=CR only; PU has start/stop (finding); labuser stop → error 5
- Pipe
\\.\pipe\spoolsspresent while running, absent when stopped - RPC UUIDs MS-RPRN / MS-PAR registered (rpcdump, no call)
- PnP:
RestrictDriverInstallationToAdministrators=1,NoWarningNoElevationOnInstall=0 - After change: START_TYPE DISABLED on the lab DC
- Out of scope: AddPrinterDriver, DLL drop, Nightmare PoC
Commands appendix
1sc qc Spooler
2sc sdshow Spooler
3reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers\PointAndPrint"
4netstat -ano | findstr 880
5sc config Spooler start= disabled