XCTF-greeting-150-WP

Posted on Nov 14, 2020

在前天连水pwn三道后我就一直在浪费时间,本来是想先把堆学掉,但是发现是在是有点复杂,想把cs:app的malloc lab和free lab完成大概还是要再花点时间,所以我就先做了这道格式化字符串,总体上也是学习了一下hijack got和hijack fini_array。

我们发现源程序没有开启reload和PIE,没有开reload的话,就可以hijack got

再看一下反编译的源码,会发现虽然无法栈溢出,但是有一个明显的格式化字符串漏洞。但是我们会发现执行完printf之后会直接return,也就是说一般我们用格式化字符串漏洞进行的操作比如泄露内存,劫持got之类的变得无意义了,因为都return了,自然无法进行进一步的利用。所以我们就需要劫持fini_array。

具体的过程,我其实不是特别清楚,这也是欠下的知识点,需要之后学习。与这道题有关的是,在Linux中,程序在main return后会执行fini_array这个数组中的每一个函数指针

那么我们只要把这个数组的第一个元素修改成_start的地址就可以实现多次利用。由于我们有数组的地址和一个可以做到任意地址读写的格式化字符串漏洞,这个是可以实现的。

既然可以做到多次利用,我们就可以考虑下一步了。程序中已有system函数,那么我们只要将某个函数的got表劫持成system函数的地址,并把"/bin/sh\x00"作为参数传入就可以了。

我们来看一下这个猥琐的函数:

看起来他很像一个标准库函数,但实际上他不是,于是我们看一下他的具体实现方法

会发现他首先读入n个字节,然后调用strlen,并以s为参数。同样是一个参数,调strlen的栈帧行为和调用system时是一样的,那么我们只要把strlen的got表地址替换为system的就可以了。

然后我们就可以考虑构造payload。

偏移上,我们发现由于输入的字符串是拼接在Nice to meet you, 这个长18的字符串后的,所以需要在payload前加两个字符来实现内存对齐。

我们知道,system的plt表地址为0x08048490,_start的地址为0x080484F0。fini_array中的参数的高16位,是0x804(这是我猜的,实际上确实是的概率大,不过为了保险还是应该一起覆盖的,但是我在这里就不一起覆盖了),所以我们只要覆盖低16位为0x84F0,就可以实现第二次执行,还需要将strlen的got表劫持为0x0804890,本来我认为高16位也不需要覆盖,但是事实证明是不可以的,必须覆盖,这个原因我还不是特别理解,需要以后学习。然后我们就可以开始计算。

首先需要两个字符实现内存对齐,然后需要三个地址:fini_array的低16位,strlen的高低16位。这里有14个字符,然后还有Nice to meet you, 这里18个字符,总共32个字符。然后我们要把0x804的值覆盖到strlen的高16位,这里就需要0x804-14-18=2020个字符。然后由于0x8490小于0x84F0,所以我们要先覆盖strlen的低16位。

所以就有了payload = 'AA' + p32(strlen_got + 2) + p32(strlen_got) + p32(fini_got) + "%2020c" + "%12$hn" + "%31884c" + "%13$hn" + "%96c" + "%14$hn"后面的31884和96的计算分别为0x8490-0x804和0x84F0-0x8490。

然后我们只要在第二次执行时输入"/bin/sh"就可以直接通过system调用shell了

最后的exp

exp其实很好写,主要还是payload的计算。