攻击Windows平台NVIDIA驱动程序

前言

现代图形驱动程序是十分复杂的,它提供了大量有希望被利用的攻击面,可以使用具有访问GPU权限的进程(比如Chrome的GPU进程)进行提权和沙箱逃逸。在这篇文章中,你们将看到如何攻击NVIDIA内核模式的Windows驱动程序,以及在此期间我发现的一些bug。我的这项研究是Project
Zero的一个20%项目的一部分,在此期间我总共发现了16个漏洞。

内核WDDM接口

图形驱动程序的内核模式组件被称为显示微端口驱动程序。微软的官方文档为我们提供了一个很好的结构图,总结了各个组件之间的关系:

在显示微端口驱动程序
的DriverEntry()函数中,DRIVER_INITIALIZATION_DATA结构被由厂商实现的函数(实际上与硬件进行交互)的回调进行填充,该函数通过DxgkInitialize()传递给dxgkrnl.sys(DirectX的子系统)。这些回调要么由DirectX内核子系统调用,要么在某些情况下直接从用户模式代码调用。

DxgkDdiEscape

一个众所周知的潜在漏洞的入口点是 DxgkDdiEscape
接口。它可以直接在用户模式下被调用,并且可以接受任意数据,该数据以厂商指定的方式(本质上是IOCTL)解析和处理。在后文中,我们将使用术语“逃逸”来表示由DxgkDdiEscape
函数支持的特定命令。

截止写作时,NVIDIA有着数量惊人的400多个逃逸,所以这里也是我花费了绝大部分时间的地方(这些逃逸中的绝大多数是否有必要处在内核空间中是一个问题):


  1. // (这些结构体的名称是我命名的) 
  2. // 表示一组逃逸代码 
  3. struct NvEscapeRecord { 
  4.   DWORD action_num; 
  5.   DWORD expected_magic; 
  6.   void *handler_func; 
  7.   NvEscapeRecordInfo *info; 
  8.   _QWORD num_codes; 
  9. }; 
  10.    
  11. // 有关特定逃逸代码的信息 
  12. struct NvEscapeCodeInfo { 
  13.   DWORD code; 
  14.   DWORD unknown; 
  15.   _QWORD expected_size; 
  16.   WORD unknown_1; 
  17. }; 

NVIDIA为每一个逃逸都单独实现了其私有数据(DXGKARG_ESCAPE 结构体中的pPrivateDriverData),格式为“头部+数据”。头部格式如下:


  1. struct NvEscapeHeader { 
  2.   DWORD magic; 
  3.   WORD unknown_4; 
  4.   WORD unknown_6; 
  5.   DWORD size; 
  6.   DWORD magic2; 
  7.   DWORD code; 
  8.   DWORD unknown[7]; 
  9. }; 

这些逃逸由32位代码(上面NvEscapeCodeInfo结构体的第一个成员)标识,并根据它们的最高有效字节进行分组(从1到9)。

在处理每个逃逸代码之前都会做一些验证。具体来说,每个 NvEscapeCodeInfo
应当包含头部后面的逃逸数据的预期大小。这将根据NvEscapeHeader中的大小来验证,NvEscapeHeader自身又通过传递给
DxgkDdiEscape的PrivateDriverDataSize字段进行验证。但是,预期大小有时可能为0(通常当逃逸数据为可变大小时),这意味着逃逸处理程序负责进行自身的验证。这将导致一些bug(1,2)。

在逃逸处理程序中发现的大多数漏洞(总共13个)都是些非常基本的bug,例如盲目地向用户提供的指针进行写入操作,向用户模式公开未初始化的内核内存以及不正确的边界检查。还有许多我发现的问题(例如OOB读取)没有报告出去,因为它们似乎没有可以利用的地方。

DxgkDdiSubmitBufferVirtual

另一个有趣的入口点是DxgkDdiSubmitBufferVirtual函数,它首次在Windows 10和WDDM
2.0中被引入,主要用来支持GPU虚拟内存(而旧的 DxgkDdiSubmitBuffer / DxgkDdiRender
函数已被弃用)。这个函数相当复杂,并且还接受来自用户模式驱动程序提交的每一个由厂商特定的数据。我在这里找到了一个bug。

其他

还有一些其他WDDM函数接受厂商特定的数据,但快速浏览后没有发现任何有趣的东西。

暴露的设备

NVIDIA暴露了可由任何用户打开的一些其他设备:


  1. \\.\ NvAdminDevice 

