对高通启动链的一点研究--后记
想一想还是说一说吧。
我们之前复现了GBL上面的验证缺陷导致的任意代码执行
虽然我们能够解锁高通8E5的BOOTLOADER
但是面临几个问题
首先是需要9008写入文件
如果机器的9008的firehose需要授权,那么就必须拆机写入了。
这是问题1
所以说有没有办法呢?
我们想到比如说fastboot flash
但是abl源码告诉我们锁定状态是不能刷入分区的
尽管有些OEM修改了允许刷入少数分区但是没有efisp
然后就是dd命令,需要root权限才能访问dev下的块
很快,酷安等论坛上就开始大量的讨论了,然后很快就有各种各样的方案了。
我们这里来整理一下。
想一想之前的几个路径
Recovery漏洞刷更新包:
这里是华为的办法
华为存在一个USB更新模式,是recovery模式下执行一个程序update-binary
这个程序会初始化一个USB端口,可以进行通信将华为定制的UPDATE.APP发送到设备
但是这个UPDATE.APP有签名和CRC在里面,会进行验证
但是我们发现EMUI9以及以下的版本没有签名验证只有CRC验证
那不就可以利用CRC的线性性,对于长度相同的A和B
有 \(CRC(A xor B)=CRC(A) xor CRC(B)\)
如果存在碰撞\(CRC(A)=CRC(B)\)
那么\(CRC(AxorB)=0\)
那么我们记这个是C
那么\(CRC(C)=0,CRC(A)=CRC(AxorC)\)
找到一个C发现是通用
对于EMUI9往上的版本,有签名验证了
但是不是所有分区都有签名验证
有些分区没有签名验证,比如说PRELOADER
但是本来华为是没有这个分区的,但是PRELOADER和XLOADER都是一个物理位置
而这个recovery的这个模式就是识别镜像的分区名决定写入到哪里
所以还是CRC碰撞
这样可以修改关键的物理分区
对于小米旧的recovery
好像可以使用adb push指令push一些镜像进去
这里面不知道能不能用shell
实际上已经存在漏洞了,好像说放一些特定的固件进去可以刷工程内核还是怎么样
具体我不清楚了
然后当然华为还有一个recovery正常OTA的漏洞早被修复过了说的是ZIP压缩包末尾的签名通过巧妙的设计偏移量
可以使得在签名前面走私一个PK的压缩包然后都会被解析,这样就可以实现任意代码执行然后换成自己的update-binary
这个实际上我找了很多镜像都已经被修复了,不知道taszk找的哪个镜像,所以无法复现。
提权到临时root:
这个是最常用的办法
但是现在的安卓有SELINUX,即使有临时root各种权限也受到限制
临时root一般是通过内核提权,比如说我们有一个内核任意写之类的
可以把当前cred的uid和gid改成0
然后清除所有的secure bits
当然改security结构体里面的sid改成kernel的似乎可以关掉selinux
但是大概率是不行的
也可以改全局的selinux_enforcing变量改成0就permissive了
当然这些前提是hyp没有保护这些区域
华为和三星就不能这样简单的修改但是小米可以
临时root还有办法比如说有一个服务或者一个工程模式,本身有root权限,那么我们如果能够劫持它的程序执行来执行我们的shellcode或者命令,那么就可以实现root权限,但是仍然受到selinux限制
这个相对简单一些我们可以看到之前的unisoc以及oneplus都存在这样的漏洞
https://zhuanlan.zhihu.com/p/55609013?utm_id=0
fastboot漏洞刷任意分区:
这里有些OEM厂商允许在锁定状态下刷写部分分区,可能要签名检查什么的
那么我们就可以想办法绕过
再比如分区名的检查和实际写入分区时候的匹配用的不一样的话也有可能绕过
或者说有一个隐藏的命令(backdoor),我们执行之后能够修改某些标志位使得允许刷写
再比如说如果我们有ABL的任意代码执行的漏洞,那么当然可以直接刷写任意分区,但是这样我们也可以直接解锁我们的BOOTLOADER了
那么我们这次应该怎么办呢?
首先我们发现高通的ABL锁定状态下就是不允许刷写
所以这块不要想了。
我们把思路往提权临时root上靠
结果呢,在浏览ABL代码时候我们发现了一些patch似乎修复了一些漏洞的样子。

