Khorina · 2023年09月13日

Android可信执行环境安全研究(二):可信应用漏洞利用

0x00 前言

在第二篇系列文章中,我们将继续通过逆向工程技术来探索TEEGRIS,从而发现并利用其中存在的安全漏洞。

0x01 TA漏洞分析

1.1 Global Platform(GP)接口

正如第一篇文章所述,TA实现了GP接口。其中我们最关注的入口点是TA_InvokeCommandEntryPoint,它在每次客户端应用程序(CA)向TA发送命令时执行。 在GP规范中,记录了该函数的原型。

image.png
TA_InvokeCommandEntryPoint的描述:

该API允许CA最多指定4个参数每个参数都有一个关联的参数类型。

在这份GP文档中同时还列出了可选的参数类型。

image.png
GP参数类型:

假设这里使用了一个参数(因此其类型不是TEE_PARAM_TYPE_NONE),这里主要涉及到两种类型,分别是value和memref。每种参数类型的值被编码为半字节,合并在一起组成了paramTypes参数。

TEE_Param是一个union类型,其定义如下。

image.png
TEE_Param Union定义:

根据参数是value类型还是memref类型,其填充内容也有所不同:

  • (1)value:成员a和b被设置为与CA传递的值完全相同的值。
  • (2)memref:CA将引用传递给其私有内存中的缓冲区。

TEE OS将在TA的地址空间中映射相同的内存,并相应地填充union的buffer和size成员。具体而言,buffer将指向TA内存空间中保证有效的虚拟地址。这样,TA和CA都会有相同物理内存的视图。

从描述中可以明显看出,检查参数类型这一点至关重要。如果TA期望使用memref,但CA却传递了一个值,这样则不能保证buffer或a指向有效的共享内存位置。这可能会导致类型混乱,其中CA可以完全控制TA认为有效的缓冲区指针及大小。

1.2 在TA中的GP检查

考虑到这个检查的重要性,我们确实有必要验证所有TA是否正确实施了这一检查。出乎意料的是,似乎并不是所有TA都有完善的检查。

image.png
TIMA TA参数类型验证:

TA首先正确检查paramTypes参数,如果它们并非预期值,则返回错误。这是符合预期的行为,不存在风险。现在,我们切换到另一个TA,也就是HDCP TA(UUID 00000000-0000-0000-0000-000048444350)。

image.png
HDCP TA参数类型验证:

这个TA完全不包含对参数类型的检查。这些参数可以从输入中获取,并直接传递给main函数。这意味着,可以对这个TA进行类型混淆攻击。

假设我们现在可以将输入参数设置为所需的任何值,我们可以尝试找到一种利用该参数的方法。理想情况下,我们希望利用它来实现任意读写,然后在利用它获得对TA的运行时控制。

在分析了TA中实现的所有命令之后,我们发现可以组合使用其中的三个命令,从而获得读写原语。由于GP API没有定义任何标准命令,因此我们不知道在TA中实现命令的确切目的。它们是特定于TA的,并且不同TA中的相同命令值可能具有完全不同的功能。我们的目标命令是FB、FC和CB。

(1)FB命令:

image.png

这个命令从Android应用程序获取输入缓冲区,将其包装到安全对象中(意味着该缓冲区已经使用TA专用密钥进行加密和签名),然后将安全对象返回给应用程序。

由于类型混淆漏洞,攻击者可以完全控制输入和输出指针。但是,由于输出内容是不可预测的,因此命令本身不能提供非常强大的原语。

(2)FC命令:

image.png

FC命令与FB命令执行相反的操作:它会获取一个安全对象,并将其解包回原始内容。但是,解包的输出不会返回到REE,而是存储在TA内的固定内存位置。

(3)CB命令:

image.png

最后,CB命令获取包含未包装数据的缓冲区,并将其一部分返回给REE。请注意,由于类型混淆漏洞,目标地址也可以设置为指向TA内部内存。

