点击上方蓝字“Ots安全”一起玩耍
披露或补丁日期: 2022 年 4 月 12 日
产品:微软视窗
咨询:https ://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-24521
受影响的版本:在 2022 年 4 月 12 日的安全更新之前,适用于 Windows 7、8.1、10、11 和 Windows Server 2008、2012、2016、2019、2022
第一个修补版本:2022 年 4 月 12 日针对 CVE-2022-24521 的安全更新
问题/错误报告:不适用
补丁 CL:不适用
错误引入 CL:不适用
记者:国家安全局、Crowdstrike 的 Adam Podlosky 和 Amir Bazine
编码
概念验证:不适用
漏洞利用样本:不适用
在进行分析时,您是否可以访问漏洞利用示例?不
漏洞
错误类:逻辑错误(缺少间接调用验证)
漏洞详情:
根据 CLFS 格式,签名数组与容器或客户端上下文相交。
当日志块被编码时,扇区的字节SIG_*被传输到一个数组,由 . 指向SignaturesOffset。在解码时,这些字节被写回到它们的初始位置。如果我们以容器上下文和签名数组彼此接近的方式构建基本日志记录,然后将上下文的字节复制到SIG_0... SIG_X,则编码和解码操作不会破坏容器上下文。此外,所有在编码和解码之间修改的数据都将被恢复。
现在让我们假设容器上下文在内存中被修改(PCLFS_CONTAINER_CONTEXT->pContainer被归零)。我们搜索了一段时间它实际使用的地方,这导致我们CClfsBaseFilePersisted::RemoveContainer可以直接从以下位置调用它LoadContainerQ:
__int64 __fastcall CClfsBaseFilePersisted::RemoveContainer(CClfsBaseFilePersisted *this, unsigned int a2)
{
...
v11 = CClfsBaseFilePersisted::FlushImage((PERESOURCE *)this);
v9 = v11;
v16 = v11;
if ( v11 >= 0 )
{
pContainer = *((_QWORD *)containerContext + 3);
if ( pContainer )
{
*((_QWORD *)containerContext + 3) = 0i64;
ExReleaseResourceForThreadLite(*((PERESOURCE *)this + 4), (ERESOURCE_THREAD)KeGetCurrentThread());
v4 = 0;
(*(void (__fastcall **)(__int64))(*(_QWORD *)pContainer + 0x18i64))(pContainer); // remove method
(*(void (__fastcall **)(__int64))(*(_QWORD *)pContainer + 8i64))(pContainer); // release method
v9 = v16;
goto LABEL_20;
}
goto LABEL_19;
}
...
}
为了确保用户不能将任何FAKE_pContainer指针传递给内核,在任何间接调用之前将此字段设置为零:
v44 = *((_DWORD *)containerContext + 5); // to trigger RemoveContainer one should set this field to -1
if ( v44 == -1 )
{
*((_QWORD *)containerContext + 3) = 0i64; // pContainer is set to NULL
v20 = CClfsBaseFilePersisted::RemoveContainer(this, v34);
v72 = v20;
if ( v20 < 0 )
goto LABEL_134;
v23 = v78;
v34 = (unsigned int)(v34 + 1);
v79 = v34;
}
一切都按计划进行,直到没有上述逻辑问题。CClfsBaseFilePersisted::FlushImage -> CClfsBaseFilePersisted::WriteMetadataBlock为了更好地理解它,让我们看看里面的调用链RemoveContainer。与已删除容器相关的信息也应从链接结构中删除,这是通过以下代码完成的:
...
// Obtain all container contexts represented in blf
// save pContainer class pointer for each valid container context
for ( i = 0; i < 0x400; ++i )
{
v20 = CClfsBaseFile::AcquireContainerContext(this, i, &v22);
v15 = (char *)this + 8 * i;
if ( v20 >= 0 )
{
v16 = v22;
*((_QWORD *)v15 + 56) = *((_QWORD *)v22 + 3); // for each valid container save pContainer
*((_QWORD *)v16 + 3) = 0i64; // and set the initial pContainer to zero
CClfsBaseFile::ReleaseContainerContext(this, &v22);
}
else
{
*((_QWORD *)v15 + 56) = 0i64;
}
}
// Stage [1] enode block, prepare it for writing
ClfsEncodeBlock(
(struct _CLFS_LOG_BLOCK_HEADER *)v9,
*(unsigned __int16 *)(v9 + 4) << 9,
*(_BYTE *)(v9 + 2),
0x10u,
1u);
// write modified data
v10 = CClfsContainer::WriteSector(
*((CClfsContainer **)this + 19),
*((struct _KEVENT **)this + 20),
0i64,
*(void **)(*((_QWORD *)this + 6) + 24 * v8),
*(unsigned __int16 *)(v9 + 4),
&v23);
...
if ( v7 )
{
// Stage [2] Decode file again for futher processing in clfs.sys
ClfsDecodeBlock((struct _CLFS_LOG_BLOCK_HEADER *)v9, *(unsigned __int16 *)(v9 + 4), *(_BYTE *)(v9 + 2), 0x10u, &v21);
// optain new pContainer class pointer
v17 = (_QWORD *)((char *)this + 448);
do
{
// Stage [3] for each valid container
// update pContainer field
if ( *v17 && (int)CClfsBaseFile::AcquireContainerContext(this, v6, &v22) >= 0 )
{
*((_QWORD *)v22 + 3) = *v17;
CClfsBaseFile::ReleaseContainerContext(this, &v22);
}
++v6;
++v17;
}
while ( v6 < 0x400 );
}
...
当操作开始时,pContainer设置为零。在阶段 [1]中,信息被编码 -> 每个扇区的字节被写入它们的位置 -> 我们使用我们从用户模式提供的信息恢复零字段。唯一的问题是在阶段 [3]CClfsBaseFile::AcquireContainerContext失败(很容易做到)。如果一切都完成了,我们将能够将任何地址传递给内部的间接调用链,从而导致直接 RIP 控制。CClfsBaseFilePersisted::RemoveContainer
补丁分析:
CLFS.sys 的补丁差异揭示了八个更改和两个新功能。其中,新的逻辑块已添加到LoadContainerQ函数中:
...
containerArray = (_DWORD *)((char *)BaseLogRecord + 0x328); // *CLFS_CONTAINER_CONTEXT->rgContainers
...
v22 = CClfsBaseFile::ContainerCount(this);
...
while ( containerIndex < 0x400 )
{
v17 = (CClfsContainer *)containerIndex;
if ( containerArray[containerIndex] )
++v24;
v89 = ++containerIndex;
}
...
if ( v24 == v22 )
{
if ( (unsigned int)Feature_Servicing_38197806__private_IsEnabled() )
{
v25 = (_OWORD *)((char *)v19 + 0x138);
v26 = (unsigned int *)operator new(0x11F0ui64, PagedPool);
rgObject = v26;
if ( !v26 )
{
goto LABEL_135;
}
memmove(v26, containerArray, 0x1000ui64);
v28 = rgObject + 0x400;
v29 = 3i64;
...
v20 = CClfsBaseFile::ValidateRgOffsets(this, rgObject);
v72 = v20;
operator delete(rgObject);
}
事实上,这个块是一个包装器CClfsBaseFile::ValidateRgOffsets:
__int64 __fastcall CClfsBaseFile::ValidateRgOffsets(CClfsBaseFile *this, unsigned int *rgObject)
{
...
LogBlockPtr = *(_QWORD *)(*((_QWORD *)this + 6) + 48i64); // * _CLFS_LOG_BLOCK_HEADER
...
signatureOffset = LogBlockPtr + *(unsigned int *)(LogBlockPtr + 0x68); // PCLFS_LOG_BLOCK_HEADER->SignaturesOffset
...
qsort(rgObject, 0x47Cui64, 4ui64, CompareOffsets); // sort rgObject array
while ( 1 )
{
currObjOffset = *rgObject2; // obtain offset from rgObject
if ( *rgObject2 - 1 <= 0xFFFFFFFD )
{
pObjContext = CClfsBaseFile::OffsetToAddr(this, currObjOffset); // Obtain in-memory representation
// of the object's context structure
...
unkn = currObjOffset - 0x30;
v13 = rgIndex * 4 + v5 + 0x30;
if ( v13 < v5 || v5 && v13 > unkn )
break;
v5 = unkn;
if ( *pObjContext == 0xC1FDF008 ) // CLFS_NODE_TYPE_CLIENT_CONTEXT
{
rgIndex = 0xC;
}
else
{
if ( *pObjContext != 0xC1FDF007 ) // CLFS_NODE_TYPE_CONTAINER_CONTEXT
return 0xC01A000D;
rgIndex = 0x22;
}
criticalRange = &pObjContext[rgIndex]; // get the address of context + 0x30
if ( criticalRange < pObjContext || (unsigned __int64)criticalRange > signatureOffset ) // comapre with sig offset
break;
}
++i;
++rgObject2;
if ( i >= 0x47C )
return ret;
}
return 0xC01A000D;
}
正如我们所看到的,这个函数只是检查签名偏移是否与任何上下文对象相交。此外,它还验证几个上下文字段,例如CLFS_NODE_ID.
关于如何发现这个漏洞的想法(模糊测试、代码审计、变体分析等):
我们认为这个漏洞可能是从代码审计/逆向工程中发现的,因为 (1) 必须制作基本日志记录以使容器上下文保持不受编码/解码操作的破坏 (2)CClfsBaseFile::AcquireContainerContext函数必须故意失败。平心而论,因为(2)很容易实现,这实际上可能是通过模糊测试或其他方式发现的。
错误的(历史/现在/未来)上下文:
https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-24521
漏洞利用
(术语利用原语、利用策略、利用技术和利用流程在此处定义。)
利用策略(或策略):
由于我们没有要分析的样本,我们不知道 ITW 漏洞是如何工作的。然而,我们确实设法利用此漏洞,通过类似的过程用管道对象覆盖进程令牌,如 SSTIC2020:Scoop the Windows 10 pool!纸。
利用流程:
-
创建管道对象并添加管道属性。属性是键值对,存储在链表中,PipeAttribute对象分配在Paged Pool中。
-
用于NtQuerySystemInformation泄漏大池中管道对象的内核虚拟地址。
-
分配fake_pipe_attribute对象。稍后将使用它将其地址注入原始双向链表。
-
使用 获取选定的小工具模块基地址NtQuerySystemInformation。
-
触发 CLFS 错误,它允许我们调用执行任意数据修改的模块小工具来实现可用于获取EPROCESS地址的任意读取原语。
-
触发 CLFS 错误以覆盖用户模式进程令牌以提升到系统权限。
相同漏洞利用流程的已知案例:
不适用
漏洞利用链的一部分?
不适用
接下来的步骤
变异分析
变异分析的领域/方法(以及原因):不适用
发现的变体:不适用
结构改进
有哪些结构性改进,例如杀死 bug 类、防止引入此漏洞、缓解利用流程、使此类漏洞更难利用等方法?
杀死错误类的想法:不适用
缓解漏洞利用流程的想法:不适用
其他潜在改进:不适用
0day检测方法
类似 0-day 的潜在检测方法是什么?意思是对于如何将此漏洞利用或类似漏洞检测为 0-day有任何想法吗?
其他参考
有关受影响版本的更多信息,请访问Microsoft 咨询网站。
有关利用的更多详细信息,请参阅CVE-2022-24521:分析和利用 Windows 通用日志文件系统 (CLFS) 逻辑错误漏洞博客文章。
原文始发于微信公众号(Ots安全):CVE-2022-24521:Windows 通用日志文件系统 (CLFS) 逻辑错误漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论