返回
顶部

references:

约定,本文所有的数字如果没有人特别说明,均为16进制

镜像

漏洞函数code path定位

afd.sys中的AfdNotifyRemoveIoCompletion函数的patch前后对比,左侧为patch过的,右侧为漏洞版本

image-20250626095117186

可以看到,patch过后的代码多了一个对a1的判断,这个a1其实是previousMode,如果是1(用户模式),则对要写入的内存执行ProbeForWrite,这个函数会检查目标地址是否是用户模式地址空间,也就是防止用户模式进程直接写入内核的内存

a1是previousMode很好判断,查看caller函数AfdNotifySock的代码可知a1其实就是AfdNotifySock函数的a3

image-20250626095802663

NTSTATUS ObReferenceObjectByHandle(
  [in]            HANDLE                     Handle,
  [in]            ACCESS_MASK                DesiredAccess,
  [in, optional]  POBJECT_TYPE               ObjectType,
  [in]            KPROCESSOR_MODE            AccessMode,
  [out]           PVOID                      *Object,
  [out, optional] POBJECT_HANDLE_INFORMATION HandleInformation
);

根据文档,ObReferenceObjectByHandle函数的第四个参数是AccessMode,因此就可以判断出AfdNotifyRemoveIoCompletion的a1是PreviousMode

查看AfdNotifySock的交叉引用,可以看到他在AfdImmediateCallDispatch派遣函数表的最后面,再往下就是另一张派遣函数表了

image-20250626100135014

查看AfdImmediateCallDispatch的交叉引用,可以找到他在AfdFastIoDeviceControl函数的797行被用到

image-20250626101600254

其中v64会作为这张表的index来获取目标函数,v64由a7计算出来

查看AfdFastIoDeviceControl函数的交叉引用,可以看到他在AfdFastIoDispatch派遣函数表中,再查看AfdFastIoDispatch的交叉引用

image-20250626101745064

可以看到afd注册了FastIoDispatch

typedef struct _FAST_IO_DISPATCH {
  ULONG                                  SizeOfFastIoDispatch;
  PFAST_IO_CHECK_IF_POSSIBLE             FastIoCheckIfPossible;
  PFAST_IO_READ                          FastIoRead;
  PFAST_IO_WRITE                         FastIoWrite;
  PFAST_IO_QUERY_BASIC_INFO              FastIoQueryBasicInfo;
  PFAST_IO_QUERY_STANDARD_INFO           FastIoQueryStandardInfo;
  PFAST_IO_LOCK                          FastIoLock;
  PFAST_IO_UNLOCK_SINGLE                 FastIoUnlockSingle;
  PFAST_IO_UNLOCK_ALL                    FastIoUnlockAll;
  PFAST_IO_UNLOCK_ALL_BY_KEY             FastIoUnlockAllByKey;
  PFAST_IO_DEVICE_CONTROL                FastIoDeviceControl;
  PFAST_IO_ACQUIRE_FILE                  AcquireFileForNtCreateSection;
  PFAST_IO_RELEASE_FILE                  ReleaseFileForNtCreateSection;
  PFAST_IO_DETACH_DEVICE                 FastIoDetachDevice;
  PFAST_IO_QUERY_NETWORK_OPEN_INFO       FastIoQueryNetworkOpenInfo;
  PFAST_IO_ACQUIRE_FOR_MOD_WRITE         AcquireForModWrite;
  PFAST_IO_MDL_READ                      MdlRead;
  PFAST_IO_MDL_READ_COMPLETE             MdlReadComplete;
  PFAST_IO_PREPARE_MDL_WRITE             PrepareMdlWrite;
  PFAST_IO_MDL_WRITE_COMPLETE            MdlWriteComplete;
  PFAST_IO_READ_COMPRESSED               FastIoReadCompressed;
  PFAST_IO_WRITE_COMPRESSED              FastIoWriteCompressed;
  PFAST_IO_MDL_READ_COMPLETE_COMPRESSED  MdlReadCompleteCompressed;
  PFAST_IO_MDL_WRITE_COMPLETE_COMPRESSED MdlWriteCompleteCompressed;
  PFAST_IO_QUERY_OPEN                    FastIoQueryOpen;
  PFAST_IO_RELEASE_FOR_MOD_WRITE         ReleaseForModWrite;
  PFAST_IO_ACQUIRE_FOR_CCFLUSH           AcquireForCcFlush;
  PFAST_IO_RELEASE_FOR_CCFLUSH           ReleaseForCcFlush;
} FAST_IO_DISPATCH, *PFAST_IO_DISPATCH;

那么AfdFastIoDeviceControl函数的类型就是PFAST_IO_DEVICE_CONTROL,根据WRK源代码可知该函数的签名如下

typedef
BOOLEAN
(*PFAST_IO_DEVICE_CONTROL) (
    IN struct _FILE_OBJECT *FileObject,
    IN BOOLEAN Wait,
    IN PVOID InputBuffer OPTIONAL,
    IN ULONG InputBufferLength,
    OUT PVOID OutputBuffer OPTIONAL,
    IN ULONG OutputBufferLength,
    IN ULONG IoControlCode,
    OUT PIO_STATUS_BLOCK IoStatus,
    IN struct _DEVICE_OBJECT *DeviceObject
    );

a7正是IoControlCode

使用AfdNotifySock函数在AfdImmediateCallDispatch派遣表的地址减去这张表的起始地址,再除以8,即可得到v64的值49

那么AfdIoctlTable[v64]就是AfdIoctlTable[49]

0: kd> dds AfdIoctlTable+4*49 l1
fffff806`2c850b24  00012127

这样我们就可以使用IoControlCode 12127来抵达漏洞函数

exp代码分析

使用漏洞控制IO ring结构体里面的regbuffer地址为用户可分配的用户模式地址,并控制regcount

exp