我之前看过一次secure channel, 在跟踪crypto处理时, 也发现了一个整数溢出(CVE-2024-29050), 可惜的是没找到远程无认证的攻击场景, 最后只能实现基于文件的远程代码执行. 在这个公告更新的时候, 也去分析了一下问题, 发现它并不是如微软官方定义的那样, 是一个dos的问题, 实际上, 它是一个uaf问题, 而且有利用的潜质, 合理的利用是非常可能被用于未认证的远程代码执行的, 鉴于它的危害性, 我就没有在补丁出来后公告, 而是现在才发文. 下面让我们一起了解一下吧.

Bug

首先, 通过补丁对比, 很容易就看到修补的位置, 在 CSsl3TlsContext::CSsl3TlsContext函数内, 多了这么一段代码:

1
2
3
4
5
if ( !(unsigned __int8)wil::details::FeatureImpl<__WilFeatureTraits_Feature_2612696381>::__private_IsEnabled(&`wil::Feature<__WilFeatureTraits_Feature_2612696381>::GetImpl'::`2'::impl) )
{
*(_QWORD *)(this + 472) = *(_QWORD *)(a2 + 472);
*(_QWORD *)(a2 + 472) = 0i64;
}

经过实际测试, 这个feature函数返回1, 因此补丁实际上是屏蔽了这个字段的赋值操作.

让我们来分析一下 472偏移的位置放了什么东西.

使用ida搜索binary:

1724034570953

筛选一下结果, 得到如下赋值操作:

1724034623677

在函数CSsl3TlsServerContext::ProcessRecord内, 一段代码如下:

1724034677110

可以看到, 这里申请了一个新内存(记作M1), 并将它赋值给472(hex: 1D8h)的字段.

查看函数CTlsMessageFragment::Initialize:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void __fastcall CTlsMessageFragment::Initialize(CTlsMessageFragment *this, struct CSsl3TlsContext *a2)
{
int v2; // eax
int v3; // edx
unsigned int v4; // edx
unsigned int v5; // eax

*(_QWORD *)this = a2;
......
v3 = 1536;
LABEL_9:
*((_DWORD *)this + 3) = v3;
v5 = *((_DWORD *)this + 2);
if ( v5 > 0xFFFFFF )
v5 = 0xFFFFFF;
*((_DWORD *)this + 2) = v5;
}

可以看到, 它把a2赋值给了M1的第一个8字节的字段.

那么我们大概可以得出一个结论, 那就是在CSsl3TlsContext::CSsl3TlsContext内, 虽然赋值操作将a2结构体的472字段置零了, 但是, 没有更新M1的第一个字段, 导致它还是指向了a2, 所以, 可能在后续的释放流程里, 原始的a2结构体被先释放了, 然后接着在新的结构体里引用了M1的第一个字段, 从而导致了UAF问题.

接下来我们看一下其它引用了该字段且是free或者clean的操作:

1724035328253

查看函数CTls13ServerContext::CleanupConnectedState

1724035366870

从这可以看到, 它在释放流程里引用了该字段.v12是M1结构体, *v12就是被释放的结构体.

最后经过实际测试, 也确实是这个位置存在uaf问题. 从使用的位置也可以看出, 它会使用到结构体的虚表, 如果占位得当, 就可以直接控制rip, 配合合理的gadgets, 就可以实现远程代码执行.

感想

漏掉它, 还是在于我经验不足, 没有深入allocate函数去看它的实现中是否错误地引用了字段, 另一个, 我当时的重点放在了字节码解析上, 所以没有重点考虑uaf的问题. 经过此次学习, 也是提醒我看代码要尽量有耐心, 多关注字段的赋值.