似乎用于 NVAPI。很多ioctl处理程序似乎都会调用DxgkDdiEscape。


  1. \\.\ UVMLite {Controller,Process *} 

可能与NVIDIA的“统一内存”相关。在这里找到1个bug。


  1. \\.\ NvStreamKms 

作为GeForce Experience的一部分默认选择安装,但也可以在安装期间选择停用。不是很明白为什么这个驱动程序是必要的。在这里也发现了1个bug。

更多有趣的Bug

我发现的大多数bug是通过手动逆向和分析得到的,并且使用了一些自定义的IDA脚本。我还写了一个模糊工具。最终结果成功得有点令人惊讶,这也说明了这些bug的简单性。

虽然大多数bug相当无聊(缺乏验证之类的简单案例),但也有一些比较有意思。

NvStreamKms

此驱动程序使用 PsSetCreateProcessNotifyRoutineEx 函数注册进程创建通知回调。该回调检查系统上创建的新进程是否和先前通过发送IOCTL设置的映像名称相匹配。

这个创建通知的例程包含一个bug:

(简化的反编译输出)


  1. wchar_t Dst[BUF_SIZE]; 
  2.    
  3. ... 
  4.    
  5. if ( cur->image_names_count > 0 ) { 
  6.   // info_是传递给例程的PPS_CREATE_NOTIFY_INFO 
  7.   image_filename = info_->ImageFileName; 
  8.   buf = image_filename->Buffer; 
  9.   if ( buf ) { 
  10.     filename_length = 0i64; 
  11.     num_chars = image_filename->Length / 2; 
  12.     // 通过扫描反斜杠来查找文件名 
  13.     if ( num_chars ) { 
  14.       while ( buf[num_chars - filename_length - 1] != '\\' ) { 
  15.         ++filename_length; 
  16.         if ( filename_length >= num_chars ) 
  17.           goto DO_COPY; 
  18.       } 
  19.       buf += num_chars - filename_length; 
  20.     } 
  21. DO_COPY: 
  22.     wcscpy_s(Dst, filename_length, buf); 
  23.     Dst[filename_length] = 0; 
  24.     wcslwr(Dst); 

此例程通过向后搜索反斜杠('\')的方法从PS_CREATE_NOTIFY_INFO的ImageFileName 成员中提取映像名称,然后使用 wcscpy_s 将其复制到堆栈缓冲区(Dst),但传递的长度是计算出的名称长度,而不是目标缓冲区的长度。

即使 Dst
是大小固定的缓冲区,这也不能被视为一个直接溢出。因为它的大小大于255个wchar长度,并且对于大多数Windows文件系统路径组件来说其不能超过255个字符。而因为ImageFileName
是规范化的路径,所以扫描反斜杠在大多数情况下也是有效的。

然而,上述规则可以通过如下方式绕过:对于一个符合通用命名规约(UNC)的路径,其规范化后保持以正斜杠('/')作为路径分隔符(感谢James
Forshaw向我指出这一点)。这便意味着我们可以得到一个“aaa / bbb / ccc / ...”形式的文件名从而引发溢出。

例如:


  1. CreateProcessW(L"\\\\?\\UNC\\127.0.0.1@8000\\DavWWWRoot\\aaaa/bbbb/cccc/blah.exe", …) 

另一个有趣的关注点是,跟随受损副本的wcslwr实际上并不限制溢出的内容(唯一的要求是有效的UTF-16编码)。因为计算的filename_length不包含null终止符,所以wcscpy_s
会认为目的地太小,然后以在开始处写入null字节的方式来清除目的地字符串(发生在内容复制到 filename_length
字节之后,因此溢出仍然发生)。这意味着 wcslwr是无用的,因为对 wcscpy_s的调用和一部分的代码从来没有工作过。

利用这个漏洞就不那么复杂了,因为驱动程序没有使用堆栈cookie编译过。在以前的漏洞中附加过一个本地特权提升漏洞利用程序,它配置了一个伪造的WebDAV服务器来利用漏洞(ROP,从主堆栈到用户缓冲区,再次ROP来分配
读写执行内存,用来存放shellcode并跳转进去)。

UVMLiteController中错误的验证

NVIDIA的驱动程序还在 \\.\
UVMLiteController路径中暴露了一个可以由任何用户打开的设备(包括从沙箱中的Chrome
GPU进程)。该设备的IOCTL处理程序直接将结果写入Irp->UserBuffer中,作为将要传递给 DeviceIoControl
的输出指针
(微软的文档中指出不要这样做)。IO控制代码指定使用METHOD_BUFFERED,这意味着在Windows内核检查地址提供的范围并将其传递给驱动器之前,用户具有写操作的权限。

然而,这些处理程序还缺少对输出缓冲区的边界检查,这意味着用户模式上下文可以通过任何任意地址传递值为0的长度(可以绕过ProbeForWrite的检查),

这样做的结果是创造出一个受限的Write-what-where情景(这里的“what“仅限于一些特定的值:包括32位0xffff,32位0x1f,32位0和8位0)。

在原始问题中附加了简单的提权漏洞利用 。

远程攻击途径?

考虑到已发现的bug数量如此之众,我做了一个调查,是否可以在不必首先破坏沙盒进程的前提下,完全从远程环境中访问其中任意一个bug(例如通过浏览器中的WebGL或通过视频加速)。

幸运的是结果似乎并非如此。但这并不令人惊讶,因为这里的易受攻击的API是非常底层的,只有经过许多层才能访问得到(对于Chrome而言,需要经历libANGLE
-> Direct3D运行时和用户模式驱动程序 ->内核模式驱动程序),并且通常需要在用户模式驱动程序中构造有效的参数才能调用。

NVIDIA的回应

发现的bug的性质表明NVIDIA仍有很多工作要做。他们的驱动程序包含的很多可能不必出现在内核中的代码,而发现的大多数错误是非常基本的错误。事到如今,他们的驱动程序(NvStreamKms.sys)仍然缺乏非常基本的缓解措施(堆栈cookie)。

不过,他们的反应倒是快速且积极的。大多数bug在截止日期之前已经修复好了,并且他们自己内部也在做一些寻找bug的工作。他们还表示,他们一直在努力重构他们内核驱动程序的安全性,但还没有准备好分享任何具体的细节。

时间线

补丁间隔

NVIDIA的第一个补丁,其中包括我报告的6个bug的修复,但是没有在公告中详细说明(发布说明称作“安全更新“)。他们原本计划在补丁发布后一个月再公布详细信息。我们注意到了这一点并告诉他们这样做并不恰当,因为黑客可以通过逆向补丁来找到之前的漏洞,而当大众意识到这些漏洞细节的时候已经晚了。

虽然前6个bug修复后在30多天内都没有发布修复的详细信息,但剩余的8个bug的修复补丁发布后5天内就发布了细节公告。看上去NVIDIA也一直在尝试减少这种差距,但是就最近的公告来看两者的发布仍有很大的不一致性。

结论

鉴于内核中的图形驱动程序所暴露出来的巨大攻击面,以及第三方厂商的低质量代码,它似乎是挖掘沙箱逃逸和特权提升漏洞的一个非常丰富的目标。GPU厂商应该尽快将其驱动代码从内核中转移出去,从而缩小攻击面使得这种情况得以限制。

作者:overXsky
来源:51CTO

时间: 2024-12-04 17:22:19

攻击Windows平台NVIDIA驱动程序的相关文章

Mirai物联网僵尸攻击竟然可以在Linux平台和Windows平台之间交叉传播

本文讲的是Mirai物联网僵尸攻击竟然可以在Linux平台和Windows平台之间交叉传播, 近日,卡巴斯基实验室通过监测,发现一个全新的物联网木马正在通过Windows设备传播.最早卡巴斯基实验室的安全研究人员观察到这个推送Mirai下载器的扩展器变体是在2017年1月,但其实这个Windows木马以前就有了,只不过通过Windows进行传播的途径也非常有限.不过,如果Mirai木马强制性的远程实施Telnet命令连接,就会从Windows主机传播到Linux主机,尽管这个传播方法目前还没有经

不同WINDOWS平台下磁盘逻辑扇区的直接读写

不同WINDOWS平台下磁盘逻辑扇区的直接读写 关键字:VWIN32.中断.DeviceIoControl 一.概述 在DOS操作系统下,通过BIOS的INT13.DOS的INT25(绝对读).INT26(绝对写)等功能调用实现对磁盘逻辑扇区或物理扇区的读写是很方便的,C语言中还有对应上述功能调用的函数:biosdisk.absread和abswrite等.但在WINDOWS操作系统下编写WIN32应用程序时却再也不能直接使用上述的中断调用或函数了.那么,在WINDOWS操作系统下能不能实现磁盘

Adobe Flash最新零时差漏洞现身Windows平台

上周,Adobe Flash又出现新的重要漏洞,影响层面几乎遍及所有微软Windows 用户,需特别防范! Flash"零时差"漏洞袭来 黑客可轻松植入恶意程序 近日,Adobe 在Microsoft Windows平台的Flash软件中发现一个新的漏洞,黑客可通过该漏洞在Windows 电脑上轻易执行恶意程序,由于被执行的程序将享有与用户相同的权限,使得黑客能在用户电脑上轻易植入更多恶意程序! 与过去所发现的Adobe漏洞不同的是:这次漏洞是"零时差"漏洞,也就是

如何让你的ASP运行于非Windows平台

window 自从Micorsoft推出ASP技术后,由于ASP在创建动态交互式站点上的强大功能及其代码编写的简便性,使ASP在越来越多的Internet/Intranet/Extranet网站上得到了极其广泛的应用,尤其是涉及到数据库操作的网站应用系统更是倾向于采用ASP技术.但由于众所周知的原因,ASP只能工作于Microsoft的Windows NT平台+IIS Web Server服务器软件,在Windows9X+PWS也能使用,但那只能是调试或者学习用的,因而就决定了ASP应用的局限性

让你的ASP运行于非Windows平台

window 自从Micorsoft推出ASP技术后,由于ASP在创建动态交互式站点上的强大功能及其代码编写的简便性,使ASP在越来越多的Internet/Intranet/Extranet网站上得到了极其广泛的应用,尤其是涉及数据库操作的网站应用系统更是倾向于采用ASP技术.但由于众所周知的原因,ASP只能工作于Microsoft的Windows NT平台+IIS Web Server服务器软件,在Windows9X+PWS也能使用,但那只能是调试或者学习用的,因而就决定了ASP应用的局限性.

postgresql在windows平台下的安装

window 经过了一天一夜的折磨,终于让postgresql正常的运行在我的计算机上了,尽管还有些不稳定,但总算是可以用了,废话少说,下面就说说我的配置过程: 1.搞来最新的postgresql for windows版本的,我用的是7.31(***,这个怎么象鬼子的那个细菌部队?打倒日本帝国主义!!!),开始默认安装.不知道为什么这个鸟玩艺儿为什么不能选择安装路径,也许是我没有找到?不过我前前后后安装了二十几遍也没有发现,如果那位大虾发现了请告诉我一声,^O^.安装完成了呢,系统会提示你重新

让ASP程序运行于非Windows平台

window|程序 自从Micorsoft推出ASP技术后,由于ASP在创建动态交互式站点上的强大功能及其代码编写的简便性,使ASP在越来越多的Internet/Intranet/Extranet网站上得到了极其广泛的应用,尤其是涉及数据库操作的网站应用系统更是倾向于采用ASP技术.但由于众所周知的原因,ASP只能工作于Microsoft的Windows NT平台+IIS Web Server服务器软件, 在Windows9X+PWS也能使用,但那只能是调试或者学习用的,因而就决定了ASP应用的

[HOW-TO] Windows平台如何编译MySQL代码

mysql|window|编译 [HOW-TO] Windows平台如何编译MySQL代码 作者:王猛 (HeartIcy@163.com) 修订:初始 日期:2003年7月13日 说明:这不是一篇论文也不是代码阅读指导,写这篇文章的目的只是告诉朋友们如何编译MySQL 代码,或许有些人觉得从Windows平台编译MySQL的代码有些复杂,其实不然.如果您对本文中所提到的知识有任何疑问可以透过CSDN的MySQL板或者MySQL@CHINA支援论坛寻求帮助.这篇文章中所提到的东西没有任何的创新性

Windows平台网站图片服务器架构的演进

构建在Windows平台之上的网站,往往会被业内众多架构师认为很"保守".很大部分原因,是由于微软技术体系的封闭和部分技术人员的短视造成的.由于长期缺乏开源支持,所以只能"闭门造车",这样很容易形成思维局限性和短板.就拿图片服务器为例子,如果前期没有容量规划和可扩展的设计,那么随着图片文件的不断增多和访问量的上升,由于在性能.容错/容灾.扩展性等方面的设计不足,后续将会给开发.运维工作带来很多问题,严重时甚至会影响到网站业务正常运作和互联网公司的发展(这绝不是在危言