pwnable.tw->3x17
前言
做Kidding做不下去看了一眼别人wp自闭了,然后开始做这个分值比较低的新题,自闭++,依然是看着别人wp调的,感觉是纯考动态调试的题,这个题告诉我们不要瞅着代码瞎看,动手找很重要
程序逻辑
程序是静态编译的,这意味着代码段的地址是固定的,又是使用ROP的题,这里的函数需要猜一下,其中write和read里进行了系统调用,比较容易识别,strtol()这个搞不懂,不过动态调试的时候可以看到最终的结果是返回输入的数字到rax里,可以猜到是个把字符串转换成整数的函数。程序主要功能是往指定地址写入指定数据
漏洞利用
程序本身就是任意地址写了,漏洞比较明显,重点在于这一次的覆写如何进行,我以前做的类似的题是直接patch掉if成loop多次覆写最终拿到flag,不过那个偏逆向,这里main的代码段是不可写的只得作罢,之后去看wp发现了一个新的东西叫做.fini_arrary,是程序执行完毕之后执行的函数,这个数组里存着两个函数地址,根据
reference,有以下的关键信息
The runtime linker executes functions whose addresses are contained in the .fini_array section. These functions are executed in the reverse order in which their addresses appear in the array. The runtime linker executes a .fini section as an individual function. If an object contains both .fini and .fini_array sections, the functions defined by the .fini_array section are processed before the .fini section for that object.
也就是说这个数组的两个函数以倒序依次被执行,我们可以通过覆写.fini_array的内容来控制执行流,根据这个数组的调用,可以找到实际调用函数的位置,这个函数位于0x402960,这里调用的是[rbp+rbx*8],调用地址为0x402988,我们不妨直接Gdb断点下在此处看看调用的是什么结果。
可以看到在调用前rbx为1,rbp为0x4b40f0即.fini_array,即调用arr[1],继续调试,可以看到rbx变为0之后和-1比较cmp不相等,因此再次执行call [rbp+rbx*8],即调用arr[0],刚好符合我们之前查到的资料。我们第一次覆写的目的是构造一个类似while的闭环,使得我们可以无限次写入,若只是覆写其中一个为main,则只能覆写一次,执行完Main之后就GG。因此我们的初步想法是把arr[1]改为main_addr,arr[0]改为一个调用调用函数的函数地址(0x402960),这样的效果就是调用arr[1]进了main,调用arr[0]又进了调用arr[1]和arr[0]的函数,继续调用main,继续调用0x402960继而调用main…而当我们完成rop的时候,只需要修改arr[0]为rop的起始地址,就可以调用它完成getshell。
ROP的布置
ROP并不像以前那样布置在栈上依次执行,我们的数据需要通过pop_register之类的命令从栈上获取到寄存器里,因此我们需要知道rsp、rbp的情况,这里又得动态去调试。我们先不布置rop,arr[1]为main_addr,arr[0]为leave_ret_adddr(0x401c4b)。断点下在0x401c4b。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20call [rbp]
//rsp = ?,rbp = 0x4b40f0
leave
(mov rsp,rbp;
//rsp = 0x4b40f0 = rbp
pop rbp)
//rsp = 0x4b40f8, rbp = 0x401c4b(leave_ret_addr)
ret(to 0x401b6d,main_addr)
//rsp = 0x4b4100, rbp = 0x401c4b
push rbp;
//rsp = 0x4b40f8, rbp = 0x401c4b
mov rbp, rsp;
// rbp = rsp = 0x4b40f8
....
// rsp = 0x4b40c8, rbp = 0x4b40f8
mov rsp, rbp;
// rsp = 0x4b40f8 = rbp
pop rbp;
// rsp = 0x4b4100, rbp = 0x401c4b
ret(to 0x4b4100)
也就是说最后操作完毕,rsp = rip = 0x4b4100, rbp = 0x401c4b。我们需要修改的就是0x4b4100的内容,布置ROP链,最后让arr[0]为leave_ret_addr即可。注意此时rsp和rip一样,因此可以直接当成栈里的情况布置ROP,一个pop_ret跟一个参数即可。
exp.py
1 | #coding=utf-8 |