日期:2020年6月
作者:Dennis Elser(代码:github)
根据其官方网站介绍,Winmagic SecureDoc “使企业能够高效地处理其 IT 环境的安全问题,其特性包括:全盘加密(FDE)、多因素认证、可移动介质容器加密(RMCE)以及文件和文件夹加密(FFE)。这些功能有助于企业增强安全性、降低业务风险,并满足政府和监管机构对硬盘加密的要求。”
Winmagic SecureDoc 产品有独立版和企业版,其 8.3 和 8.5 版本存在两个本地权限提升漏洞(CVE-2020-11519 和 CVE-2020-11520)。这些漏洞于 3 月下旬报告给 Winmagic 后,供应商于 2020 年 6 月中旬 发布了补丁(版本 8.5SR2)。然而,该补丁未能充分修复漏洞,导致 8.5SR2 版本仍易受到所述漏洞的攻击。尽管因此原因未公开漏洞的技术细节,但自那时起,这些漏洞实际上已视为公开。据供应商称,另一个补丁仍在开发中,距离最初向 Winmagic 报告漏洞已过去大约 106 天。 7 月 15 日,在向供应商初次报告漏洞 111 天后,Winmagic 向客户发布了 SecureDoc v8.5 SR2 HF1,据称修复了 CVE-2020-11519 和 CVE-2020-11520。早于 8.3 版本的 SecureDoc 未经验证,但根据受影响组件的代码推断,很可能也受到影响。
成功利用任意一个漏洞,都会使本地经过身份验证的攻击者将权限提升至 SYSTEM。
这两个漏洞均影响 Winmagic SecureDoc 产品自带的“SDDisk2k.sys”内核驱动程序组件。通过使用 Hex-Rays IDA Pro 反汇编器和反编译器进行手动静态分析,识别出了这些安全缺陷。事后看来,如果采用诸如模糊测试等动态测试方法,则能以更少的精力发现这些弱点。这是因为该驱动程序可被受限的用户态应用程序访问,并且它默认认为其输入格式正确。
由于“SDDisk2k.sys”驱动程序不安全地创建了“SecureDocDevice”设备对象,并且缺少设置适当安全描述符的代码,即便是受限的用户账户也能通过 CreateFile() API 函数获取该设备的句柄。驱动程序允许用户态应用程序获取其设备对象的句柄,从而直接打开了通往内核态攻击面的路径。``` c RtlInitUnicodeString(&DestinationString, L"\Device\SecureDocDevice"); RtlInitUnicodeString(&SymbolicLinkName, L"\DosDevices\SecureDocDevice"); if ( IoCreateDevice(v1, 0xDD8u, &DestinationString, 0x8D1Fu, 0, 0, &DeviceObject) >= 0 ) // <--- unsafe { memset(DeviceObject->DeviceExtension, 0, 0xDD8ui64); DeviceObject->Flags |= 4u; DeviceObject->AlignmentRequirement = 0; if ( IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString) < 0 ) IoDeleteDevice(DeviceObject); IoObject = DeviceObject; }
通过对 "SDDisk2k.sys" 驱动程序的多个 [IOCTL](https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-i-o-control-codes) 服务处理程序进行逆向工程,发现其中一个处理程序向用户态暴露了关键功能,即它允许对任意驱动器的原始磁盘扇区进行读写操作——这是设计如此。此外,通过与此代码交互,注意到该驱动程序会忽略先前可能对驱动器设置的任何排他锁。因此,并发读写操作成为可能,从而引发竞争条件并存在数据丢失的风险。
以下展示了该驱动程序反编译后的 IOCTL 服务处理程序,该程序负责处理原始磁盘扇区的读取请求。它调用了一个函数 sub_29CD4(),参数为 "controlled_buf",这是一个指向缓冲区的指针,其内容可由任何调用它的用户态应用程序任意选择:``` c
if ( ioctlcode == 0x8D1F2824 ) // <--- I/O control code for raw disk reading functionality
{
controlled_buf = (unsigned __int8 *)controlled_addr;
mode = 0;
temp_result = sub_29CD4((char *)controlled_buf, v3, mode); // <--- call to raw disk read function
实际上,这个由攻击者控制的缓冲区是一个结构体,其字段 "offset"、"length" 和 "ptr_buf" 是完全未经检查的函数参数,这些参数被传递给对 IoBuildSynchronousFsdRequest() 的调用。后一个函数准备了一个 IRP_MJ_READ I/O 请求包 (IRP),并通过调用 IofCallDriver() 将其发送到底层文件系统驱动程序:``` c __int64 __fastcall sub_29CD4(char *controlled_addr, PIRP a2, char mode) { //[...snip...] // extract drive number and type (floppy/hd) from offset 0 devicetype_and_num = (unsigned __int8)*controlled_buf // <--- controllable from user mode
// advance pointer p = controlled_buf + 1;
// build device name if ( (devicetype_and_num & 0x80u) == 0 ) v11 = vsnprintf_wrapper(&device_name, 0x3Fui64, L"\Device\Floppy%d", devicetype_and_num); else v11 = vsnprintf_wrapper(&device_name, 0x3Fui64, L"\Device\Harddisk%d\Partition0", devicetype_and_num & 0x7F); v12 = v11; if ( v11 >= 0 ) { RtlInitUnicodeString(&DestinationString, &device_name);
// get object pointer of drive
if ( IoGetDeviceObjectPointer(&DestinationString, 0x80u, &FileObject, &DeviceObject) >= 0
|| (v12 = sub_2C5D4(&DestinationString, &DeviceObject), v12 >= 0) )
{
// extract further fields from structure
offset = *(_QWORD *)(p + 0x4E); // <--- where to start reading from
length = *(_DWORD *)(p + 0x56); // <--- number of bytes to read
ptr_buf = *(void **)(p + 0x5A); // <--- ptr to destination buffer
devobj = DeviceObject;
StartingOffset.QuadPart = offset << 9;
KeInitializeEvent(&Event, NotificationEvent, 0);
// build request
v17 = IoBuildSynchronousFsdRequest(
(unsigned int)(mode != 0) + IRP_MJ_READ, // <--- issue read request
devobj,
ptr_buf,
length << 9,
&StartingOffset,
&Event,
&IoStatusBlock);
v18 = v17;
if ( v17 )
{
v19 = v17->Tail.Overlay.CurrentStackLocation;
if ( mode )
v19[0xFFFFFFFF].Flags |= 0x10u;
ObfReferenceObject(devobj);
// send request to respective device object (issue read request)
v12 = IofCallDriver(devobj, v18);
//[...snip...] }
就像从用户模式读取原始磁盘扇区一样,通过调用 IOCTL 处理程序 0x8D1F2820 也可以写入磁盘扇区,该处理程序处理相同的数据结构,并以类似的方式实现。鉴于与该驱动程序协议的兼容性,任何用户模式应用程序都可以完全破坏操作系统。除非受到安全启动机制的保护,这甚至包括允许在系统启动过程中早期运行的软件(勒索软件、启动套件、自定义植入程序等)。
### CVE-2020-11520
对 "SDDisk2k.sys" 驱动程序的服务处理程序的进一步检查发现,来自用户模式应用程序的内存地址未经事先验证就被处理。在某些情况下,驱动程序会盲目地向这些指针所访问的内存写入数据,攻击者可利用此漏洞创建内核写入原语。尽管所有这些写入原语都允许直接控制**写入数据的位置**,但遗憾的是没有发现能够直接控制**写入什么数据**的原语。除了 [CVE-2020-11519](#cve-2020-11519)(其在此上下文中的利用需要通过磁盘读写操作进行额外的迂回,我认为这是一种不干净的方法因而希望避免)之外,确定了一个特定的处理程序,它虽然不允许控制数据本身,但通过其他手段仍足以被重新利用。