通常,三个命令组合使用的场景如下:

image.png

FB命令接收来自REE的输入,对其进行加密,然后将其返回给REE

REE使用FC命令将其传递给TA,后者将其解密并存储在TA内部内存位置中。

最后,使用CB命令将缓冲区的一部分返回到REE。最终结果是CB命令的输出是FB命令输入的部分副本。

可以通过指定FB命令的输入指向TA内部内存,并指定CB命令的输出以指向REE内存的方式,实现任意读取。

对FB命令输入进行类型混淆攻击以提取TA内存:

image.png

类似地,还可以通过将FB命令的输入设置为REE内存,并将CB命令的输出设置为TA内存的方式,实现任意写入。对CB命令输出进行类型混淆攻击以覆盖TA内存:

image.png

现在,我们就可以在HDCP TA中实现任意读写。由于TA被认为是安全的,并且与不受信任的Android OS完全隔离,因此这已经是我们的一大突破。不过,三星实施了漏洞利用缓解措施,所以这里还有两个问题:

  • (1)由于ASLR,我们不知道TA的确切内存映射位置。
  • (2)因为我们想利用TA提升特权,获得对整个TEE内存的完整访问权限,所以我们可能也希望获得代码执行

1.3 绕过ASLR

如上篇文章所说,ASLR的原理是对代码和数据段进行随机化。这个随机的范围是0到32767之间的随机数乘以4096(页大小)。

我们没有找到能够以某种方式泄露信息(例如指针值)的方法,如果能有这样的信息,我们就可以恢复随机偏移量。

如果我们仅仅是将上述三个命令组合起来,尝试从随机位置读取,会发生什么情况?实际上,TA将会崩溃,在Android中我们将可以在dmesg中看到错误日志。

[ 119.608950] SW> [TEEgris:SCrypto] SCrypto 2.4 is in FIPS approved mode

但是,它不会阻止我们再次尝试与同一个TA进行交互,也不会阻止访问同一个随机地址。这一次,可以看看是否足够幸运,可以命中真实的映射内存。

这个过程可以重复多次,直到映射了我们尝试访问的地址。由于可能的随机数只有32k,因此找到真实的随机数并不是那么困难,通常可以在不到一分钟的时间内完成。尽管这样的效率在漏洞利用过程中并不理想,但还是可以接受的。

另外,实际上我们碰到有效随机数的机会是大于1/320000的,因为我们已经有了任意读取原语,只需要从映射的二进制文件中查找和读取一页即可。在提取内存的内容后,我们可以将它们与已知的二进制内容进行匹配,以检索实际的随机偏移量。

1.4 实现代码执行

在这个阶段,我们已经有了任意读写权限和ASLR绕过方式。在拥有了如此强大的原语之后,利用它们来获取代码执行就不是一个困难的问题。可能会有几种不同的思路,最终我们决定将其与在TA中发现的另一个栈缓冲区溢出漏洞结合起来。

命令TZ_RepeaterAuth_Send_ReceiverId_List_T()在请求缓冲区中接收某些信息,对传递的参数执行一些验证,然后根据设置的HDCP版本调用函数TZ_RepeaterAuth_Send_ReceiverId_List20_T()或TZ_RepeaterAuth_Send_ReceiverId_List21_T()。但是,如下图所示,参数验证的过程仅仅是比较两个攻击者提供的值,也就是request[3]和req_size。这样一来,攻击者就可以构造请求,确保检查通过。

image.png
反编译的TZ_RepeaterAuth_Send_ReceiverId_List_T函数:

在下图中跳转到函数TZ_RepeaterAuth_Send_ReceiverId_List20_T(),我们可以看到将请求缓冲区的内容复制到一个160字节的数组中,该数组驻留在栈中,大小控制为5 * request[3]。这会导致in缓冲区存在栈缓冲区溢出。

