背景
GPU是终端设备负责图形化渲染的硬件,和CPU相比,它能更高效地并行计算。传统的PC端GPU可以通过PCIe接口在主板上热插拔,而在移动端,GPU往往和CPU集成在一块芯片上,再搭配负责网络通信的基带等配件,统一称为SoC。目前移动端市场占有率比较高的SoC有高通的骁龙系列处理器、联发科天玑系列处理器、华为海思处理器等。这些SoC的CPU部分都有统一规范的arm指令集,这保证了一套程序只需编译一次,在不同厂商的CPU上都能正确运行。但是这些厂商的GPU指令集却非常封闭,甚至有些没有公开的文档,每家厂商在自己的标准上发展了自己的生态,试图构建商业护城河。作为开发者如果需要根据每个硬件厂商定制不同的GPU操作逻辑,想必是一件非常复杂的事情,而事实上的安卓开发者,大多数情况下并不需要接和GPU进行交互,我们在绘制一个窗口、展示一张图片时,是通过调用安卓系统封装的统一接口实现。那安卓又是如何保证这么多硬件兼容性的呢?
上面的兼容性问题其实不止出现在移动端,在PC端也普遍存在。为了保证各种GPU硬件的兼容性,业界制定了统一的OpenGL、Vulkan、DirectX等标准,这些标准约定了名称规范统一的API调用约定。各家SoC厂商如果想推广自己的硬件,就必须自己负责开发基于这些标准实现的系统驱动、软件链接库。以OpenGL标准为例,如果要绘制一个窗口,开发者需要编写下面这样的代码。至于glfwInit、glfwCreateWindow、glfwMakeContextCurrent、glewInit这些函数,是由硬件厂商负责编写成链接库,在系统里供我们动态链接。这些链接库函数会和GPU内核驱动交互,而GPU驱动控制硬件,处理用户的逻辑。
安卓操作系统约定,硬件厂商的SoC在出厂时同时需要提供软件支持,必须满足至少OpenGL以及Vulkan(安卓7以后支持)两种调用约定。厂商负责编写的代码有两个部分,内核层的GPU驱动以及用户态的GPU 链接库。这种代码模块分离的思想在其他硬件如音频驱动、摄像头上也有类似的使用,被安卓统一定义为硬件抽象层(Hardware Abstraction Layer),又称HAL。
安卓只需要在HAL层声明需要哪些接口,明确定义好动态链接库导出的函数名称,传参方式,具体实现都交给了厂商。由于硬件厂商众多,他们编写的代码安全性必定不能和主线代码相比。与之矛盾的是,为了和硬件交互,厂商编写的代码往往都直接运行在内核态,一旦驱动代码出现安全问题,整个系统的安全建设将付之一炬,可谓安卓安全生态中最短的一块木板。
当然安卓研发人员也意识到这种问题的严重性,使用SELinux约束了这些硬件接口的调用权限。比如普通app没有权限直接使用摄像头、麦克风等硬件,必须通过系统的service进行数据中转,极大减少了驱动暴露出的攻击面。在service中转前,又使用AOSP的权限管控约束了APP的行为。
然而,GPU硬件出于性能的考虑,没办法使用service进行中转,任意一个APP默认就有权限和GPU驱动进行交互,在SELinux上也没有相应的进行权限管控。这也导致了GPU成为安卓安全生态中最为脆弱的一环,在过去一两年的安卓在野漏洞利用中,攻击者无一例外地瞄准了GPU驱动,借助GPU的驱动漏洞实现从普通APP到root的权限提升。
认识GPU
在利用GPU漏洞进行系统提权之前,我们有必要理清楚GPU的正常交互逻辑,开发者是如何操作GPU进行图形绘制、并行计算呢?
Shader编程
在CPU计算领域,我们通常使用一些高级编程语言进行程序编写,交给编译器将我们的程序编译成CPU能理解的机器码运行。和CPU流程类似,GPU领域也有专用的编程语言,称为shader,它是控制图形硬件进行图像渲染或单元计算的程序。如下所示,是一个简单的并行计算数组绝对值的shader代码。
当然GPU硬件肯定没办法理解上述语言是什么意义,从上面的语言到GPU硬件指令还需要额外的中间语言编译、硬件语言翻译两个阶段。
我们使用glslang编译工具链将shader代码编译为SPIR-V格式的字节码。
glslangValidator -V shader_abs.comp -o shader_abs.spv
SPIR-V字节码是由业界提出的一种计算机图形学统一的中间语言。OpenGL、VulKan等标准提供了接口来加载并运行SPIR-V字节码,在运行时,OpenGL这些框架会动态地将字节码翻译为GPU能直接理解的指令码进行执行。
至此我们简单理解了GPU的交互过程,这对GPU驱动漏洞挖掘还不够,我们需要从更底层的视角去发现问题。像上面的shader代码,程序的输入输出是float数组,但对于硬件来说这些都是内存里的比特,程序的运行必定涉及到GPU、CPU的内存数据交换、共享,这些又是怎么处理的呢?
GPU内存模型
传统的CPU在使用内存时,提出了虚拟内存的思想。基于硬件控制,不同的进程内存空间相互独立,称为虚拟内存,每个进程的虚拟内存和实际的物理内存之间的映射关系由页表保存。GPU在内存管理方面和CPU有着非常相似的地方。每个GPU程序上下文运行在相互独立的虚拟内存空间,GPU内核驱动负责维护每个GPU上下文的页表,管理程序内存申请、释放、和CPU的共享内存逻辑。
以高通GPU驱动为例,用户态的程序可以通过ioctl和GPU驱动交互,和GPU共享内存。
继续查看高通GPU内核驱动的代码,可以看到驱动里大量复杂的ioctl交互逻辑,处理用户态的请求。
正常的GPU交互逻辑,是app通过加载OpenGL的链接库,在链接库里构造好参数,来和驱动进行ioctl交互。但是一个恶意的攻击者可以直接不加载OpenGL库,直接调用内核态的驱动代码。如果驱动代码中正确处理用户请求,就有可能导致UAF、溢出等内存漏洞。
GPU漏洞回顾
2024年8月,Google的Android Red Team团队披露了一个高通GPU驱动的UAF漏洞CVE-2024-23380,借助这个漏洞,攻击者可以从普通APP的权限提升到系统root。接下来本文对该漏洞的成因、利用过程进行详细分析。
高通GPU驱动提供了一系列接口用于操作GPU的虚拟内存。如IOCTL_KGSL_GPUOBJ_ALLOC用于申请GPU对象(内存),API返回一个id标志,用于区分不同的对象。可以通过IOCTL_KGSL_GPUOBJ_INFO查询到id对应的GPU对象所占用的虚拟内存地址、内存大小等信息。
类似地,IOCTL_KGSL_GPUOBJ_FREE用于释放,IOCTL_KGSL_MAP_USER_MEM用于将CPU的虚拟内存映射到GPU虚拟地址,实现内存共享。上述这些内存申请释放操作,实际上驱动的处理逻辑都是在读写GPU的页表,建立或释放从GPU到物理内存页的映射。
除了正常的GPU内存申请操作,IOCTL_KGSL_GPUOBJ_ALLOC还支持一个特殊的flag KGSL_MEMFLAGS_VBO。通过查阅驱动代码发现,带有这个特殊flag的GPU对象在申请时并没有申请对应的物理内存,而是以zero page进行占位填充。
而后又可以通过IOCTL_KGSL_GPUMEM_BIND_RANGES操作将其他GPU对象的内存页映射到自己对应的虚拟地址空间。相反,也有与之对应的KGSL_GPUMEM_RANGE_OP_UNBIND取消映射操作。而这也是本次漏洞的关键所在。
CVE-2024-23380漏洞分析
内核驱动开发中,一个需要特别注意的点就是并发控制,当读写一些全局变量时,需要对加锁、释放锁的时机格外注意。如下是GPU VBO在进行BIND_RANGES时的处理逻辑,用于将GPU的虚拟内存页VA1映射到另外一块虚拟内存页VA2。操作完成后,VA1和VA2将指向同一块物理内存。
不难看出,上面的操作中 3 和4的顺序被搞反了,正确逻辑应该是先建立好映射后,再释放锁。BIND_UNRANGES与之相反,加锁、解除VA2和物理页的映射、添加VA2和zero page的映射、释放锁。
漏洞利用
上述的漏洞是由于锁释放的时机不对导致的race,那触发漏洞必定通过多线程并发实现,那竞争成功后又能达到什么样的效果呢?
在GPU中申请分别一个正常的对象、一个带有VBO标记的对象,内存页映射关系如下所示。
当正常VBO对象正常执行BIND_RANGE后,GPU的VA1和VA2会同时指向一块物理内存。如果此时执行UNBIND_RANGE操作,它们又会回到上面的初始状态。
但当race的过程时,如果出现以下的执行过程
在第三步释放VA1后,GPU对应的物理页会被还给系统的内存分配器,然而此时VA2仍保留着到物理内存的映射。导出出现PAGE UAF,我们可以通过控制GPU指令,来实现对该物理内存块的读写。
在实际的代码利用过程中,我们可以频发触发PAGE UAF漏洞,使GPU保留更多的物理地址页映射,方便后续的占位操作。此时GPU映射的物理页虽然处于空闲状态,但是仍保留在kgsl_page_pool这个页管理器中,并没有完全被系统回收,需要Linux内核触发memory shrink后,才会回调_kgsl_pool_shrink,进而将物理页完全交给系统。通过查阅文档得知,正常情况下高权限用户可以通过命令 echo 3> /proc/sys/vm/drop_caches 来触发内存碎片回收,对于一个普通app来说,没有权限修改procfs,可以通过频繁申请内存来触发。
在系统完全回收这些内存后,我们可以再次调用mmap申请内存,这一步是为了让CPU的PTE table占据这些空闲页。接下来,我们通过编写shader程序,去读写这些PTE table对应的GPU VA,进而间接通过改变CPU的虚拟内存映射的物理地址,实现全局物理内存读写。在实验过程中发现,高通处理器系列的kernel虽然开启了KASLR,内核虚拟地址是完全随机化的,但是物理地址却是固定值0xa8000000L。从这个位置开始,我们就可以读写整个内核空间的代码、数据段,进而实现代码提权。
最后放出一个视频演示
总结
本文简述了移动端GPU安全研究方向、GPU的攻击面梳理、漏洞分析等内容。从攻击者角度来看,纵观过去几年漏洞安全研究历史,传统的内存破坏、整数溢出等漏洞开始淡出历史舞台,这些都可以通过编译期检查、各种sanitizer机制加以缓解,但像mmu misconfiguration等逻辑型漏洞很难通过fuzzer来触达,驱动在运行时更需要硬件支持,这也为自动化漏洞挖掘带来了新的挑战。从防御者角度来看,内核驱动代码的漏洞不可避免,这也不失为当时安卓顶层安全设计的一个失误,既然无法收敛权限,那也许将驱动程序从内核中剥离出来是一种更安全的解决方案,但这更需要SoC厂商更多的技术支持,其中的技术路线选型、架构设计和原始的驱动代码开发不尽相同,涉及到的软件稳定性、兼容性、性能验证等工作量又是一个未知数,仅从安全收益的角度来说似乎很难说服SoC厂商做出这样的改变。如果安卓官方重新定义一种更为安全的HAL层的接口,强制约束厂商的接入方式,理论上是一种更合理的动机,不过这都需要Android官方和SoC厂商迈出艰难的第一步,究竟GPU安全未来的发展如何,我们拭目以待。
参考资料
【1】https://i.blackhat.com/BH-US-24/Presentations/REVISED02-US24-Gong-The-Way-to-Android-Root-Wednesday.pdf
【2】https://yanglingxi1993.github.io/dirty_pagetable/dirty_pagetable.html
獬豸实验室
獬豸实验室 (Dawn Security Lab)是京东旗下专注前沿攻防技术研究和产品沉淀的安全研究实验室。重点关注移动端安全、系统安全、核心软件安全、机器人安全、IoT安全、广告流量反作弊等基础和业务技术研究。
实验室成员曾多次获得Pwn2Own冠军,在BlackHat、DEFCON、MOSEC、CanSecWest、GeekCon等顶级安全会议上发表演讲,发现Google、Apple、Samsung、小米、华为、Oppo等数百个CVE并获得致谢。曾获得2022年黑客奥斯卡-Pwnie Awards“最佳提权漏洞奖” ;同时也是华为漏洞奖励计划优秀合作伙伴,CNNVD一级支撑单位,GeekCon优秀合作伙伴。
加入我们
獬豸实验室正在招募各路英雄,想要加入崇尚技术创新的、用技术守护互联网安全的我们,欢迎投递简历至[email protected]。
(邮件主题:姓名-投递岗位)
安全研究员
岗位级别:P6及以上
岗位职责
从事操作系统、二进制、浏览器、App等方面的漏洞挖掘和研究工作
任职要求
1. 有独立挖掘分析Android、iOS、macOS、Linux Kernel、Chrome等基础系统漏洞、编写exp能力,发现过原创CVE
2. 有一定代码开发能力,譬如python、go、c/c++、Java等,了解静态代码分析、Fuzz等技术
3. 有顶级安全会议演讲、论文,头部厂商致谢
4. 善于团队合作,为人正直,基础扎实,不畏困难,积极向上
后端开发工程师岗
岗位级别:P7及以上
岗位职责
1. 参与安全产品能力的需求讨论、设计,并负责相关功能的开发落地
2. 按照项目排期,按时提交稳定、高质量代码,完成开发任务
3. 负责系统的架构、技术、运维文档的编写、维护以及其他与项目相关工作
4. 生产突发问题的排查、处理,重点、难点问题的攻关,突破
任职要求
1. 3年以上互联网领域的设计与开发经验,具备扎实的开发基础,精通Java开发语言(Java)
2. 熟练掌握IO、多线程开发技术,有丰富的高并发系统设计经验,对事务、锁、并发等实现机制有深入了解
3. 熟练使用Spring、Spring MVC等框架,并对框架原理有一定了解
4. 熟悉SOA架构,对RPC、序列化、服务治理有相应了解
5. 熟悉常用数据库软件(MySQL)的原理和使用,熟悉常用ORM和连接池组件,对数据库的优化有一定的理解
6. 熟悉计算机网络基础原理、了解常用网络通信协议
7. 热爱技术,对技术有不懈的追求,喜欢研究开源代码,良好的学习能力、团队协作能力和沟通能力,具备安全攻防经历、有安全相关开发经验优先
大数据开发/算法工程师
岗位级别:P7及以上
岗位职责
1. 负责安全准实时计算系统的搭建
2. 负责其他大数据产品相关的架构设计,包括OLAP系统、指标系统等
3. 搭建技术框架,合理分配完成任务,带领其他工程师完成项目需求
4. 攻坚技术难点,解决高并发下复杂数据加工遇到的问题
5. 持续跟踪、学习和引入新技术
任职要求
1. 全日制统招本科及以上学历,具备一线动手能力
2. 拥有较好的架构工作经验,对常用的大数据技术如flink、clickhouse、hadoop、hbase、es等非常熟悉
3. 具有高扩展性、高性能和分布式系统的实践经验,具有大数据以及高并发的系统数据库开发经验,能够应对海量数据的存储,处理和分析需求
4. 对于性能优化、问题解决有丰富的经验
5. 了解图数据库,有数据中台架构设计优先
6. 具有较强的独立思考、分析及解决问题的能力
7. 具有较强的创新意识,善于沟通和解决问题,善于总结分享,有团队精神和主人翁意识,具有较强责任感
数据挖掘工程师
岗位级别:P7及以上
岗位职责
1. 负责安全模型策略建设。深入理解电商业务和系统架构,根据风险制定风险设备、设备指纹、爬虫识别、业务反欺诈、广告反作弊等的防护策略和模型,为模型的业务效果负责
2. 负责数据模型的选型,设计,开发,落地部署和跟踪等
任职要求
1. 本科及以上学历,数学、统计、计算机专业。具有5年以上安全或风控经验优先
2. 熟练掌握互联网业务常见的风险识别、安全策略制定
3. 熟练掌握大数据分析、风险建模方法,识别恶意行为的经验
4. 熟悉常用的统计模型、机器学习、数据挖掘相关算法
5. 抗压力强、沟通协作力强,有敏锐的风险意识和业务sense
移动安全开发工程师
岗位级别:P6以上
岗位职责
1. 负责公司移动安全SDK、设备指纹能力等相关开发工作
2. 与团队内其他伙伴合作,协助完成安全需求的工程化落地工作
任职要求
1. 本科及以上学历,3年以上开发经验。具备良好的编码基础和工程实践能力;计算机/信息安全等相关专业、有相关开发经验者优先
2. 精通Android或iOS相关开发,有一定底层开发经验
3. 具备较强的自驱力,认真负责,对安全技术有浓厚兴趣,有意愿攻克难题;有较好的抗压力强、有较好的团队协作能力
移动安全工程师
岗位级别:P6以上
岗位职责:负责公司移动安全能力建设和风险消除、红蓝对抗相关工作,包括但不限于代码保护、风险识别、设备指纹、漏洞扫描等工作
任职要求
1. 本科及以上学历,3年以上开发经验。具备良好的编码基础和工程实践能力;计算机/信息安全等相关专业、有相关攻防经验者优先
2. 精通Android或iOS安全知识,有一定底层开发和对抗经验
3. 具备较强的自驱力,认真负责,对安全技术有浓厚兴趣,有意愿攻克难题;有较好的抗压力强、有较好的团队协作能力
安全情报运营岗
岗位级别:P6以上
招聘人数:3人
岗位职责:建立外部情报通道,监控黑灰产动向,对黑灰产技术进行研究和分析,获取关键情报线索
任职要求
1.熟悉开源网络情报技术、工作方法、技巧手段
2.熟悉互联网黑灰产链路和模式,强烈的兴趣和专注的研究能力,持续关注国内外黑灰产动态
安全情报研发分析岗
岗位级别:P6以上
招聘人数:2人
岗位职责
1. 负责公司安全情报平台的开发和优化,为情报运营提供平台和工具
2. 收集、分析和解读各种来源的威胁情报,输出精准的情报告警
3. 利用专业工具和技术进行威胁情报挖掘,编写调查分析报告,及时提供关键威胁信息和应对建议
4. 开发和维护威胁情报库,为公司提供及时的威胁预警和响应策略
任职要求
1.本科及以上学历,计算机、信息安全或相关专业优先
2.有情报攻防经验,具备敏锐的风险发现能力,主动关注前沿攻防技术并能灵活转化为实战演练
3.精通常见离线和实时大数据开发和分析技能
4.至少精通一门以上编程语言,如:Python、Java、Golang等
5.有大型分布式系统研发经验者优先
原文始发于微信公众号(京东安全应急响应中心):安卓GPU漏洞攻防介绍
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论