Git Product home page Git Product logo

callbackhell's Introduction

CallbackHell

Exploit for CVE-2021-40449 (Win32k - LPE)

Description

CVE-2021-40449 is a use-after-free in Win32k that allows for local privilege escalation.

The vulnerability was found in the wild by Kaspersky.

The discovered exploit was written to support the following Windows products:

  • Microsoft Windows Vista
  • Microsoft Windows 7
  • Microsoft Windows 8
  • Microsoft Windows 8.1
  • Microsoft Windows Server 2008
  • Microsoft Windows Server 2008 R2
  • Microsoft Windows Server 2012
  • Microsoft Windows Server 2012 R2
  • Microsoft Windows 10 (build 14393)
  • Microsoft Windows Server 2016 (build 14393)
  • Microsoft Windows 10 (build 17763)
  • Microsoft Windows Server 2019 (build 17763)

However, this exploit is current only tested on the following versions:

  • Microsoft Windows 10 (build 14393)
  • Microsoft Windows 10 (build 17763)

Technical Writeup

I highly recommend reading Kaspersky's technical writeup before proceeding.

As mentioned in the technical writeup by Kasperky, the vulnerability exists in GreResetDCInternal. If an attacker hooks the user-mode callback DrvEnablePDEV, which is called during hdcOpenDCW, it is possible to destroy the original device context by calling ResetDC, which causes a use-after-free in the kernel when the user-mode callback returns.

The following pseudo-code is made partially from the leaked Windows XP source code and by reverse-engineering the latest (before the patch) GreResetDCInternal from Win32kfull.sys. The irrelevant parts have been removed with [...]. Look for the VULN: comments.

BOOL GreResetDCInternal(
    HDC hdc,
    DEVMODEW *pdmw,
    BOOL *pbBanding,
    DRIVER_INFO_2W *pDriverInfo2,
    PVOID ppUMdhpdev)
{
    // [...]
    HDC hdcNew;

    {
        // Create DCOBJ from HDC
        DCOBJ dco(hdc);

        if (!dco.bValid())
        {
            SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
        }
        else
        {
            // Create DEVOBJ from `dco`
            PDEVOBJ po(dco.hdev());

            // [...]

            // Create the new DC
            // VULN: Can result in a usermode callback that destroys old DC, which
            // invalidates `dco` and `po`
            hdcNew = hdcOpenDCW(L"",
                                pdmw,
                                DCTYPE_DIRECT,
                                po.hSpooler,
                                prton,
                                pDriverInfo2,
                                ppUMdhpdev);

            if (hdcNew)
            {
                po->hSpooler = NULL;

                DCOBJ dcoNew(hdcNew);

                if (!dcoNew.bValid())
                {
                    SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
                }
                else
                {
                    // Transfer any remote fonts

                    dcoNew->pPFFList = dco->pPFFList;
                    dco->pPFFList = NULL;

                    // Transfer any color transform

                    dcoNew->pCXFList = dco->pCXFList;
                    dco->pCXFList = NULL;

                    PDEVOBJ poNew((HDEV)dcoNew.pdc->ppdev());

                    // Let the driver know
                    // VULN: Method is taken from old (possibly destroyed) `po`
                    PFN_DrvResetPDEV rfn = po->ppfn[INDEX_DrvResetPDEV];

                    if (rfn != NULL)
                    {
                        (*rfn)(po->dhpdev, poNew->dhpdev);
                    }

                    // [...]
                }
            }
        }
    }

    // Destroy old DC
    // [...]
}

As can be seen from the pseudo-code, the old device context can be freed in a user-mode callback from the hdcOpenDCW call, and later on, the method DrvResetPDEV is retrieved from the old device context and called with (po->dhpdev, poNew->dhpdev).

To create and hook a device context, one can do the following:

  • Find an available printer with EnumPrinters
  • Load the printer driver into memory with OpenPrinter, GetPrinterDriver and LoadLibraryExA
  • Get the printer driver's user-mode callback table with GetProcAddress and DrvEnableDriver
  • Unprotect the printer driver's user-mode callback table with VirtualProtect
  • Overwrite the printer driver's desired user-mode callback table entries
  • Create a device context for the printer with CreateDC(NULL, printerName, NULL, NULL)

We should now have a device context for a printer with hooked user-mode callbacks.

We're interested in only one hook, namely DrvEnablePDEV. This hook is interesting in two aspects: triggering the UAF and controlling the arguments, as described earlier. To trigger the UAF vulnerability, we will call ResetDC inside of the hook, which will destroy the old device context. When we return from the hook, we will still be inside the first GreResetDCInternal, which will shortly after get and call the function pointer for DrvResetPDEV from our old and destroyed device context with the two arguments that got returned from DrvEnablePDEV; the old and the new DHPDEV.

If your process is running with a medium integrity level, KASLR should not be an issue with the help of EnumDeviceDrivers and NtQuerySystemInformation.

Kaspersky mentions that the original exploit used GDI palette objects and a single kernel function call to achieve arbitrary memory read/write. This exploit uses a technique to allocate a BitMapHeader on the big pool and RtlSetAllBits to enable all privileges on our current process token. The BitMapHeader will point to our current process token's _SEP_TOKEN_PRIVILEGES. By calling RtlSetAllBits(BitMapHeader), it's possible to enable all privileges for our current process token with a single kernel function call. From here, one can abuse the new privileges to get SYSTEM. This exploit uses SeDebugPrivilege to inject shellcode into the winlogon.exe process.

PoC

./poc.png

References

callbackhell's People

Contributors

ly4k avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

callbackhell's Issues

Code explanation

Can someone please explain how this code exactly works or share with me a write-up for the exploit if available. I tried to search everywhere but i couldn't find a detailed explanation for the exploit.

Failed to allocate remote memory Windows 2019 17763

Hi,
I saw your project the other day and I decided to give it a try against a Windows Server 2019 (Build 17763) that I have set up.
Every time I ran the exploit I got a [-] Failed to allocate remote memory[-] Failed to write to remote memory error and I was wondering was is it that makes it not work.

The problem arises here:
LPVOID buffer = VirtualAllocEx(proc, NULL, sizeof(payload), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

because the buffer is left blank therefore I get the error each time, but what could cause the allocation to fail?

I'm trying to debug the problem, but I don't seem to be able to reach its solution.

Any hints?
Thanks so much.

Exploit does not elevate privileges on Windows build 19042.746

Hi,

thank you for your work!

The exploit does not elevate privileges on Windows build 19042.746
I tried to inject into explorer.exe for the last step and that does work for spawning the cmd, also all the steps before the injection do work. Injecting into winlogon.exe did not work as the privileges are not sufficient I guess.
But once the cmd got spawned it has the context of the executing user and not system.

What could be done to debug this or add functionality for Windows build 19042.746 ?

Many thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.