Windows NTFS 权限提升漏洞
与以下不同:
补丁:2021年12月14日
根据 Twitter 上的 @AravGarg3 所述,CVE-2021-43229 似乎是可利用的,并且与 整数溢出 有关。
来源:https://twitter.com/AravGarg3/status/1479447843458863104
| Windows 10 次要版本 | 发布日期 |
|---|---|
| 1387 | 2021年11月22日 |
| 1415 | 2021年12月14日 |
使用 IDA Pro 中的 BinDiff,补丁差异比较结果如下:
三个候选函数脱颖而出:
根据 Microsoft 12 月的 安全更新指南,有四个 CVE 与 NTFS 相关:
CVE-2021-43240 似乎与 NtSetShortNameInfo 有关。
NtfsRenameToPrivateDir、TxfAllocateAndStoreNameForTxLogging 和 TxAllocateFullFilePathForChangeNotify 三个函数都有相同的新长度检查,并且似乎与 CVE-2021-43229、CVE-2021-43230 和 CVE-2021-43231 相关,但不一定按此顺序。目前无法确定哪个对应哪个,Microsoft 对这方面的信息有些吝啬。
在这三个 CVE 中,分配大小计算(目录路径长度 + 文件名长度)时发生整数溢出,导致基于池的缓冲区溢出,随后两次 memmove 操作目录路径和文件名。
NtfsRenameToPrivateDir到 NtfsRenameToPrivateDir 的路径如下所示:
NtfsCommonSetInformation
|__________________
| |
v v
NtfsSetLinkInfo NtfsSetRenameInfo
|__________________|
|
v
NtfsRemoveSupersededTarget
|
v
NtfsRenameToPrivateDir
首先,要调用 NtfsCommonSetInformation,只需调用 NtSetInformationFile。然后,要经过 NtfsSetLinkInfo 和 NtfsSetRenameInfo,分别使用 FileLinkInformationEx 和 FileRenameInformationEx 作为 NtSetInformationFile 中的文件信息类。
要通过 NtfsSetRenameInfo 到达 NtfsRemoveSupersededTarget,必须设置 FILE_RENAME_REPLACE_IF_EXISTS 和 FILE_RENAME_POSIX_SEMANTICS 标志。使用现有文件的名称重命名文件,以访问 NtfsRemoveSupersededTarget。对于 NtfsRenameToPrivateDir,稍微有些棘手,因为被替换的文件必须被某个进程打开。
不幸的是,在调用 NtfsRemoveSupersededTarget 之前会进行长度检查。
使用 NtfsSetLinkInfo 的方法与 NtfsSetRenameInfo 类似,只是标志名称不同,含义相同。但与 NtfsSetRenameInfo 一样,在调用 NtfsRemoveSupersededTarget 之前也会进行另一个检查。
TxfAllocateAndStoreNameForTxLogging到 TxfAllocateAndStoreNameForTxLogging 的路径如下所示:
NtfsCommonCreate
|
v
NtfsCreateNewFile
|
v
TxfNewFileCreate
|
v
TxfAllocateAndStoreNameForTxfLogging
首先,要调用 NtfsCommonCreate,只需调用 CreateFile。
TxfNewFileCreate 函数前缀为 Txf,意为“事务性 NTFS”,在创建事务文件时被调用。TxfAllocateAndStoreNameForTxfLogging 在处理过程中被调用,以存储事务文件的路径。
只需要两个调用:
下面显示了 TxfAllocateAndStoreNameForTxLogging 的易受攻击代码:
...
and di, 2
add di, [rsi+UNICODE_STRING.Length] ; 文件名
add di, [rsp+68h+RelativeNormalizedDirectoryPath.Length] ; 此处溢出
cmp [rsp+68h+arg_20], r12b
jnz loc_16592C
movzx edx, di
add rdx, size UNICODE_STRING ; NumberOfBytes
mov ecx, cs:PoolType
or ecx, 10h ; PoolType
mov r8d, 'afxT' ; Tag
call cs:__imp_ExAllocatePooliWthTag
...
为了触发溢出,需要创建一个长度超长的文件,超过 0xFFFF 字节(32767 个字符)。
出现的一个问题是,文件或目录名称不能超过 256 个字符(包括 NULL 字符)。为了解决这个问题,需要使用深层子文件夹。奇怪的是,即使成功创建了子文件夹,子文件也无法创建,这肯定是因为之前进行了某些检查。
使用 资源管理器 的地址栏,可以注意到显示目录路径的格式。这并不是我预期的格式,资源管理器 没有使用“经典”路径,而是使用了旧的 DOS 格式,即 8.3 短格式。
保持子文件夹创建方式不变,并在文件创建期间对子目录使用短名称,就可以解决问题。
需要注意的是,系统会向溢出的尺寸增加 16 个字节,这是 UNICODE_STRING 的大小,该结构将表示最终路径。这是因为请求的内存将包含 UNICODE_STRING 及其后面的缓冲区。
方法:
当前条件:
来自 Synacktiv 在 SSTIC 2020 的一个有趣的报告可能是一个值得深入研究的思路,即针对 VS Heap。
CVE-2020-17087 是一个类似的利用案例,由 PixiePoint Security 使用上述技术进行了演示。
| Simi | Conf | 函数 | 信息 |
|---|
| NOK | 0.98 | 0.99 | NtfsCommonSetInformation$fin$0 | 跳转修改 |
| 0.97 | 0.99 | NtSetShortNameInfo | ||
| 0.96 | 0.99 | NtfsUpdateSecurity | ||
| NOK | 0.91 | 0.94 | NtfsRenameToPrivateDir$fin$1 | 跳转修改 |
| 0.89 | 0.98 | NtfsInitializeFileInDirectory | ||
| OK | 0.88 | 0.95 | TxfAllocateAndStoreNameForTxLogging | 长度检查 |
| OK | 0.87 | 0.93 | NtfsRenameToPrivateDir | 长度检查 |
| OK | 0.81 | 0.95 | TxfAllocateFullFilePathForChangeNotify | 长度检查 |
| 0.76 | 0.94 | NtfsCommonSetInformation |