アーカイブ

SECCON 2019 Online CTF に参加した (Beeeeeeeeeer, Sandstorm, repair, PPKeyboard)

SECCON 2019 Online CTFにp3r0zとして参加しました。2131点で42位でした。

僕はBeeeeeeeeeerとSandstormとrepairとPPKeyboardを解いたので、その解法を簡単に書きたいと思います。

Beeeeeeeeeer

問題ファイルをダウンロードしてみると、中身はシェルスクリプトのようです。 整形してみても、コマンド置換を使ったり$'string'を使ったりして難読化しています。 良い解き方が思い浮かばなかったので、set -xをして地道に1ステップずつ追っていきました。部分的にset -xを検出してexitするコードや、後半の方でshutdownを呼び出すコードが含まれているので注意しましょう(僕はdockerを使って実行していたので問題ありませんでした)。

最終的に要点だけ抜き出すとこんな感じになります。

Sandstorm

問題ファイルを落とすとこんな感じです。

exifを見てみましょう。

InterlaceとしてAdam7が使われています。画像中でAdamとか書いてあるので、きっとインターレースに何か鍵があるのでしょう。
とりあえずインターレースで読み込む順番を再現してみます。Adam 7アルゴリズムはWikipediaを見るととても簡単に実装できそうです。

実行してみるとこんな感じです。

明らかに8×8で読み込んだとき(一番左上の画像)にQRコードのような模様が出ていて怪しいので、8×8で読み込むピクセルだけを集めた画像を作ります。

というわけで答えはSECCON{p0nlMpzlCQ5AHol6}です

repair

960×540という名前のついたよくわからないファイルが渡されます。 とりあえずstringsにかけてみると、Lavf58.28.100といった単語が出てくるので、おそらく動画や音声のファイルかなという推測します。 他にも、JUNKLISTといったキーワードを調べていくと、これはどうやらRIFF形式のファイルだということがわかります。 RIFFについては調べると詳しい情報がたくさん出てきます。

まずはそのままVLCなどで再生してみようとしましたが、もちろん再生できない。バイナリを眺めると、どうやら動画のヘッダー部分が欠落しているようです。 つまり動画の正しいヘッダーを付け足して、動画の中身を閲覧することができれば答えに近づけそうです。

さて動画のヘッダーを作るには、前述のWebサイトの情報から、フレームのサイズやフレーム数、コーデックなどの情報が必要になります。フレームサイズはファイル名から960×540であると推測します。フレーム数も、動画ファイル後半にあるidx1セクション(動画のインデックスセクション)から257であることがわかりました。問題はコーデックです。

最初はいくつか手打ちでRIFFヘッダーを作って試しましたが、自分のヘッダーが間違っていて再生できないのか、あるいはコーデックが間違っていて再生できないのかわからないので、最終的にffmpegのAPIを使って、半分自動でコーデックを求めて、映像を画像に落とし込むスクリプトを書きました。

このスクリプトのfind_available_codecs関数を呼び出すと以下の結果が得られます。

結構いっぱいあるやん…。とりあえずそれぞれのコーデックでデコードするプログラムを回して(前述のプログラムのmain関数を参照)画像をそれぞれ見たら、cinepakのときに正しくデコードできていました。

PPKeyboard

PE(64bit)とpcapが渡されます。とりあえずpcapを開いてみると、どうやらUSBの通信のようです。
PEも解析してみます。.rdataセクションを見てみると、DDJ-XP1という文字が見つかりました。どうやらDJ用途のMIDIコントローラーのようです。 また関数呼び出しを見てみると、WINMM.dllに含まれるmidiInOpenmidiInStartなどを呼び出している箇所があります。このことから、どうやらこのpcapはこのプログラムとMIDIコントローラーの通信をキャプチャしたものではないかと考えます。

MIDIコントローラーと通信を行うプログラムの解説記事を探してみると、MIDIキーボードとの通信は以下のレイアウトの構造体で行うようです。

capからUSB URBのペイロードを抜き出してみると次のようなデータが得られます。

眺めてみると、前述のNoteOnのパケットと一致するので、これはMIDIコントローラーからの通信と考えて間違いなさそうです。

PE側の解析に戻りましょう。winmmを使ってMIDIコントローラーから信号を受信するには、 midiInOpen関数を呼び出すときの第3引数にハンドラーを指定します[参考]。ここに指定している関数(0x0140001070番地から始まる関数)の中身をPythonコードに落とし込んでみると以下のようになります(参考: MidiInProc callback function – MSDN)。

この処理にあうように、ペイロードを加工&バイトオーダーを入れ替えて渡してあげると、以下の出力が得られます。

あとは普通にデコードしてあげると以下のようになります。

感想

repairで土曜日の夜を溶かしてしまいました(最初のほうで手打ちRIFFと格闘していたら3時間くらい時間を溶かしました、しかも再生できない)。 その後に作り始めた自動化のスクリプトは1時間ほどで作ることができて、さらにPyAVを使うとヘッダーの知識不要がほぼ不要になったので、やはり長いもの(ffmpeg)には巻かれるべきだなと思いました。

あと今回もpwnを解くことができませんでした。復習してちゃんと解けるようになりたいです(毎回言ってる気がする)。