这是一个品相极好的内核提权逻辑漏洞,同一个模块中有完整的漏洞利用链,不需要破坏对象也不需要内存布局便可利用,所以在此分享。
漏洞信息
- 漏洞模块:mskssrv.sys
- 发布时间:2023.6.13
环境
- windows 10 版本 10.0.19045.3031(22h2 5月补丁)
EXP
使用 exp:https://github.com/Nero22k/cve-2023-29360/tree/main
漏洞原理
补丁对比发现漏洞函数 FsAllocAndLockMdl 在调用 MmProbeAndLockPages 函数时给其传递的第二个参数 AccessMode 由 KernelMode 修改为了 UserMode。
漏洞函数 FsAllocAndLockMdl 的功能较为简单:创建一个 MDL 结构描述一个虚拟地址物理页布局。创建 MDL 共分为两步:一是调用 IoAllocateMdl 创建 MDL 结构并初始化其头部数据,二是调用 MmProbeAndLockPages 探查 MDL 结构要描述的虚拟地址、锁定物理页并设置 MDL 的 pfn 数组。
分析 MmProbeAndLockPages 函数发现其是否探查虚拟地址与其第二个参数 AccessMode 有关,当 AccessMode 为 UserMode 时会探查要描述的虚拟地址是否为内核层地址(大于 0x7FFFFFFF0000),是则报错,反之当 AccessMode 为 KernelMode 时则不会探查。
分析漏洞函数 FsAllocAndLockMdl 的调用关系发现,其调用可以由用户层 NtDeviceIoControlFile 函数触发且其要描述的虚拟地址也可以由用户层传递的数据完全控制,这就造就了一个可以使用用户层调用利用 FsAllocAndLockMdl 创建一个描述内核虚拟地址的 MDL 的漏洞。如果再能将该 MDL 映射到用户态地址空间中,就可以在用户层使用用户态虚拟地址修改对应的内核虚拟地址中的数据,这样利用链就完整了。
利用原理
漏洞模块 mskssrv.sys 中正好存在这样一条由用户层 NtDeviceIoControlFile 函数触发的利用链:
1, FSRendezvousServer::PublishTx 函数使用用户层提供的数据创建一个描述内核虚拟地址的 MDL,该 MDL 的指针将被存储在 FSFrameMdl 对象中,而 FSFrameMdl 对象又被链接在 FSStreamReg 对象 0xc8 处的 FSFrameMdlList 链表中。
2,FSRendezvousServer::ConsumeTx 函数从 FSStreamReg 对象 0xc8 处的 FSFrameMdlList 链表中遍历出所有 FSFrameMdl 对象,然后将 FSFrameMdl 对象中的 MDL 映射到用户态虚拟地址空间,最后将完成映射的 FSFrameMdl 对象链接到 FSStreamReg 对象 0x140 处的 FSFrameMdlList 链表中。
创建 MDL:
映射 MDL:
分析最终映射 MDL 的 MmMapLockedPagesSpecifyCache 函数发现其既可以将 MDL 描述的虚拟地址缓冲区的物理页映射到内核虚拟地址空间中也可以映射到用户虚拟地址空间中,这也取决于其第二个参数 AccessMode。
部分对象结构:
//size 0xd8
typedef struct _FSFrameMdl{
LIST_ENTRY list_entry //0x0 与 FSStreamReg 对象中的 FSFrameMdlList 相链接
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
}FSList,*pFSList;
//size 0x78
typedef struct _FSFrameMdlList{
DWORD64* vftable //0x0 虚表
FSList fslist //0x40 FSList 结构体,其中的链表链接 FSFrameMdl 对象
}FSFrameMdlList,*pFSFrameMdlList;
//size 0x1d8
typedef struct _FSStreamReg{
DWORD64* vftable //0x0 虚表
FSStreamReg* self //0x20 指向自己的指针
DWORD type //0x30 类型标识
DWORD size //0x34 自身大小
DWORD64* pEprocess //0x38 当前进程的 EPROCESS 指针(初始化进程)
DWORD64* pEprocess //0x40 当前进程的 EPROCESS 指针(注册进程)
FSFrameMdlList fsframemdllist //0xc8 链接未映射 MDL 的 FSFrameMdl 对象
FSFrameMdlList fsframemdllist //0x140 链接已映射 MDL 的 FSFrameMdl 对象
DWORD64* pFile_Object //0x1d0 FILE_OBJECT 指针
}FSStreamReg,*pFSStreamReg;
利用分析
利用步骤
1,获得当前进程的 token 的地址。
2,创建一个 MDL 结构描述 token.Privileges 的地址。
3,将上述 MDL 结构映射到用户态进程地址空间中。
4,修改用户态地址空间中的 token.Privileges,开启全部权限。
5,注入 winlogon.exe 进程创建进程。
详情
1,获得当前进程的 token 的地址。
使用 NtQuerySystemInformation 函数泄露当前进程 token 的地址。
1: kd> dt _TOKEN ffffcb861c5ed6b0
nt!_TOKEN
+0x000 TokenSource : _TOKEN_SOURCE
+0x010 TokenId : _LUID
+0x018 AuthenticationId : _LUID
+0x020 ParentTokenId : _LUID
+0x028 ExpirationTime : _LARGE_INTEGER 0x7fffffff`ffffffff
+0x030 TokenLock : 0xffffdf0f`34ed6090 _ERESOURCE
+0x038 ModifiedId : _LUID
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES
+0x058 AuditPolicy : _SEP_AUDIT_POLICY
+0x078 SessionId : 1
+0x07c UserAndGroupCount : 0xf
+0x080 RestrictedSidCount : 0
+0x084 VariableLength : 0x1e0
+0x088 DynamicCharged : 0x1000
+0x08c DynamicAvailable : 0
+0x090 DefaultOwnerIndex : 0
+0x098 UserAndGroups : 0xffffcb86`1c5edb40 _SID_AND_ATTRIBUTES
+0x0a0 RestrictedSids : (null)
+0x0a8 PrimaryGroup : 0xffffcb86`19f206e0 Void
+0x0b0 DynamicPart : 0xffffcb86`19f206e0 -> 0x501
+0x0b8 DefaultDacl : 0xffffcb86`19f206fc _ACL
+0x0c0 TokenType : 1 ( TokenPrimary )
+0x0c4 ImpersonationLevel : 0 ( SecurityAnonymous )
+0x0c8 TokenFlags : 0x2a00
+0x0cc TokenInUse : 0x1 ''
+0x0d0 IntegrityLevelIndex : 0xe
+0x0d4 MandatoryPolicy : 3
+0x0d8 LogonSession : 0xffffcb86`16c999d0 _SEP_LOGON_SESSION_REFERENCES
+0x0e0 OriginatingLogonSession : _LUID
+0x0e8 SidHash : _SID_AND_ATTRIBUTES_HASH
+0x1f8 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
+0x308 pSecurityAttributes : 0xffffcb86`210e3350 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
+0x310 Package : (null)
+0x318 Capabilities : (null)
+0x320 CapabilityCount : 0
+0x328 CapabilitiesHash : _SID_AND_ATTRIBUTES_HASH
+0x438 LowboxNumberEntry : (null)
+0x440 LowboxHandlesEntry : (null)
+0x448 pClaimAttributes : (null)
+0x450 TrustLevelSid : (null)
+0x458 TrustLinkedToken : (null)
+0x460 IntegrityLevelSidValue : (null)
+0x468 TokenSidValues : (null)
+0x470 IndexEntry : 0xffffcb86`2767e090 _SEP_LUID_TO_INDEX_MAP_ENTRY
+0x478 DiagnosticInfo : (null)
+0x480 BnoIsolationHandlesEntry : (null)
+0x488 SessionObject : 0xffffdf0f`2fd29570 Void
+0x490 VariablePart : 0xffffcb86`1c5edc30
1: kd> dx -id 0,0,ffffdf0f2d868040 -r1 (*((ntkrnlmp!_SEP_TOKEN_PRIVILEGES *)0xffffcb861c5ed6f0))
(*((ntkrnlmp!_SEP_TOKEN_PRIVILEGES *)0xffffcb861c5ed6f0)) [Type: _SEP_TOKEN_PRIVILEGES]
[+0x000] Present : 0x602880000 [Type: unsigned __int64]
[+0x008] Enabled : 0x800000 [Type: unsigned __int64]
[+0x010] EnabledByDefault : 0x40800000 [Type: unsigned __int64]
使用 !pte 命令查看 token 所在的 pfn,并使用RamMap.exe 查看 token 映射的物理地址,可以发现二者一致。
2,创建一个 MDL 结构描述 token.Privileges 的地址。
将 token.Privileges 的地址作为输入调用 FSRendezvousServer::PublishTx 函数,并最终在 FsAllocAndLockMdl 函数中创建描述 token.Privileges 所在页的 MDL。
3: kd> r
rax=0000000000000004 rbx=0000000000000000 rcx=ffffcb861c5ed6f0 --------> token.Privileges
rdx=0000000000001000 rsi=0000000000000000 rdi=ffffdf0f32b89ae8
rip=fffff80690292c34 rsp=ffffa08f61fd25a8 rbp=ffffdf0f32eea340
r8=ffffdf0f32eea3e0 r9=0000000000000000 r10=00000000736c644d
r11=0000000000001001 r12=0000000000000000 r13=ffffdf0f320a50a0
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei pl zr na po nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040246
MSKSSRV!FsAllocAndLockMdl:
fffff806`90292c34 48895c2408 mov qword ptr [rsp+8],rbx ss:0018:ffffa08f`61fd25b0=0000000000000000
3: kd> k
# Child-SP RetAddr Call Site
00 ffffa08f`61fd25a8 fffff806`9029b824 MSKSSRV!FsAllocAndLockMdl
01 ffffa08f`61fd25b0 fffff806`9029c63d MSKSSRV!FSFrameMdl::AllocateMdl+0x140
02 ffffa08f`61fd2610 fffff806`9029a7e9 MSKSSRV!FSStreamReg::PublishTx+0x9d
03 ffffa08f`61fd2670 fffff806`90299513 MSKSSRV!FSRendezvousServer::PublishTx+0xdd
04 ffffa08f`61fd26b0 fffff806`869ddddc MSKSSRV!SrvDispatchIoControl+0x143
05 ffffa08f`61fd2700 fffff806`7e411385 ks!DispatchDeviceIoControl+0x3c
06 ffffa08f`61fd2730 fffff806`86ca16bf nt!IofCallDriver+0x55
07 ffffa08f`61fd2770 fffff806`86ca1023 ksthunk!CKernelFilterDevice::DispatchIrp+0x23b
08 ffffa08f`61fd27d0 fffff806`7e411385 ksthunk!CKernelFilterDevice::DispatchIrpBridge+0x13
09 ffffa08f`61fd2800 fffff806`7e810dec nt!IofCallDriver+0x55
0a ffffa08f`61fd2840 fffff806`7e810a41 nt!IopSynchronousServiceTail+0x34c
0b ffffa08f`61fd28e0 fffff806`7e80fdb6 nt!IopXxxControlFile+0xc71
0c ffffa08f`61fd2a20 fffff806`7e60f7f5 nt!NtDeviceIoControlFile+0x56
0e 00000067`ed4ff0d8 00007ff6`ebbbcdf1 ntdll!NtDeviceIoControlFile+0x14
...
创建的 MDL,这里可以看到 MDL 中的描述与之前通过 RamMap.exe 和 windbg 查看的一致
3,将上述 MDL 结构映射到用户态进程地址空间中。
调用 FSRendezvousServer::ConsumeTx 将上述创建的 MDL 映射到用户态进程空间中。
0: kd> r
rax=0000000000000000 rbx=0000000000000000 rcx=ffffdf0f36b8c670 --------> MDL
rdx=0000000040000010 rsi=ffffdf0f344bc038 rdi=ffffdf0f32eea340
rip=fffff80690292ce0 rsp=ffffa08f61fd25a8 rbp=ffffdf0f344bc010
r8=ffffdf0f32eea3e8 r9=ffffdf0f344bc038 r10=ffffdf0f33e75080
r11=0000000000000001 r12=0000000000000001 r13=ffffdf0f320a5000
r14=ffffdf0f32eea3e8 r15=ffffdf0f32b16990
iopl=0 nv up ei pl nz na pe nc
cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00040202
MSKSSRV!FsMapLockedPages:
fffff806`90292ce0 48895c2408 mov qword ptr [rsp+8],rbx ss:0018:ffffa08f`61fd25b0=ffffdf0f32b16880
0: kd> k
# Child-SP RetAddr Call Site
00 ffffa08f`61fd25a8 fffff806`9029b9e8 MSKSSRV!FsMapLockedPages
01 ffffa08f`61fd25b0 fffff806`9029c817 MSKSSRV!FSFrameMdl::MapPages+0x168
02 ffffa08f`61fd2600 fffff806`9029aa6d MSKSSRV!FSStreamReg::ConsumeTx+0xdf
03 ffffa08f`61fd2670 fffff806`9029947f MSKSSRV!FSRendezvousServer::ConsumeTx+0xe9
04 ffffa08f`61fd26b0 fffff806`869ddddc MSKSSRV!SrvDispatchIoControl+0xaf
05 ffffa08f`61fd2700 fffff806`7e411385 ks!DispatchDeviceIoControl+0x3c
06 ffffa08f`61fd2730 fffff806`86ca16bf nt!IofCallDriver+0x55
07 ffffa08f`61fd2770 fffff806`86ca1023 ksthunk!CKernelFilterDevice::DispatchIrp+0x23b
08 ffffa08f`61fd27d0 fffff806`7e411385 ksthunk!CKernelFilterDevice::DispatchIrpBridge+0x13
09 ffffa08f`61fd2800 fffff806`7e810dec nt!IofCallDriver+0x55
0a ffffa08f`61fd2840 fffff806`7e810a41 nt!IopSynchronousServiceTail+0x34c
0b ffffa08f`61fd28e0 fffff806`7e80fdb6 nt!IopXxxControlFile+0xc71
0c ffffa08f`61fd2a20 fffff806`7e60f7f5 nt!NtDeviceIoControlFile+0x56
0d ffffa08f`61fd2a90 00007fff`4ed2d194 nt!KiSystemServiceCopyEnd+0x25
0e 00000067`ed4fde58 00007ff6`ebbbc102 ntdll!NtDeviceIoControlFile+0x14
...
映射操作由 nt!MmMapLockedPagesSpecifyCache 函数实现,其返回值为映射的用户态进程空间地址。
查看映射的用户态地址,发现其与内核态地址具有相同的 pfn,说明其与内核态地址指向相同的物理内存页。
4,修改用户态地址空间中的 token.Privileges,开启全部权限。
完成上述映射后,修改映射后的用户态地址中的内容内核态地址内容也会发生改变。
5,注入 winlogon.exe 进程创建进程。
参考文章
- https://big5-sec.github.io/posts/CVE-2023-29360-analysis/
- http://bsodtutorials.blogspot.com/2013/12/understanding-mdls-memory-descriptor.html
- http://www.osronline.com/article.cfm%5eid=423.htm
沙箱云检测
精确检测
基于检测规则的精确漏洞检测,准确识别漏洞编号。
通用检测
基于检测新进程用户, 用户组, 或完整性等级的通用漏洞检测,准确识别用户, 用户组, 或完整性等级升高。
关于我们
360沙箱云是 360 安全情报中心旗下的在线高级威胁分析平台,对提交的文件、URL,经过静态检测、动态分析等多层次分析的流程,触发揭示漏洞利用、检测逃逸等行为,对检测样本进行恶意定性,弥补使用规则查杀的局限性,通过行为分析发现未知、高级威胁,形成高级威胁鉴定、0day 漏洞捕获、情报输出的解决方案;帮助安全管理员聚焦需关注的安全告警,经过安全运营人员的分析后输出有价值的威胁情报,为企业形成专属的威胁情报生产能力,形成威胁管理闭环。解决当前政企用户安全管理困境及专业安全人员匮乏问题,沙箱云为用户提供持续跟踪微软已纰漏,但未公开漏洞利用代码的 1day,以及在野 0day 的能力。
360混天零实验室负责高级威胁自动化检测项目和云沙箱技术研究,专注于通过自动化监测手段高效发现高级威胁攻击;依托于 360 安全大数据,多次发现和监测到在野漏洞利用、高级威胁攻击、大规模网络挂马等危害网络安全的攻击事件,多次率先捕获在野利用 0day 漏洞的网络攻击并获得厂商致谢,在野 0day 漏洞的发现能力处于国内外领先地位,为上亿用户上网安全提供安全能力保障。
发表评论
您还未登录,请先登录。
登录