在强网杯这种省级比赛遇到了这样的题目,就有种中考碰到了微积分题目的感觉。
QEMU aarch64
Challenge Binary Download here
第一个难点就是把程序跑起来,这是一个armv8的程序,比赛时候想了很多方法。包括想到用树莓派甚至是iPhone来跑这个程序来调试,这些想法都不现实,后来还是老老实实搭建QEMU,期间找到了一个开箱即用的Ubuntu虚拟机:Some Qemu images to play with
,跑起来又觉得太卡,输入命令行等回显都要两三秒,更不用说用gdb来调试了。
这两天有时间又重新找了一下aarch64 QEMU虚拟机的搭建方法,然后按照用QEMU运行ARM64版本的Debian8这里的方法搭建了一台Debian系统的。讲道理Ubuntu系统当然是首选,但是可能是网络的原因,载入网络镜像之后虚拟机就启动不了,Debian用起来也没有区别。
参照链接里面的命令把虚拟机的端口转发出来,我除了转发22端口,来多转了一个4444端口,用于连接程序。然后把gdb,pwntools,gef等一系列必备工具装上。(这又是一个痛苦的等待过程,QEMU跑起来真的很慢)
IDA
另外一个令人头大的地方就是如何才能看懂程序的逻辑。这是ARMv8的程序,用的是和x86完全不同的指令集。比赛进行过程中手头上只有6.8版本的IDA,没有办法反编译ARM64的程序,指令集又不熟悉的情况下可谓是雾里看花。后来才在机缘巧合之下陆续搞到了6.9和7.0版本的IDA,终于可以反编译分析程序。
Vulns
UAF 0x1
1 | root@debian:~# ./game |
首先贴上一段程序的运行流程, 可以看到这是一个RPG游戏程序(仙剑奇侠传?),主菜单有几个选项,包括买东西,出城打野,自杀,保存等。也许是自己功力不够,就算反编译了程序也看不出漏洞在哪里,跟着官方的writeup才勉强看出了端倪。
上面是main函数里面判断角色死亡的条件,用的是role->blood & 0x8000000000000000
,也就是判断blood的最高位是否为1,等同于判断blood是否为负数。
而在journey函数里面,判断角色存活的条件是role->blood > 0
,如果发现角色已经死亡(血量<=0),那么就会在这个函数里面调用角色结构体里面的虚表把对象给free掉。
两个判断条件的不一致就导致了一个Use-After-Free漏洞。当角色在journey函数里面血量等于0死亡,程序并不会立即结束,而是返回到main函数,留给我们继续操作的余地。
然后选择save的选项,fopen将在heap中保存file object,而地址就正好是原先role对象所在的地址。而role->coins
所在的地址正好就是现在file->_IO_file_jumps
的地址,通过shop函数就能把file->_IO_file_jumps
的内容给泄露出来,然后就得到了一个libc的地址,可以用于计算偏移地址。
UAF 0x2
得到libc地址以后,在主菜单选择kill yourself来free掉file object,然后选择退出程序,进入了comment函数。
comment函数这里在heap上申请了0x200 bytes空间,正好又落在了之前file object所在的位置,到这里我们已经通过输入评论的内容可以任意控制file object里面的结构。
因为之前并没有对file object进行close操作,在最后调用exit()的时候会调用_IO_Cleanup
来处理file object,最终在_IO_flush_all_lockp
会调用被修改的file objec里面的vtable。这里的利用逻辑和house of orange很类似,都是伪造file object来达到控制IP的目的。
然而据writeup里面所说,这里的libc版本是2.24,引入了vtable check,所以file object的vtable地址并不能随便填。
如果把vtable地址改到0x13f050
的位置,那么_IO_OVERFLOW
就会落在0x13f060->0x650b0
的位置,而我们可以看到只要在fp+232的位置填上我们想要跳转的位置,就能控制IP,这里直接就填上libc里面找到的one_gadget就能get shell。
EXP
1 | from pwn import * |
因为遇到的野怪和它的出招的是随机的,没有办法保证每一次游戏角色都0血死亡,这里只能让角色不停攻击自己,每次消耗10点血,争取0血死亡。如果没法0血死亡进入预定的程序轨道,EXP就会报错bad try。这里的重点在于如何构造一个fake file object 去满足_IO_flush_all_lockp
里面的条件来劫持IP。
Recap
- aarch 64 QEMU搭建和联动调试
- ARM指令集的简要用法
- 通过文件对象的UAF来劫持IP
- 以前老是不明白为什么
fopen
打开文件以后还要close
,现在懂了。