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):

ACESIDrights that matter
SYLocal Systemfull
BABuilt-in AdministratorsCC DC … WD WO (change config, start/stop)
IUInteractive UsersCC LC SW LO CR RC (query / interact, not WP = start in this dump — confirm per build)
SUService logonsimilar to IU
AUAuthenticated UsersCR (SERVICE_USER_DEFINED_CONTROL)
PUPower Usersincludes 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

  1. Does this host need to print? DCs: no. The lab DC gets sc config Spooler start= disabled.
  2. Service ACL: AU/WD/WO/DC present? Remove.
  3. Point-and-Print: restrict to admins; do not package-point-and-print from untrusted servers.
  4. RPC: spoolss pipe listening on a server that is not a print server → disable.
  5. Patch state: I record wmic qfe / Get-HotFix for 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.exe LocalSystem, AUTO_START, depends RPCSS
  • SDDL: AU=CR only; PU has start/stop (finding); labuser stop → error 5
  • Pipe \\.\pipe\spoolss present 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