针对本次浏览器输入\\.\globalroot\device\condrv\kernelconnect导致主机拒绝服务的情况,查明原因是由于condrv驱动的疏忽,在IRP_MJ_CREATE中判断调用源并拒绝后没有对IRP进行正常的完成请求流程操作。导致调用者仍然获得到condrv的设备对象,随后关闭该对象的时候进入IRP_MJ_CLEANUP分发函数,尝试释放资源,但在创建的时候由于被拒绝所以没有分配任何资源,导致释放资源时访问空指针最后系统BSOD。
定位到IRP_MJ_CREATE分发函数CdpDispatchCreate
可以看到将我们访问的目录遍历查找CdpObjecetCreationTable表,类似的目录有很多,出现漏洞的目录为KernelConnect
由此看出KernelConnect对应的IRP_MJ_CREATE分发函数为CdCreateKernelConnection
Follow CdCreateKernelConnection~
发现有判断IRP的请求模式,如果请求来自应用层返回STATUS_ACCESS_DENIED,看似逻辑正确。
但是有没有发现漏了什么,,,
没错,漏了IoCompleteRequest来完成IRP并且没有设置IoStatus,返回到上方的CdpDispatchCreate也并没有进行完成IRP。就这样一个请求被传回了调用者,而IoStatus.Status字段没有填充,或许初始为0(STATUS_SUCCESS),这可能导致允许了这次请求。而触发这次漏洞的原因是由于调用者因此次请求而成功拿到了此文件对象,但是此时condrv没有对这个文件对象中的资源进行任何的初始化。
随后使用对象完毕后关闭该对象并且回收的时候出现了以下两行代码,FsContext没有进行初始化,是一个悬空指针随后悲剧上演。
Chrome访问\\.\globalroot\device\condrv\kernelconnect时的调用堆栈
触发此漏洞的R3函数为GetFileAttributeExW,R0函数为CdpDispatchCleanup
其实无论任何程序,尝试使用以下代码都可以造成BSOD
随后自写相同逻辑的驱动证明此问题确实会导致Cleanup的请求被发起
测试驱动代码:
#include <ntddk.h> VOID DriverUnload(PDRIVER_OBJECT DrvObj); VOID InitIrp(PDRIVER_OBJECT DrvObj); NTSTATUS InitMainDevObj(PDEVICE_OBJECT* OutDevObj, PDRIVER_OBJECT DrvObj, PUNICODE_STRING DevName); NTSTATUS IrpDisphDefault(PDEVICE_OBJECT DevObj, PIRP Irp); NTSTATUS IrpDisphCleanup(PDEVICE_OBJECT DevObj, PIRP Irp); NTSTATUS IrpDisphCreate(PDEVICE_OBJECT DevObj, PIRP Irp); NTSTATUS IrpDisphClose(PDEVICE_OBJECT DevObj, PIRP Irp); NTSTATUS DriverEntry(PDRIVER_OBJECT DrvObj, PUNICODE_STRING RegPath) { DECLARE_CONST_UNICODE_STRING(DevName, L"\\Device\\FileAttributesTest"); DrvObj->DriverUnload = DriverUnload; KdPrint(("函数 %ws 执行\n", __FUNCTIONW__)); InitIrp(DrvObj); InitMainDevObj(NULL, DrvObj, (PUNICODE_STRING)&DevName); return STATUS_SUCCESS; } VOID DriverUnload(PDRIVER_OBJECT DrvObj) { PDEVICE_OBJECT pDeviceNode = DrvObj->DeviceObject; PDEVICE_OBJECT pDeleteNode ; while (pDeviceNode) { pDeleteNode = pDeviceNode->NextDevice; IoDeleteDevice(pDeviceNode); pDeviceNode = pDeleteNode; } } NTSTATUS InitMainDevObj(PDEVICE_OBJECT* OutDevObj, PDRIVER_OBJECT DrvObj, PUNICODE_STRING DevName) { PDEVICE_OBJECT CreatedDevice; NTSTATUS Status = STATUS_UNSUCCESSFUL; Status = IoCreateDevice(DrvObj, 0, DevName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &CreatedDevice); if (!NT_SUCCESS(Status)) return Status; CreatedDevice->Flags |= DO_BUFFERED_IO; CreatedDevice->Flags &= ~DO_DEVICE_INITIALIZING; if(OutDevObj) *OutDevObj = CreatedDevice; return STATUS_SUCCESS; } VOID InitIrp(PDRIVER_OBJECT DrvObj) { for (DWORD32 dwMajorIndex = 0; dwMajorIndex <= IRP_MJ_MAXIMUM_FUNCTION; ++dwMajorIndex) DrvObj->MajorFunction[dwMajorIndex] = IrpDisphDefault; DrvObj->MajorFunction[IRP_MJ_CREATE] = IrpDisphCreate; DrvObj->MajorFunction[IRP_MJ_CLEANUP] = IrpDisphCleanup; DrvObj->MajorFunction[IRP_MJ_CLOSE] = IrpDisphClose; } NTSTATUS IrpDisphDefault(PDEVICE_OBJECT DevObj, PIRP Irp) { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS IrpDisphCleanup(PDEVICE_OBJECT DevObj, PIRP Irp) { KdPrint(("函数 %ws 执行\n", __FUNCTIONW__)); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; return STATUS_SUCCESS; } NTSTATUS IrpDisphCreate(PDEVICE_OBJECT DevObj, PIRP Irp) { if (Irp->RequestorMode) { KdPrint(("函数 %ws 执行\n", __FUNCTIONW__));//此处遗漏 //Irp->IoStatus.Information = 0; //Irp->IoStatus.Status = STATUS_ACCES·S_DENIED; return STATUS_ACCESS_DENIED; } Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; return STATUS_SUCCESS; } NTSTATUS IrpDisphClose(PDEVICE_OBJECT DevObj, PIRP Irp) { KdPrint(("函数 %ws 执行\n", __FUNCTIONW__)); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; return STATUS_SUCCESS; }
本文作者:玄道
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/151714.html