This page looks best with JavaScript enabled

CVE-2020-1337: my two cents

 ·  ✍️ neofito

In January of this year, Peleg Hadar (@peleghd) and Tomer Bar reported to Microsoft, without publicly releasing any details, a Windows Print Spooler vulnerability that could allow privilege elevation from Windows 7 onwards. The security updates intended for that vulnerability (CVE-2020-1048) were released by Microsoft on May 12.

On the same day, Alex Ionescu (@aionescu) and Yarden Shafir (@yarden_shafir), who collided discovering the vulnerability, published a very detailed blog post about it: PrintDemon: Print Spooler Privilege Escalation, Persistence & Stealth (CVE-2020-1048 & more). Using this knowledge and after some tests, I was able to find a bypass for the security updates released by Microsoft mainly via junction points and symlinks (kudos to James Forshaw @tiraniddo).

My report, sent on May 19, was marked by Microsoft as duplicated, colliding with other researchers. The vulnerability was catalogued as CVE-2020-1337, and shared with these persons.

In this post I’ll try to summarize the CVE-2020-1048 vulnerability exploitation process and the subsequent bypass found by me, plus some detailed references to read.


As I explained above, on May 12 of this same year Microsoft released the CVE-2020-1048 security updates:

An elevation of privilege vulnerability exists when the Windows Print Spooler service improperly allows arbitrary writing to the file system. An attacker who successfully exploited this vulnerability could run arbitrary code with elevated system privileges. An attacker could then install programs; view, change, or delete data; or create new accounts with full user rights.

To exploit this vulnerability, an attacker would have to log on to an affected system and run a specially crafted script or application.

The update addresses the vulnerability by correcting how the Windows Print Spooler Component writes to the file system.

Yarden Shafir and Alex Ionescu published his blogpost PrintDemon: Print Spooler Privilege Escalation, Persistence & Stealth (CVE-2020-1048 & more) giving detailed clues about the vulnerability and its exploitation without releasing a functional PoC, or at least not “as is”.

The exploitation process requires to create a specially crafted local printer port and a virtual printer with a generic driver. The printer port has to point to a file in a restricted path, i.e. C:\Windows\system32\evil.dll, because during its creation Windows doesn’t check the user privileges to write in this path. If a standard user launches a print job later through this virtual printer, an error will be prompted because the user is not allowed to write in that path; the printing job is executed via impersoation and that is the normal behavior, isn’t it?

Note that to point the local pointer to the C:\Windows\system32 path and later be able to create the virtual printer using this port with a generic driver it has to be done via PowerShell or programmatically; using the UI printer wizard will cause an Access denied error to be returned because the system will check the privileges before even writing the file.

Access denied while creating a local printer port

The blog post, that I encourage all of you to read, deepens in the process done by the Windows print spooler and its print jobs. When a print job is launched two different kind of files are created in the C:\Windows\system32\spool\PRINTERS directory:

  • FPnnnnn.SPL or nnnnn.SPL with the content to be printed.
  • FPnnnnn.SHD or nnnnn.SHD with all the metadata related to the print job.

I’m not describing the SHD file format because this knowledge is not an essential requirement to understand the exploitation process (see this and this). As short, when the spooler service is started it searchs for shadow files (.SHD) on his default directory (C:\Windows\system32\spool\PRINTERS habitually) checking its content and launching the printing of the associated spool file (.SPL) doing this as SYSTEM without impersonating the owner user. Now, puzzling all the details it is posible to build a functional PoC.

Using Visual Studio to open the original solution, we will first modify the printserver\pserver.c file as described next:

    LPWSTR g_PortName = L"c:\\windows\\system32\\evil.dll";

We will also modify the hPrinter and hMonitor variable declaration:

    HANDLE hPrinter = NULL;
    HANDLE hMonitor = NULL;

