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
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
26
2.1k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
250
21k
Fireside Chat
paigeccino
33
3k
Done Done
chrislema
181
16k
Reflections from 52 weeks, 52 projects
jeffersonlam
346
20k
Happy Clients
brianwarren
98
6.7k
We Have a Design System, Now What?
morganepeng
50
7.2k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
10
710
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
Building Applications with DynamoDB
mza
90
6.1k
Automating Front-end Workflow
addyosmani
1366
200k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
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