【write-up】SECCON BeginnersCTF【[Rev]yakisoba】

前書き

日曜日にCTFを放り出して何をしていたかというと、洋服と靴を買いに行っていました…。来月のカード支払いが恐ろしい…。 土曜日に何をしていたかは秘密です。

write-up

問題文と、ヒントのYou'd better automate your analysisからangrの使用を連想します。angrについて軽く説明しておくと、angrとは、Pythonで記述されたバイナリ解析用フレームワークで、以下のようなことができるとされています。
- 逆アセンブル - シンボリック実行
- コントロールフロー分析etc...
angrの記事はまた別の機会に書こうと思うので、今回はこの問題に特有の部分のみ抜粋して書いていこうと思います。例のごとくfileコマンドで色々と…。 f:id:yamanobori_programing:20200529134027p:plain
ELF-64bitでstriped、stripedではあるものの、IDAでmain関数を確認することが出来る、たまにできたりできなかったりする違いは何なのだろう?逆アセンブル結果を見てみると、mov eax 0xor eax eaxに書き換えられていたり、呼び出し先の関数がスパゲティーコード化されているなど、明らかに難読化されているっぽい…。 f:id:yamanobori_programing:20200529161731p:plain
過去のwrite-upを参考に本問題のwrite-upを作成していきます。参考にしたwrite-upは以下の二つになります。(と言っても少し書き換えただけですが…)
ox0xo.github.io qiita.com
少し書き換えただけのものを一応二つアップしておきます。

import angr

project = angr.Project('./yakisoba')
entry = project.factory.entry_state()
simgr = project.factory.simgr(entry)
simgr.explore()

states = simgr.deadended
for state in states:
    flag = b"".join(state.posix.stdin.concretize())
    print(flag)
import angr

p=angr.Project('./yakisoba')
state=p.factory.entry_state()
sim=p.factory.simulation_manager(state)
sim.explore(find=0x4006d2,avoid=0x4006f7)
if(len(sim.found)>0):
    print(sim.found[0].posix.dumps(0))

これを走らせるとflagを得る。
ctf4b{sp4gh3tt1_r1pp3r1n0}

スクリプトについて

この問題をやってみての率直な感想は、「今までやってたRevの問題って(まぁ自覚はしてたものの)あんまり大したことない問題だったんだなぁ」ってこと。キャンプの立ち話でangrとかmiasmとCTFの話については少ししたのですが、それからCTFのRev問題でangrとか使うような事もあまりなかったことと、キャンプ以降少しセキュリティから離れた世界線を生きていたので、今回の問題はRevの範囲でも今まで触れたことが無い問題で新鮮味がありました。なるべく多くの事を吸収してから次の問題に進みたいと思います。今回参考にしたwrite-upについては、ほぼほぼ使いまわしな感じがするので、とりあえず、この節では、このスクリプト内でどんな動作が行われているかを確認して、次の問題に移りたいと思います。
先のスクリプトについて、以下のコメントの様にまとめ、解説していきたいと思います。

import angr

project = angr.Project('./yakisoba')                   #load
entry = project.factory.entry_state()                   #entry
simgr = project.factory.simgr(entry)                    #simgr
simgr.explore()                                         #exp

states = simgr.deadended                                #deadended
for state in states:
    flag = b"".join(state.posix.stdin.concretize())
    print(flag)
import angr

p=angr.Project('./yakisoba')                   #load
state=p.factory.entry_state()                   #entry
sim=p.factory.simulation_manager(state)         #simgr
sim.explore(find=0x4006d2,avoid=0x4006f7)     #exp
if(len(sim.found)>0):
    print(sim.found[0].posix.dumps(0))

load

angrにおけるバイナリのロードは

project=angr.Project('filepath')

によって行われています。それだけ。

entry

state=p.factory.entry_state()

によって、エントリーポイントにおける実行ファイルの状態を表すobjectを返します。ドキュメントとか見てると、state.regs.ripレジスタの値を取得したりしているので、色々値を取得できるっぽい?

simgr

APIドキュメントによれば、simgrsimulation_managerは全く同じものの様です。(じゃあ何で二つ存在するんだ...)これによって新たなsimulation_managerを構成できます。simulation_managerの概念的な理解は、この図で出きると思います。

f:id:yamanobori_programing:20200531140846p:plain
引用:https://hackmd.io/@K-atc/HJmVUH4Rb?type=view#Simulation-Manager
何となくは理解できるけど、やはりもう少し理解を深めるためにはシンボリック実行自体の理解が必要なように思われる…。

exp

explorメソッドで任意のアドレスに到達して、かつまた任意のアドレスに到達しないような状態を取得することが出来ます。findやavoid以外にも引数をとることが出来ますが、今の僕では説明し切れないのでまた次の機会に…。

deadended

stashはいくつかの状態をとることが出来ますが、deadendedはその中のうちの一つで、何らかの理由でラウンドが継続できなくなったときにこの状態になるようです。(参考にしたスクリプトは探索終了時にそ明示的に探索の終了を表すためにdeadendedを与えているのでしょうか?)
angrやシンボリック実行については、まだよく分からない部分の方が多いので、少しづつ理解を進めていこうと思います。