https://git.codelinaro.org/clo/la/abl/tianocore/edk2/-/commits/uefi.lnx.5.0.r25-rel/QcomModulePkg/Library/FastbootLib/FastbootCmds.c?ref_type=heads
这里高通修复了cmdline的漏洞
也就是下面复现的漏洞早已经进行了相应的修复。
我一看有一个似乎有人之前还给我发过,当然其实我没有主动要,人家主动发过来的,还说是高通内部的提交(乐,其实是开源的),说是什么cmdline的漏洞,当然我没怎么看因为当时还不太关心这些,但是这个漏洞不能用来abl的任意代码执行因为高通的写入RPMB的scm call过了milestone之后调用就没有用了这也是防止你有内核的权限之后调用解锁函数。所以说就放到一边了。
现在我们细看一下发现有两个命令
一个命令是"fastboot oem set-hw-fence-value"
另一个命令是“fastboot oem set-gpu-preemption”
按道理来说这两个命令后面只能接0或者1,然后会变成一个cmdline的参数传入内核

但是修复前是没有额外检查的,除了过滤开头的空格
所以说我们可以向cmdline进行无限制的注入
当然会发生重复注入
但是不要怕,我们可以一点点尝试
想到cmdline注入
我们最先想到的就是很著名的aleph的initroot
https://alephsecurity.com/2017/05/23/nexus6-initroot/
还是一个问题,就是我们通过oem命令实际上写入一个uefi变量,而updatecmdline里面会读取这个uefi变量,但是高通的uefi启动时候就把变量标记为易失的,所以写入之后不能持久化重启会失效,那么进入fastboot模式后我们想要继续启动只能通过fastboot continue,而continue会强制正常启动,所以启动到rec/fastbootd是不可能了。
我们可以注入nokaslr关闭内核地址随机化
注入rdinit=
init=
root=
initrd=
等等理论上可以加载自定义的ramdisk实现avb的绕过
但是现实是,现代安卓的启动流程比较复杂
kernel加载init_boot分区中的init,而init_boot和vendor_boot合起来作为ramdisk
如果进入recovery模式,那么这个init会加载第二阶段的init,在vendor_boot分区的/system/bin/init
如果正常启动,会挂载system分区,加载/system/bin/init
所以几次尝试没有成功
initrd=这个参数可以指定从某个内存地址加载initrd,但是问题是abl的加载地址可能不是固定的,我们也不知道具体的地址,尤其是download缓冲区的地址,因为是调用uefi的内存分配器分配的内存
如果root=这个参数那么需要写入分区
既然这几个传统的方式不行,我们还可以尝试关闭dm-verity,但是实测好像关不上,而且关了也需要有权限写入分区才行
然后,我们想到注入prop,cmdline中有很多参数类似于androidboot.verifiedbootstate=green
这样的
实际上,这是注入了ro.boot.xxx这样的prop
这样prop开机后是只读的(当然可以有root情况下通过resetprop修改)
像上面哪个verifiedbootstate如果改成orange,那么开机就会假装展示你的bootloader已经解锁,但是这是假的。
我们想到之前有很多教程教你怎么永久宽容SELinux,其中提到了androidboot.selinux=permissive
这个cmdline,但是正常的安卓标准是不允许在零售的产品上从cmdline读取selinux状态,而应该是强制enforcing
所以测试了一些设备不能够宽容selinux
但是,后来,收到一些其他人的反馈,在某些设备上,init仍然从cmdline读取selinux状态,这导致我们可以宽容selinux。
通过降级,我们可以重新利用这个已经修复的漏洞。
selinux是安卓的一个重要安全组件
至于关闭selinux之后,存在一个安卓的通用提权方案magica
magica能够在selinux宽容的情况下对安卓10+进行提权
magica具体的原理后续写一篇文章分析(咕了x
当然有些网友开发出了利用系统服务/一些组件的高权限实现提权的方案。
以及利用一些其他已经修复的高通GPU漏洞实现内核的任意写入关闭selinux
当然也有很多特定于型号的提权方案,这些本质是OEM代码的缺陷导致,这些都是后续各位佬共同努力的结果(别问我我不会)。
另外一个问题是解锁炸tee,这是问题2
这个问题的根源是我们没有完全按照高通官方的解锁流程导致tee的密钥等等密钥没有被正确的更新
因为解锁后tee似乎不再信任原先的一些密钥等等,这只是一些猜测。
所以解决问题的办法很简单,直接模仿官方的解锁流程重写我们的UEFI APP即可。
第三个问题是OEM修改了解锁检查的逻辑不使用标志位而是使用RSA验证等,那么需要通过GBL正常启动到系统
这个问题就是说,我们需要编译一个GBL使得他能够充当linuxloader的角色,其实这也是GBL的本意。
当然也可以直接修补官方的linuxloader,直接放会crash,想想也行了无限递归
所以至少要patch掉efisp加载的逻辑,具体后面有空再看了(咕了x
所有的漏洞在2月更新全部得到修复。
那么我们来总结一下这次漏洞复现:
首先呢,我们可以看到,这些漏洞都是高通私有源码中的缺陷导致的,也就是说,在没有高通私有源码的情况下,通过逆向工程来确认这些漏洞是困难的,尤其是8E以及以下的一个更复杂的启动链漏洞,而且也是不必要的。通过各种公开途径获取的信息相当有限。而高通会给一部分合作的个人/组织/公司提供一些私有源码,ABL虽然是开源的,但是开源的版本和最终oem使用的版本有很大差异,而UEFI SBL1(XBL)这两个部分完全是高通的私有源码,而这些得到私有源码的个人就能够进行代码审计从而得到可能的漏洞,然后进行盈利从而导致在野利用。
其次可以看到,导致这次漏洞复现的根本原因是存在大量的在野利用,比如说某鱼和酷安上在打算复现之前早以及存在大量的付费解锁等服务。不少甚至价格高昂,达到1000/次的价格
我们的整个复现过程是什么样的呢,其实大概比较早的时候有人就在某比较大的群里说高通新加了一个GBL,当然我本来不是做高通相关的,所以对整个高通的启动流程以及UEFI相关的是完全不熟悉,但是只要提到高通新加了一个东西,那么我们自然而然就能想到这个东西是不是不够安全的,也就是大概率GBL不会经过签名验证,但是我们没有设备也不方便写入分区,所以就一直搁置着。至于高通启动链实际上还存在更上层的漏洞,这些漏洞是通过一些人利用一些权限拿到了高通的私有源码进行审计得到的。后面也是和几个人请教了一下UEFI编译等的相关问题。然后在大量在野利用的情况下,我们进行了漏洞的复现。我们有理由怀疑这是高通故意留下的后门。
后面是看到了高通开源的ABL源码里面的修复内容,进行了cmdline注入漏洞的复现。
从这次复现可以看出,一个是我们要多利用已有的工具进行程序化的分析(包括LLM),第二是没有leak情况下几乎寸步难行,第三是很多看似很安全的东西不一定真的那么安全。
另外,本人是独立的安全研究爱好者,与其他提交或者披露漏洞的组织个人无关
黄粱一梦,终是一空
本文来自博客园,作者:sakrain,转载请注明原文链接:https://www.cnblogs.com/sakrain/p/19686560

浙公网安备 33010602011771号