DEF CON 30 CTF Quals Teedium Wallet复盘 发表于 2022-06-05 | 分类于 漏洞利用 | 2 条评论 题目是一道关于ARM TEE中Trusted Application的Pwnable题目,其中的TEE OS是[OP-TEE OS](https://github.com/OP-TEE/optee_os),整体架构如下 ![image.png-155.1kB][1] [1]: http://static.zybuluo.com/birdg0/9ksorwhkmxnukfl2ovfzgefx/image.png [2]: http://static.zybuluo.com/birdg0/f5lict3ps0xqh40kg39kf649/%E5%9B%BE%E7%89%87.png [3]: http://static.zybuluo.com/birdg0/giq6iu3ixxi1m1yo7jk69rx2/%E5%9B%BE%E7%89%87.png [4]: http://static.zybuluo.com/birdg0/ihkql2opv9l4xrr8fsq8ttnb/%E5%9B%BE%E7%89%87.png [5]: http://static.zybuluo.com/birdg0/ltuydkq1uarfstic84ume4da/%E5%9B%BE%E7%89%87.png [6]: http://static.zybuluo.com/birdg0/cdflczdhdo7qgl3zlkqx0sar/%E5%9B%BE%E7%89%87.png [7]: http://static.zybuluo.com/birdg0/uvf24ymw7liqf4lic3axl7qe/%E5%9B%BE%E7%89%87.png 题目的源码在[https://github.com/Nautilus-Institute/quals-2022/tree/main/teedium-wallet](https://github.com/Nautilus-Institute/quals-2022/tree/main/teedium-wallet),在[serialize_tx](https://github.com/Nautilus-Institute/quals-2022/blob/main/teedium-wallet/challenge/wallet_ta.c#L845)中调用[serialize_sighash](https://github.com/Nautilus-Institute/quals-2022/blob/main/teedium-wallet/challenge/wallet_ta.c#L725)时没有对原buffer进行realloc,导致可能发生堆溢出,但是只能溢出4个字节,并且是固定值1。 作者已经放出了[Exploit](https://github.com/5lipper/ctf/tree/master/defcon30-quals/teedium),因此主要记录一下调试的过程。调试的难点主要在于OP-TEE OS和TA都开启了ASLR,其中OP-TEE kernel的加载基址是`0xe100000`,按照[https://github.com/ForgeRock/optee-build/blob/master/docs/debug.md#15-debugging-ta](https://github.com/ForgeRock/optee-build/blob/master/docs/debug.md#15-debugging-ta)中的步骤想要调试TA可以先在`tee_thread_enter_user_mode`下断,根据汇编对比确定TEE OS(bl32_extra1.bin)中的`tee_thread_enter_user_mode`在`0xE101CD8`。为了调试可以先patch掉[kernel加载时的aslr种子](https://github.com/OP-TEE/optee_os/blob/697510a3f9de264932082813bdb76fe7a5bf73be/core/arch/arm/kernel/entry_a32.S#L503)。另外Normal World和Secure World都有日志输出,在Secure World的日志中可以得到TA的加载地址,为了获取日志还需要修改qemu的启动参数: ```bash ./qemu-system-arm \ -nographic \ -monitor /dev/null \ -smp 2 \ -machine virt,secure=on \ -cpu cortex-a15 \ -semihosting-config enable=on,target=native \ -m 1057 \ -bios bl1.bin \ -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 \ -no-reboot \ -s \ -S \ -serial tcp:localhost:54320 -serial tcp:localhost:54321 \ #主要是这一行 -fsdev local,security_model=none,id=fsdev0,path=/test/optee_examples/hello_world/host \ -device virtio-9p-device,fsdev=fsdev0,mount_tag=hostshare # 最后两行用于跟host共享目录,guest还需要执行mount hostshare -t 9p /mnt/host ``` 在启动qemu前先启动日志监听`nc -z 127.0.0.1 54320 || python3 soc_term.py 54320`和`nc -z 127.0.0.1 54321 || python3 soc_term.py 54321`。在启动qemu后就可以使用gdb在`0xE101CD8`下断: ![图片.png-342.9kB][2] 执行exp后每次切换到TEE的User Mode时都会在`0xE101CD8`断下来,第一次是ldelf,第二次是题目的TA ![图片.png-549.7kB][3] 从日志获得了TA的基地址后就可以下断对TA进行调试了。 下面分析一下exp,optee的TA中使用的堆管理器是bget allocator,exp中的利用方式是在free时伪造了一个freed的堆块,在进行合并unlink时,把栈上的fp修改到了输入的buffer,然后迁移栈进行rop。 当释放被溢出的堆块时,由于`prevfree`为1,[当前堆块的前一堆块会被认为处于free的状态](https://github.com/OP-TEE/optee_os/blob/697510a3f9de264932082813bdb76fe7a5bf73be/lib/libutils/isoc/bget.c#L983),另外由于bget把分配了的堆块的大小用负数表示,处于释放的堆块的大小用正数表示,因此合并完前一个堆块后,[它的大小是一个非常大的数](https://github.com/OP-TEE/optee_os/blob/697510a3f9de264932082813bdb76fe7a5bf73be/lib/libutils/isoc/bget.c#L996): ![图片.png-633kB][4] [之后在计算后一个堆块时相当于加上负数指向了伪造的堆块](https://github.com/OP-TEE/optee_os/blob/697510a3f9de264932082813bdb76fe7a5bf73be/lib/libutils/isoc/bget.c#L1016): ![图片.png-637.7kB][5] [由于伪造的堆块的bsize大于0因此会认为后一个堆块也处于free状态](https://github.com/OP-TEE/optee_os/blob/697510a3f9de264932082813bdb76fe7a5bf73be/lib/libutils/isoc/bget.c#L1017),把后一个堆块也进行合并,[在unlink时把栈上的fp修改到了输入的buffer](https://github.com/OP-TEE/optee_os/blob/697510a3f9de264932082813bdb76fe7a5bf73be/lib/libutils/isoc/bget.c#L1026),最后迁移栈进行rop: ![图片.png-645.7kB][6] rop调用syscall获取flag,并复制到输出的buffer: ![图片.png-629.4kB][7] 另外TA开启了ASLR的情况下,通过观察发现只有TA本身的加载基址会变,变化的范围应该是通过`get_pad_begin`确定,看默认配置是(0x0-0x80)*0x1000,[ARMv7的User Mode的虚拟地址范围的基址应该是0x100000](https://github.com/OP-TEE/optee_os/blob/697510a3f9de264932082813bdb76fe7a5bf73be/core/arch/arm/mm/core_mmu_v7.c#L646),最后再贴一个TA的内存布局: ``` E/LD: region 0: va 0x00102000 pa 0x0e300000 size 0x002000 flags rw-s (ldelf) E/LD: region 1: va 0x00104000 pa 0x0e302000 size 0x00b000 flags r-xs (ldelf) E/LD: region 2: va 0x0010f000 pa 0x0e30d000 size 0x001000 flags rw-s (ldelf) E/LD: region 3: va 0x00110000 pa 0x0e30e000 size 0x004000 flags rw-s (ldelf) E/LD: region 4: va 0x00114000 pa 0x0e312000 size 0x001000 flags r--s E/LD: region 5: va 0x00115000 pa 0x0e34b000 size 0x001000 flags rw-s (stack) E/LD: region 6: va 0x00182000 pa 0x00001000 size 0x02a000 flags r-xs [0] # TA Code E/LD: region 7: va 0x001ac000 pa 0x0002b000 size 0x00e000 flags rw-s [0] # TA Data E/LD: region 8: va 0x00200000 pa 0x40b90c78 size 0x002000 flags rw-- (param) # 输出buffer E/LD: region 9: va 0x00202000 pa 0x40bbc060 size 0x006000 flags rw-- (param) # 输入buffer ```
鸟神终于又想起了自己的博客密码
哈哈