这周末熬夜肝了一波SECCON CTF ,做出来一道kindvm,这道题基本上可以说是一道vm的入门题目。个人感觉seccon ctf还是质量很高的,有脑洞题,也有正经的漏洞利用,包括kernel iot 都有,国外的比赛认真打能学到很多东西。虽然我们只得了64名,还需要继续积累吧
首先来看main函数中kindvm_setup 初始化函数中的全局变量。这个vm是32位架构的。
第一个变量 kc,组织结构是这样的:
kc[2]的位置是input_username()的返回值,来看input_username(),malloc了一块0x10的堆空间,将name存储在这里。好像存在一个栈溢出,但是想想就知道不可能这么简单,看汇编代码果然发现了有cannary的验证。无法利用栈溢出。
之后定义了一个mem变量,malloc了0x400堆上地址。这个mem是用来存储数据的,功能基本上是一个简单的栈。
还有一个reg变量,功能类似于寄存器。也是一个0x400的堆上数据。
最后是一个指令表函数func_table
初始化之后,进入input_insn()函数,这个函数是用来输入指令的,指令变量存储在insn中,是一个存储在堆上的0x400的一维数组。
之后再去执行kc[4],也就是func_greeting(),打开kc[3]位置的文件并输出。因此只要输出flag.txt就能得到flag,所以,我们需要找到一个方法任意写,修改掉banner.txt。
因此我们需要找到一个地址,去存放flag.txt,我们可以看到,kc是在堆上的,而且kc[2]上存放的正好就是我们可以控制的name变量地址。但是我们可以控制的地址只有mem和寄存器变量reg,因此,我们需要再找一个方法实现任意写和任意读写。
之后我们再看main函数,kc[1]是一个判断用的变量,初始化是0,变成1退出。之后回执行exec_insn(),这个函数会根据insn,也就是我们输入的指令去执行相应的指令函数。
kindvm定义了一系列函数用于从insn中读取指令数据,分别读8 16 32位。
我们可以看到step()来作为insn的索引值。
step函数:将kc[0]的值返回作为insn指令的索引值,并且递增1。
因此isns和kc[0]相配合即实现了对指令的解析。
exec中我们先读出8位给v1变量,作为func_table的索引,去执行相应的指令函数。
指令insn_load和指令insn_store,分别用来将reg寄存器数组写入mem堆变量中和讲mem写入相应的reg寄存器中。在其中从insn中分别读出一个8位和16位的指令值v2和v3,分别作为reg的索引和mem的偏移。
但是我们可以看到v3的值是一个__int16 v3,有符号的16位整型,因此在
if ( v3 > 1020 )
这个mem溢出的检查中,也是有符号的检查,因此只要将v3变量设置成负数,即可实现对堆上mem之前的地址的读到寄存器变量reg中。实现了任意读。
insn_store则从reg变量中将相应的寄存器变量写入堆中,通过传入负数实现任意写。
而根据我们之前的分析kc全局数组也存储在堆上,并且就在mem之前。因此我们只需要通过构造指令,使v3变成负数,即可实现漏洞的利用。现在看具体的调试信息:
如果要本地调试的话,我们需要先创建对应的hint1.txt hint2.txt hint3.txt banner.txy flag.txt。
看堆上的信息,因为之前的初始化中,将mem的值全部初始化成0x41,因此我们找到mem的位置在堆上偏移为0x38.
kc的位置在堆上的偏移为0x8,0x10 也就是kc[2]存储了name变量,也就是"flag.txt","banner.txt"存储在偏移0x14的位置。至此,我们已经有了所有需要的东西。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2018-10-28 23:32
被obfuscation编辑
,原因: