Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
SECCON_quals_2019_follow_me.pdf
Search
Xcyba17her
October 29, 2019
0
170
SECCON_quals_2019_follow_me.pdf
an write-up of the problem "follow-me" from SECCON online CTF 2019
Xcyba17her
October 29, 2019
Tweet
Share
More Decks by Xcyba17her
See All by Xcyba17her
CCCamp_CTF__ALLES_CTF__2019.pdf
xcyba17her
0
190
PE(exe)ファイルのRev問を解いてみよう<公開版>
xcyba17her
6
2.4k
Featured
See All Featured
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.5k
4 Signs Your Business is Dying
shpigford
181
21k
Speed Design
sergeychernyshev
25
670
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
Building Adaptive Systems
keathley
38
2.3k
Practical Orchestrator
shlominoach
186
10k
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
The Language of Interfaces
destraynor
154
24k
Code Review Best Practice
trishagee
65
17k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
8
1.2k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
38
1.9k
How to Ace a Technical Interview
jacobian
276
23k
Transcript
SECCON quals 2019 follow-me writeup @__Xcyba17her_
どんな問題? 計算を行えるcalcというelfバイナリと,branchtrace.cpp, calc.traceが与えられる. calc.traceにはある計算式をcalcに与えて問題作成者が実 行したときにcalcがどのようなアドレスを通ったかとい うようなデータがならんでおり,これと同じトレース結 果を出すような計算式を導けという問題.
(最初calcの使い方がわからなかった) 2
branchtrace.cppの内容 calc.traceを出力するためのプログラム? ソースコードを見ても何をしているかさっぱりわからな かったので無視してしまった… 3
calc.traceの内容 json形式のデータが並んでいる. 600行くらいあるので読むの嫌だな~と思ったが,この ファイルがキーだと思い読んでいくことにした. inst_addrがcalc実行時のアドレスを指しており,この通り に追っていけば何かしらヒントが得られると推測した. 4
calc.traceのinst_addrを追う IDAでcalcを開く. アドレスの値が小さく,PIEが有効であるように見える. inst_addrの値だけでは追うことができない 5
calc.traceのinst_addrを追う calc.traceの2行目~5行目にcalc.traceが記録された当時の calcやlibcのベースアドレスと思しきデータがある. {"event": "image_load", "image_name": "/home/tomori/follow-me/build/sample/calc", "image_id":
1, "base_addr": "0x55f6b4d44000", "image_size": "0x1377"} calcのベースのアドレスは0x55f6b4d44000である. IDA上のアドレスは,inst_addrから0x55f6b4d44000を引いたも のになる. 6
calc.traceのinst_addrを追う 1つ目のinst_addrについて調べる. calc.traceの6行目にある{"event": "branch", "inst_addr": "0x55f6b4d445de", "next_inst_addr": "0",
"branch_taken": true} IDAで見るべきアドレスは0x5de 対応するアドレスの命令は,条件付きジャンプ命令jzで あった. これとtrue,falseの2値があるbranch_takenというデータの 存在から,calc.traceの各行のinst_addrとbranch_takenは, 条件付きのジャンプ命令と,ジャンプの有無を表している のではないかと考えた. 7
calc.traceのinst_addrを追う 2つ目(calc.traceの7行目)のinst_addrをみる. inst_addrは0x55f6b4d44f44であるから,IDAで見るべき アドレスは0xf44 対応する命令は,jz命令であった. しかし0x5deのjz命令との関係は見えず.
でもこの調子で読んでいく. 8
calc.traceのinst_addrを追う 6つめ(calc.traceの11行目)以降のinst_addrである 0x55f6b4d44eae,0x55f6b4d44630,0x55f6b4d44620に注目. 0xeaeのjg short loc_ED5をtrue方向に進み,sub_B6E関数に入る と,mallocとcallocを呼ぶ流れになっているが,0x630,0x620は それぞれpltセクションでのjmp命令のアドレスになっている.
これで,calc.traceがcalcのジャンプ命令おきにジャンプの有無 を記録したものという確証がついた. 9
calc.traceのinst_addrを追う この調子で600行読んでいくわけにもいかないので, calc.traceの中でよく通ってるinst_addrがないか探してみる. やけに0x55f6b4d44e87のジャンプ命令を通っており,その後も 似たパターンのアドレスを通っている. 10
calc.traceのinst_addrを追う 0x55f6b4d44e87付近をグラフモードで見ると,sub_B6Eが このプログラムのメイン機能となっている部分であること がわかる. ある文字列について1文字ずつ走査し,それが’,’であるか,数 字であるか,’+’であるか,’-’であるか,’*’であるか,’m’であ るか,’M’であるか,’C’であるかにより処理を分岐させている.
これはコマンドライン引数に指定する数式の計算を行う処理の ためのものであると予想. 11
calcの動作 IPAの試験でよく見る逆ポーランド記法の形の数式 をコマンドライン引数として指定すると計算してく れる. mは2数のうち小さいものを,Mは2数のうち大きい ものを選ぶ演算子. 12
演算子ごとの通過パターン 数字,演算子ごとに分岐先は異なっており,それぞれで 決まった一連の命令が行われる.したがって,calc.trace 中に同じinst_addrの流れやパターンが見られるはず. これらのパターンがわかれば,calc.traceに示された流れ と同じような流れで動かせる入力が見つかるはず. 13
‘,’が入力された場合の流れ 最初のjnz short loc_C0Cのアドレスは, 0xbefになっている.また,最後のjmp loc_E79のアドレスは,0xC13になっている. どの数値や演算子による分岐についても最後 にjmp命令があるのでそれとcalc.traceを組
み合わせて考えればよい. これに合致するcalc.trace上のパターンは, 0xe87 → 0xbe9→ 0xbef→0x93e →0xc13で ある. 14
数字が入力された場合の流れ 最後のjmp loc_E79のアドレスは0xc4fとなっ ている. これに合致するcalc.trace上のパターンは, 0xe87 → 0xbe9→
0xc1c→0xc22 →0xc4fであ る. 15
‘+’が入力された場合の流れ 最後のjmp loc_E79のアドレスは0xca6となっている. これに合致するcalc.trace上のパターンは,0xe87 → 0xbe9 → 0xc1c
→ 0x8dc → 0x8dc → 0x8dc → 0xa0b → 0xa1f → → 0x93e → 0xca6である. 0xa1fを通る回数は2数により異なり,問題の肝.後で考える. 16
‘-’が入力された場合の流れ 最後のjmp loc_E79のアドレスは0xcfdとなっ ている. これに合致するcalc.trace上のパターンは, 0xe87 → 0xbe9→
0xc1c → 0xc58 → 0xcaf → 0x8dc → 0x8dc → 0x93e → 0xcfdである. 17
‘*’が入力された場合の流れ 最後のjmp loc_E79のアドレスは0xd54となっている. これに合致するcalc.trace上のパターンは,0xe87 → 0xbe9 → 0xc1c
→ … → 0xd54である. 0xa1fを通る回数は2数により異なり,問題の肝.後で考える. 18
‘m’が入力された場合の流れ 最後のjmp loc_E79のアドレスは0xdabとなっている. これに合致するcalc.trace上のパターンは,0xe87 → 0xbe9 → …
→ 0xdabである. 19
‘M’が入力された場合の流れ 最後のjmp loc_E79のアドレスは0xe02となっている. これに合致するcalc.trace上のパターンは,0xe87 → 0xbe9 → …
→ 0xe02である. 20
‘C’が入力された場合の流れ 最後のjmp loc_E79のアドレスは0xe56となっている. calc.traceにないので,いまは無視してよい! 21
パターンのまとめ いずれの数字や演算子のパターンでも0x55f6b4d44e87で 始まる. いずれの数字や演算子のパターンでも最後のjmp命令の アドレスで終わる. 1. ‘,’ →
0x55f6b4d44c13 2. 数字 → 0x55f6b4d44e87 3. ‘+’ → 0x55f6b4d44ca6 4. ‘-’ → 0x55f6b4d44cfd 5. ‘*’ → 0x55f6b4d44d54 6. ‘m’ → 0x55f6b4d44dab 7. ‘M’ → 0x55f6b4d44e02 calc.traceにはこれらのパターンが連続しているため, 数字の特定を除けば入力文字列(数式)は求められる! 22
ソルバ1 Pythonプログラムにcalc.traceを読ませ, パターンを読ませて入力文字列(数式)を 特定させる. 現時点で数値は特定していないため一律 で’1’を出力させている. https://github.com/Ciruela-
Xcyba17her/CTF/blob/master/SECCONq uals2019/follow_me/follow_me_solver_ 01.py 出力: 111,111,111,111,111,1111,111,mm-mM- 111,111,111,mm-111,111,111,111,111,- +-M+111,111,111,mm* import json f = open('calc.trace') json_data = json.load(f) inst_addr_list = [] for i in range(len(json_data)): try: inst_addr_list.append(json_data[i]['inst_addr']) except: continue answer = '' for i in range(len(inst_addr_list)): if inst_addr_list[i] == '0x55f6b4d44e87': ia = inst_addr_list[i - 1] if ia == '0x55f6b4d44c4f': answer += '1' elif ia == '0x55f6b4d44c13': answer += ',' elif ia == '0x55f6b4d44dab': answer += 'm' elif ia == '0x55f6b4d44e02': answer += 'M' elif ia == '0x55f6b4d44cfd': answer += '-' elif ia == '0x55f6b4d44ca6': answer += '+' elif ia == '0x55f6b4d44d54': answer += '*' print(answer) input('[END OF PROGRAM]') 23
数値の特定 読み込み文字が’+’または’*’のとき,0xa1fを通る回数が 毎回変わっている.この回数を揃えないとフラグは得ら れない. ソルバの結果から加算は2回,乗算は1回行われるが, calc.traceによると,1回目の加算では4回,2回目の加算 では9回,乗算では7回0xa1fを通っている. 24
加算の仕組み sub_98Cに2つの数値を渡し,一方が0になるまでデクリメント し,もう一方をインクリメントしていくことで和を求める. 関数の第一引数rsiをnum1,第二引数rdiをnum2と呼ぶ. 問題は,数式に”a,b,+”を指定したときに,図のnum1は一体a とbのどちらになるのか.num1に1を足したものが0xa1fを通る 回数となるので解決しなければならない.
25
加算の仕組み 減算ではどうなるか確認する.”a,b,-”を入力数式とする と結果はa-bとなるが,バイナリ内で前ページにおける num1とnum2についてnum1-num2が行われればnum1=a でnum2=bになるし,そうでなければnum1=bでnum2=a であるといえる. 減算を行うsub_A27では,num2-num1が行われている. したがって,
num2=aでnum1=bである.もっといえ ば,”a,b,+”を入力数式とするとき,加算の過程で0xa1f を通る回数は(b+1)回となる. 26
乗算の仕組み “a,b,*”が入力数式であるとする.sub_98Cを利用して0にaを b回足すことで積を表現している. jg short loc_A5D(at 0xa81)を通過する回数は(b+1)回である. 1回aを結果に足し合わせていく中で,sub_98C中の0xa1fを
通過する回数は(a+1)回である. 27
加算と乗算のまとめ “a,b,+”を実行すると,0xa1fは(b+1)回通る. “a,b,*”を実行すると,0xa81を(a+1)回通り,その1回1回 で0xa1fを(b+1)回通る. calc.traceによると,正解の入力数式を指定すると, 1回 目の加算では4回,2回目の加算では9回,乗算では7回
0xa1fを通っている.また,乗算では2回0xa81を通って いる. 再現するには,xを任意の自然数として,1回目の加算で は”x,3,+”が,2回目の加算では”x,8,+”が,乗算で は”6,1,*”という演算が行われていなければならない. 28
数値部分の決定 ソルバ1の出力”111,111,111,111,111,1111,111,mm- mM-111,111,111,mm-111,111,111,111,111,-+- M+111,111,111,mm*”の数値部分をa,b,c,…,rで表し, どのように計算されるか整理する. a,b,c,d,e,f,g,mm-mM-h,i,j,mm-k,l,m,n,o,-+- M+p,q,r,mm*
xとyのうち小さい方を選択する関数をmin(x,y),大きい方 を選択する関数をmaxとする. また,長くなるので中間の結果を格納する変数をx,y,z, 結果をresultとする. x = a - max(b, min(c, d - min(e, min(f, g))))) y = x - min(h, min(i, j)) z = y + max(k, (l - (m + (n - o)))) result = z * min(p, min(q, r)) 29
数値部分の決定 ここで2ページ前の条件を考えると,a,b,…,rは次の条件 を満たさなければならない. 1. n – o = 3
[1つ目の加算に関する条件] 2. max(k, (l - (m + (n - o)))) = 8 [2つ目の加算に関する条件] 3. z = 6 かつ min(p, min(q, r)) = 1 [1つの乗算に関する条件] これだけ満たしていれば,あとはどうでもいい! x = a - max(b, min(c, d - min(e, min(f, g))))) y = x - min(h, min(i, j)) z = y + max(k, (l - (m + (n - o)))) result = z * min(p, min(q, r)) 30
数値部分の決定 1. n – o = 3 n =
003,o = 000で解決.(桁数を合わせないといけないことに 注意.また,上位桁のゼロ埋めができる) 2. max(k, (l - (m + (n - o)))) = 8 k=008とし,(l - (m + (n - o)))が8より大きくならないように, n = 003,o = 000に加え,l=000,m=000などとして解決. 3. z = 6 かつ min(p, min(q, r)) = 1 まず後ろはp=q=r=001で解決.z=6は少し難しいが, max(k, (l – (m + (n – o)))) = 8を考慮し,y = -2であればよい. y = -2にするために,まずa=b=c=d=f=g=000,e=0000にすれば x = 0となり,min(h, min(I, j)) = 2とするためにh=i=j=2とすれ ば y = -2となり,z = 6を満たすことができる. x = a - max(b, min(c, d - min(e, min(f, g))))) y = x - min(h, min(i, j)) z = y + max(k, (l - (m + (n - o)))) result = z * min(p, min(q, r)) 31
数値部分の決定 前のページをまとめると… つまり,入力すべき数式(の一例)は以下のようになる. a=000, b=000, c=000, d=000, e=0000,
f=000, g=000, h=002, i=002, j=002, k=008, l=000, m=000, n=003, o=000, p=001, q=001, r=001 000,000,000,000,000,0000,000,mm-mM-002,002,002,mm- 008,000,000,003,000,-+-M+001,001,001,mm* 32
フラグを出す 問題文にあった形式で問題サーバに送る. フラグは SECCON{Is it easy for you
to recovery input from execution trace? Keep hacking:)} curl -q -H 'Content-Type:application/json' -d "{¥"input¥": ¥"000,000,000,000,000,0000,000,mm-mM-002,002,002,mm- 008,000,000,003,000,-+-M+001,001,001,mm*¥"}" http://follow- me.chal.seccon.jp/submit/quals/0 33
以上 34