RWCTF-4th TrustZone challenge Writeup
第四届 realworldctf 我和 @chennan 出了三个题目,分别是 Trust or Not
, UnTrustZone
and Wheels on the Bus
, 其中 Trust or Not
, UnTrustZone
是和 TrustZone 相关的题目。
TrustZone challenge
TrustZone是基于硬件的安全功能,它通过对原有硬件架构进行修改,在处理器层次引入了两个不同权限的保护域——安全世界和普通世界,任何时刻处理器仅在其中的一个环境内运行。同时这两个世界完全是硬件隔离的,并具有不同的权限,正常世界中运行的应用程序或操作系统访问安全世界的资源受到严格的限制,反过来安全世界中运行的程序可以正常访问正常世界中的资源。这种两个世界之间的硬件隔离和不同权限等属性为保护应用程序的代码和数据提供了有效的机制:通常正常世界用于运行商品操作系统(例如Android、iOS等),该操作系统提供了正常执行环境(Rich Execution Environment,REE);安全世界则始终使用安全的小内核(TEE-kernel)提供可信执行环境(Trusted Execution Environment,TEE),机密数据可以在TEE中被存储和访问。
Trust or Not
题目描述:
Trust or Not
Score: 357
1 >Reverse`, `difficulty:normalWe have lost some of our files and cannot retrieve the plaintext data originally stored.
Hint: flag file is stored in
/data/tee/2
securely.
1 >nc 47.242.114.24 7788
TL;DR
要解决这个题目,首先要了解什么是安全存储。 数据要么以某种加密/授权的方式存储在linux文件系统/data/tee
中,要么存储在Emmc RPMB(Replay Protected Memory Block)分区中。这次的相关题目主要使用了 OP-TEE
的开源项目,其更详细的信息可以在OP-TEE文档 中找到。
**Hardware Unique Key (HUK) **
大多数设备都有某种硬件唯一密钥(HUK),主要用于派生其他密钥。例如,当派生密钥用于安全存储等时,可以使用 HUK 派生。HUK 的重要之处在于它需要得到很好的保护,并且在最好的情况下,HUK 永远不应该直接从软件读取,甚至不应该从安全方面读取。有不同的解决方案,加密加速器可能支持它,或者,它可能涉及另一个安全的协处理器。
Secure Storage Key (SSK)
SSK是每个设备的密钥,在OP-TEE启动时生成并存储在安全内存中。SSK用于派生TA存储密钥(TSK)。
1 | SSK = HMACSHA256 (HUK, Chip ID || “static string”) |
获取硬件唯一密钥(HUK)和芯片ID的功能取决于平台实现。目前,OP-TEE 系统中每台设备只有一把 SSK,用于安全存储子系统。但是,为了将来,我们可能需要为每台设备使用生成 SSK 的相同算法为不同的子系统创建不同的密钥。为不同子系统生成不同的密钥的简单方法是使用不同的静态生成密钥的字符串。
Trusted Application Storage Key (TSK)
TA存储密钥
TSK是每个受信任的应用程序密钥,由SSK和TA的标识符(UUID)生成。它被用来保护FEK,换句话说,用来加密/解密FEK。
代码实现:build/optee_os/core/tee/tee_fs_key_manager.c
1 | if (uuid) { |
do_hmac 这里使用的是 HMAC_SHA256
最后就是
1 | TSK = HMACSHA256 (SSK, TA_UUID) |
File Encryption Key (FEK)
当一个新的TEE文件被创建时,密钥管理器将通过 PRNG(pesudo随机数生成器)为TEE文件生成一个新的 FEK,并将加密的 FEK 存储在 meta 文件中。FEK 用于对存储在 meta 文件中的TEE文件信息或块文件中的数据进行加密/解密。
Ideas
通过逆向和比对OP-Tee的源代码,希望选手能发现 HUK
没有被设置。然后flag被加密了且存储在 /data/tee/2
文件里
1 | TEE_Result __fastcall tee_otp_get_hw_unique_key(tee_hw_unique_key *hwkey) |
那么只要分析下安全存储的过程,可以参考如图:
思路就大概是
- 通过
HUK
和chip id
计算出SSK
- 通过计算出来的
SSk
和TA UUID
计算出TSK
- 通过计算出的
TSK
和 被加密的FEK
计算出明文FEK
- 最后通过
FEK
解出明文的数据
其中被加密的 FEK
存储在 /data/tee/2
文件中,可以参考如下 010 tempte结构
1 | //------------------------------------------------ |
Solved
最后脚本如下:
1 | from Crypto.Cipher import AES |
UnTrustZone
题目描述
UntrustZone
Score: 500
1 Pwn`, `difficulty:normalIt is clearly not worth your trust.
The default username is root.
1 nc 47.243.205.105 8899
TL;DR
这个题需要补充一些关于 TrustZone
的另外一部分关于 TA
和CA
的前置知识。 TA
是 Trusted Application 的缩写,通常运行在 TEE 环境下的应用简称为 TA
。CA
是 Client Application 的缩写,通常运行在 REE 环境下的应用简称为 CA。
一个访问安全OS的服务流程为:打开 TEE 环境 > 开启一个会话 > 发送命令 > 获取信息 > 结束会话 > 关闭 TEE 环境。
借助OP-TEE来实现特定安全需求时,一次完整的功能调用一般都是起源于CA,TA做具体功能实现并返回数据到CA,而整个过程需要经过OP-TEE的client端接口,OP-TEE在Linux kernel端的驱动,Monitor模式下的SMC处理,OP-TEE OS的thread处理,OP-TEE中的TA程序运行,OP-TEE端底层库或者硬件资源支持等几个阶段。当TA执行完具体请求之后会按照原路径将得到的数据返回给CA。
Ideas
设计这个题目的时候,就只是想让选手了解下 TA
这个攻击面,所以漏洞设计的得特别简单,就是一个在TA
中的栈溢出,我修改了附件中的HUK
和签名时候的 key 让他保持于远程的不一致。希望选手通过 Pwn 这个 TA, 来获取 安全存储,即 /data/tee/2
下被加密的 flag 。
1 | data_sz = params[1].memref.size; |
Debug
参考: optee-build/debug.md at master · ForgeRock/optee-build (github.com)
- 有 源码调试:
首先对 ldelf 的入口下断, b thread_enter_user_mode
然后执行 CA 程序,在 LOG 窗口中找到 TA 的加载地址
然后对 TA 入口下断, b *(baseaddr + TA_InvokeCommandEntryPoint_addr
无源码调试
OP-TEE 有日志功能,在日志功能中能看到 TA 的加载地址,可以通过这个进行调试
内存布局
Text Address | File Name | Description |
---|---|---|
0x0 | bl1.elf | ARM Trusted Firmware Boot Loader Stage 1 |
0x1070 | libteec.so | OP-TEE Client Shared Library [Normal World] |
0x4009c0 | Client Application [Normal World] | |
0xe01b000 | bl2.elf | ARM Trusted Firmware Boot Loader Stage 2 |
0xe040000 | bl31.elf | ARM Trusted Firmware Boot Loader Stage 3-1 |
0xe100000 | tee.elf | OP-TEE |
0xffff000008081000 | vmlinux | Linux Kernel [Normal World] |
- usermod
1 | user mode内存布局 |
一般而言: ldelf 加载地址是固定的, 处理代码位于 build/optee*os/core/arch/arm/kernel/ldelf_loader.c
ldelf_load_ldelf
函数中, 最后加载的base为 0x40006000, 具体代码可见build/optee_os/core/arch/arm/kernel/ldelf_loader.c
Solved
解题关键是需要了解没法直接解密的时候,我们应该如何读取 flag:
- TEE_AllocatePersistentObjectEnumerator
- TEE_GetNextPersistentObject
- TEE_OpenPersistentObject
- TEE_ReadObjectData
- memcpy data to buffer
首先, ldefl
加载基地址是不变的,我们可以在这上边找 gadget , 另外虽然 TA
有随机化,但是这随机化并不是很高,可以通过爆破解决。所以 TA
的程序也是找 gadget 的目标之一。ldefl 程序的代码段是被通过 ldelf_load_ldelf
函数是写死在 bl32_extra1.bin
中的。
最后我们找到的了几个可以设置 5 个参数的 gadget。
1 | uint64_t CallFun5(TEEC_Session* sess,uint64_t func,uint64_t x0,uint64_t x1,uint64_t x2,uint64_t x3,uint64_t x4) |
Reference
OP-TEE中secure stroage——安全存储使用的key的产生 (daimajiaoliu.com)
OP-TEE Documentation — OP-TEE documentation documentation (optee.readthedocs.io)