Next we include the strsafe.h header and the next code fragment from this post at stackoverflow, that will be in charge for storing the malicious DLL content in memory:

    FILE* f;
    wchar_t wDirPath[MAX_PATH];
    DWORD dRet;

    dRet = GetCurrentDirectory(MAX_PATH, wDirPath);
    if (dRet == 0)
        printf("GetCurrentDirectory failed (%d)\n", GetLastError());
        goto CleanupPath;
    wcscat_s(wDirPath, sizeof(wDirPath), L"\\evil.dll");

    _wfopen_s(&f, wDirPath, L"rb");
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    fseek(f, 0, SEEK_SET);

    char* string = malloc(fsize + 1);
    fread(string, 1, fsize, f);

    printf("[.] Reading evil.dll: %d bytes\n", fsize);

Note: the Windows Driver Kit must be previously installed in order to compile the project because the ntdllp.lib has to be found.

Finally, as a standard user in a Windows 10 system without the CVE-2020-1048 security update we will launch the printserver.exe with our evil.dll in the same path:

Running the printserver binary

The execution will be stopped waiting for our interaction; without restarting the execution we can check that a new printer has been added, PrintDemon, with a print job paused:

PrintDemon with a print job paused

To reboot the spooler service as a standard user we have to reboot the system. Once the system has started we’ll restart the print job queue:

Print job queue restart

As a result our malicious file has been planted on C:\Windows\system32:

Malicious file on system32

We can use the malicious DLL also released by Yarden Shafir and Alex Ionescu with Faxing Your Way to SYSTEM — Part Two, published April 30, to accomplish a DLL hijacking attack in order to escalate privileges to system. That process will not be discussed in this article but I think it is slightly described by me on twitter.

A few days after the release of PrintDemon, more precisely May 15, BC Security (@BCSecurity1), actual maintainer of the Empire framework, release a PowerShell exploit. Probably my fault but I have to say that the exploit is not functional without a spooler service reboot.

On the other hand, on the past day August 5 Peleg Hadar (@peleghd) and Tomer Bar released all the details about the CVE-2020-1048 on both talks on Black Hat and DEF CON (video, slides y whitepaper). The exploit and additional material could be found on github.

My bypass for CVE-2020-1048

After the PrintDemon device deletion and the corresponding update installation we will initiate anew the virtual printer creation process using the next PowerShell command:

Virtual printer creation error

As showed above we will get an access denied error trying to create the local printer port. The security update added some checks for the correct write privileges on the specified path, blocking the process at this point; but wait, are you sure the process is completely blocked? We will need to find a trick to bypass this check…

First we will install a generic printer driver; we will also create a new directory that we will later use as the print job destination:

Generic driver and directory creation

After that we will add a local printer port pointing to a file on this directory and we will confirm that everything went well:

Local printer port creation

The process will end correctly because our standard user has write privileges on the specified directory. Finally we will add the virtual printer device using the local printer port and the generic driver:

Virtual printer device creation

At this moment we will pause the virtual printer:

Pausando la impresora virtual

Now we will send a test printer job that will be paused on the printer queue:

Virtual printer queue on pause

Using our standard user we will reboot the system. Now, what could we do to print our file content on a privileged path? The answer is using the symboliclink-testing-tools released by James Forshaw.

Without going into specifics (more details) we will generate a junction point for the directory pointed by our local printer port, C:\Users\me\test, and later we will use a symbolic link to point C:\Users\me\test\foobar to C:\windows\system32\evil.dll:

Creating the symlinks

If we restart the paused printer queue we will accomplish the creation of the C:\windows\system32\evil.dll:

Malicious file creation

Once the bypass was confirmed I developed a C# PoC using NtApiDotNet by James Forshaw to programmatically create the required symlinks and IlMerge 3.0.40 plus MSBuild.ILMerge.Task to get the final binary. After making some questions and several tests I got a functional PoC.

The first execution requires to use as arguments the first step plus the path of our malicious file; after that we will need to reboot the system:

BinaryPlanting.exe first step

Once the system has started we will use as arguments of the binary the final step plus the name of the file that will be created on C:\Windows\system32:

BinaryPlanting.exe last step

As a result we will have planted our malicious DLL on C:\Windows\system32 allowing us to execute a DLL hijacking to elevate privileges on a vulnerable system.

Hive Minds