image.png
反编译的TZ_RepeaterAuth_Send_ReceiverId_List20_T函数:

这是非常经典的栈缓冲区溢出,我们在其中控制复制的大小和缓冲区内容。最多可以复制1275个字节,这样就有足够的空间来存储Shellcode。但是,TA还使用了栈金丝雀(Stack Canaries)防护,如下图所示,所以漏洞利用就变得困难起来。

image.png
反编译的TZ_RepeaterAuth_Send_ReceiverId_List20_T末尾部分:

由于我们现在有任意读取和ASLR绕过,所以可以轻松读取__stack_chk_guard的值并将其填充到我们的Shellcode中,以便使金丝雀验证通过。我们尝试将所有步骤结合起来,以证明最终可以控制HDCP TA中的程序计数器(PC)。当栈金丝雀设置为错误的值时,我们在dmesg中可以看到如下调试信息:

[38142.232347] SW> [TEEgris:SCrypto] SCrypto 2.4 is in FIPS approved mode

在读取并正确设置金丝雀后,我们将栈上的返回地址替换为0xAAAAAAAA,则会打印如下内容:

[37276.997902] SW> [TEEgris:SCrypto] SCrypto 2.4 is in FIPS approved mode

至此,TA漏洞利用的第一部分就告一段落。我们在HDCP TA中获得了任意读取、写入和代码执行。但是,由于XN,我们只能够复用现有的代码,这一点会在下一篇文章中进行更多分析。

0x02 TA防回滚机制

在研究过程中,我们发现三星当时最新的安全补丁公告中包含了如下内容。三星版本发布公告,涉及HDCP TA参数类型检查:

image.png

似乎已经有研究人员将缺失的参数检查报告给三星,并且得到了修复。但是,修复方法真的是正确的吗?让我们看一下新的TA_InvokeCommandEntryPoint。新HDCP TA中的TA_InvokeCommandEntryPoint:

image.png

该函数似乎对参数类型实施了适当的检查。基于栈的缓冲区溢出漏洞仍然存在,但是如果没有泄露金丝雀的方法,我们就无法对其进行利用。这样一来,我们之前的成果就付之东流了。

但事实并非如此,我们发现,TA只是REE传递给药执行的TEE的代码签名blob。如果REE要求TEE执行旧版本的TA,会发生什么情况?在上篇文章中,我们看到根据TA版本的不同,可以启用防回滚功能。SEC2版本不支持这个功能,而SEC3和SEC4支持。新的HDCP TA标头中指定的版本是什么?我们用十六进制编辑器打开它。

image.png
新的TA标头:

看来新的TA版本仍然是SEC2!这意味着,不会有任何防回滚的机制。随后,我们可以进行验证,将手机的固件升级到较新版本,创建修改后的libteecl.so副本,该副本将会在/data/local/tmp查找TA,而不是/vendor/tee。这样一来,我们可以将旧的TA放在/data/local/tmp中,并让TEE执行旧版本的TA。

实际上,TEEGRIS OS允许加载旧版本的TA,该TA仍然包含可以利用的漏洞。这意味着,即使新版本TA正确修复了参数检查问题,攻击者也可以强制TEE加载旧版本TA,以回到易受攻击的状态。

0x03 总结

在这篇文章中,我们演示了发现并利用HDCP TA中两个漏洞的详细过程,同时还说明了TEE中防回滚机制的重要性。欢迎大家继续关注下一篇文章,我们将详细分析如何提升特权,并获得对整个TEE内存的完整访问权限.

作者:P!chu
文章来源:TrustZone

推荐阅读

更多物联网安全,PSA等技术干货请关注平台安全架构(PSA)专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入PSA技术交流群,请备注研究方向。
推荐阅读
关注数
4551
内容数
127
Arm发布的PSA旨在为物联网安全提供一套全面的安全指导方针,使从芯片制造商到设备开发商等价值链中的每位成员都能成功实现安全运行。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息