DEFCON CTF 本戦

shinhさんがこんな記事を書いてたので、それに習って僕も書いてみる。

shinh.hatenablog.com

ルールとかが変わったみたいな話は流石にもう有名だと思うので、詳しく解説はしないけど、まあ雰囲気とかは練習にCGC形式の問題解いた動画あるので、それ見るといいかもしれない。

www.youtube.com

slackのログとかを元に出来るだけ時系列で書きつつ、僕は何をしていたかということを書いていきたい

準備

6/26

legitbsのブログで2016年のDEFCON本戦はCGCの形式を用いることが発表される。「パッチ適用してバイナリ提出したら次のラウンドでそのバイナリ他のチームに完全公開されるのやばない?」「難読化しないと差分ですぐpatchバレそうだね」「バックドアが必要かもしれない」という話をする(伏線)。後スコアリング意味不明すぎるなという感想。legitbsとDARPAが用意してるドキュメントが?すぎるのでこれは情報まとめてまだドキュメント読んでないメンバーも把握できるようにすべきだと思ったので、docsを用意した

~7/21

7月半ばまでslackに動きなし。bonoさんやcharoさんやbackerさんなど、メンバーの内何人かが進んで数百個の質問回答が羅列されている?なFAQなどのドキュメントを読み進めてくれていたので、例のdocsには結構情報が書き出されていた。スコアリングとかルールについてチーム内で議論しながら、どんなmitigationが考えられるかなあみたいな話をする。おそらくここら辺でshinhさんがmitigationツールを書き始めてくれる。

7/22

現状何も決まってなくてやばない?wみたいな雰囲気が漂っていたからか、bataさんが「現状決まってないが早急に解決すべき議題」のリストを投下してくれる。具体的には、

  • 役割分担内役

    本番の競技時間中に誰がパッチを当てるか、PoVを書くか、インフラ管理、運営との折衝をするかなど。どれに何人振り分けるかとかも大事。

  • パッチの方針

    脆弱性を見つけた場合、どういう形式で他のメンバーに共有し、どういう形式でパッチを書き、管理するか。最速でミスなくパッチを適用するということはこれまでのDEFCONにおいては至上命題だったので、やはり重要だと思った。

  • PoVの方針

    脆弱性を見つけ、メンバー間で共有できた場合、xml、Cのどちらの形式でPoVを書き、どの形式でメンバーに共有し、どのような形式でマージ、提出するのか。点数を得るためにはPoVは書かざるを得ない上、1つの問題に複数の脆弱性があるA&Dならではの、複数のPoVのマージの方法などの問題がある。

これに関してまた色々議論があって、僕は「バックドア欲しい」という発言をしていたり、それに対して「でも正直うちみたいな弱小チームのバイナリどこもパクらない気がするし、用意する必要なくね?w」という一理ある意見などがあった(この日はこれ以上この話は進まず)。

自チームのPoVが他チームにどれぐらい有効かみたいなのは当然戦略を立てる上で最重要な情報の1つなわけだけど、そこら辺を見るのにダッシュボードが欲しいという話が出る(伏線)。

shinhさんがボイスチャットでミーティングした方がいいのではという提案をする。確かにした方が良いねという話なので24日の夜ぐらいにしましょうと決定。

デコンパイルサーバー欲しくねという話になり、悪ノリでmageさん担当になる。

これは結局有償版IDA持ってる人間は自分のソフトで出来るので、僕は誰か使っていたのかは把握してないんだけど、有償版IDA持ってない人にとっては、curlワンライナーを叩くだけで、シグネチャとかから関数名推測してくれた状態のデコンパイルした結果が取得できて結構便利だったと思う。

この日を境に進捗が結構ある。

7/23

shinhさんのmitigationツールが具体的な形になる。この時点でも既に結構強くなってて、type1 PoVはかなり防げていた。

bataさんがバイナリ内のlibc関数のシグネチャを作ろうとし始める。CGCのlibcはlibcというほどlibcではなくて、問題ごとにフルスクラッチで書かれてるので、効果がどれぐらいあるのかはあの時点では予想できなかった。

docsに書かれている議題の内いくつかに対して独断で暫定案を書いた。

各種ツール使いづらすぎるという話になり、前日のダッシュボードと共にwebuiを作るべきという話になる。tyageがやることになる(デスマの始まり)。

akiymさんが運営から提供されるチーム間のパケットから入力データを抜き出し、バイナリに対してリプレイすることで正常入力系を生成したり脆弱性を探すツールを作り始める。

本番ではこれがすごい大活躍する。

7/24

bataさんがCGCとAFLとかのfuzzerを連携させようとし始める。

Google Hangoutでミーティングする。

確かインフラ構成とかと、議題に関して色々話した。

後PoVをCで書かないといけない可能性が高いので、exploitation用の便利関数のライブラリを作りたいねという話になりbackerさんが作り始める。

すごい便利だった。

7/25

ここで初めてCGC環境立てた。それまで一切実際に触れてなかった。

インフラ構成の図が欲しいという話をするんだけど皆反応が薄かったので自分でやり始める。

運営から情報があり、今まで謎だったパケットの取得方法が判明。なんか決められた運営サーバのポートからUDPで流されるらしい。受け取れなかったらどうしてくれるんだ。

bataさんがバックドア実装してくれる。これが結構すごい。「弱小チームのバイナリをパクってくれるのか?」という疑問があったが、競技終了後に他のチームと話してたら、「君らのチームバックドアあっただろ?最悪だわ」みたいなことを言ってるチームがあったし、まあ効果あったんじゃないかなあ。実際うちのmitigation当てたバイナリが結構脆弱性防いでいたからか、Shellphishが丸パクリしているのは目撃した。

shinhさんがCGCのコマンドのラッパを書いてくれる。上のリンクから見れる動画みたら分かるけどもコマンドが本当に?なので結構助かる。

~7/27

各自色々作業する。僕は比較的仕事がないニートだったので、shinhさんのmitigation bypass出来ないかなーと思って色々試したら結構すぐ出来てしまった。他のチームも似たようなことしてきたらこれ使おうと思った。

7/28

インフラ構成の図をtyageと作る。

ネットワーク構成の図もあったほうが良くね?という話になり、多分ucqさんあたりが作った。

多分これぐらいは公開しても怒られないかなーと思うので貼るけどこんな感じ

f:id:potetisensei:20160821161359p:plain

~7/30

各々作業。悲しいことに僕はすごい暇だったんで、ROP支援ツールでも書くかという感じになって書き始める。凝り性なので本番まで全然完成しないんだけど、本番で実際使う機会あったしすごい便利だった。どういうものかというとこんな感じ。

poteti@ubuntu:~/CGC$ ./rop-investigator/investigate LEGIT_00001
// pop gadget for esp
// gadset 0xa0fcdb0: ----
// 80485dd:
// 80485dd: pop    esp;
// 80485de: and    al, 0x08;
// 80485e0: int    0x80;
// 80485e2: pop    ebx;
// 80485e3: ret    ;
//

// pop gadget for eax, ebx, ecx, edx:
// gadset 0xa3b1860: ----
// 8048600:
// 8048600: pop    edx;
// 8048601: pop    ecx;
// 8048602: pop    ebx;
// 8048603: ret    ;
//
// 80486fd:
// 80486fd: pop    eax;
// 80486fe: ret    ;
//

// pop gadget for eax, ebx, ecx, edx, esi:
// gadset 0xa560db0: ----
// 80485ff:
// 80485ff: pop    esi;
// 8048600: pop    edx;
// 8048601: pop    ecx;
// 8048602: pop    ebx;
// 8048603: ret    ;
//
// 80486fd:
// 80486fd: pop    eax;
// 80486fe: ret    ;
//

// syscall gadget:
// gadset 0xa6dacb0: ----
// 80485e0:
// 80485e0: int    0x80;
// 80485e2: pop    ebx;
// 80485e3: ret    ;
//


// ROP start
buf_t *buf = buf_new();

// transmit(1, flag_addr, read_size, NULL)
buf_p32(buf, 0x80485ff); // pop esi edx ecx ebx
buf_p32(buf, NULL);
buf_p32(buf, read_size);
buf_p32(buf, flag_addr);
buf_p32(buf, 1);
buf_p32(buf, 0x80486fd); // pop eax
buf_p32(buf, 2);
buf_p32(buf, 0x80485e0); // int 0x80;

// exit(0)
buf_p32(buf, 0x8048600); // pop edx ecx ebx
buf_p32(buf, NULL);
buf_p32(buf, NULL);
buf_p32(buf, 0);
buf_p32(buf, 0x80486fd); // pop eax
buf_p32(buf, 1);
buf_p32(buf, 0x80485e0); // int 0x80;

// Extra Stager for bypassing IDS
// If you wanna use this function, please XOR buf->buf by 0xff
buf_t *buf2 = buf_new();

// receive(0, dummy_stack, 0x100, NULL)
buf_p32(buf2, 0x80485ff); // pop esi edx ecx ebx
buf_p32(buf2, NULL);
buf_p32(buf2, 0x100);
buf_p32(buf2, dummy_stack);
buf_p32(buf2, 0);
buf_p32(buf2, 0x80486fd); // pop eax
buf_p32(buf2, 3);
buf_p32(buf2, 0x80485e0); // int 0x80;

// xor(dummy_stack, dummy_stack+0x100, 0xff)
buf_p32(buf2, 0x80486ff);
buf_p32(buf2, pop3_gadget);
buf_p32(buf2, dummy_stack);
buf_p32(buf2, dummy_stack+0x100);
buf_p32(buf2, 0xff);

// stack pivot -> dummy_stack
buf_p32(buf2, 0x80485dd); // pop esp ebx
buf_p32(buf2, dummy_stack);

この時点でもrp++は超えてるし、shinhさんのmitigationに対しても一定の効果はあった。ちなみに今はもっと賢い。後公開する気はさらさらない。

7/31

mageさんがサラッとslack botを作ってくれる。これによって、脆弱性を発見した時、slackへの通知とdocsの準備が一度に出来るようになった。

8/1

2度目のボイスチャットでのミーティング。結構順調そうに見えるかもしれないけど、この時点でダッシュボードは完成してないし、実はPoVとかバイナリの管理に関してはノータッチでマジでやばかった。というか本番もここら辺はなあなあになった。そこら辺について話す。

~8/3

各自作業する。僕は飛行機に乗る。2度目のズートピアを見た。爆睡した。

到着する。皆だんだんと合流し始める。

運営がホテルの部屋を取ってくれてるのは8/4からなんだけど、完全に知らなくてホテルの部屋がなかったので、ucqさんの部屋の床に靴掛け用のタオルを敷いて寝る。

8/4

起きる。皆合流する。Splatoonをした。ROP支援ツール実装やってたけど実装したいところまで実装できなかった。Splatoonのせい。皆27時ぐらいまで起きていて良くなかった。

 

初日

11時準備開始のはずなんだけども、なんかホテルの廊下にDEFCON参加者のクソバカ行列が出来てて人間渋滞みたいなのが発生したせいで遅刻する。当初の予定では11:30から本番開始なんだけど、実際始まったの15時7分とかだった気がする。

それまでは何してるかというと、練習問題みたいなのが1問だけ公開されてて、api叩けるかとかの確認が出来る。

ちなみにその練習問題フェーズの間、legitbsは?なので、事前に公開されていた仕様書には「Digest認証で」と書いてあった所がBasic認証になっていたりして、ダッシュボード書きなおす必要があったり、その認証のid/passが配布されてないやんけと運営に文句言いに言ったら「idはbinja(space)(space)(space)だ。自チームの名前の後に空白文字3つ入れて」と言われて入れてみても認証が通らず、再度言いにいったら別の運営の人が「no space!」って言ってて本当になんなんだという気持ちになった(それで通った)。

本番が始まる。1問目が公開される。なんかtrumpを揶揄する内容だった気がする。

始まって8分ぐらいで自明なbof脆弱性見つけた。20分ぐらいしてtype1 PoV書く。流石にこれは時間かかりすぎ。反省すべき。

それと大体同じタイミングで2問目が出る。なんかWiiで使われてるファイルシステムのパーサー兼データ操作系のプログラムだったかな。全体的に整数値オーバーフローがあるんだけど明らかにExploit書くのは辛くて、放置した。

その後3時間ぐらいは1問目にまだ脆弱性があるという説が浮上して確認したり、2問目解析したり、クラッシュしたパケットログを元に解析とかてんやわんやしてて進捗がなかった。

3問目が公開される。3問目は結構ノータッチだった気がする。

パッチを当てた後も、1問目に対して結構クラッシュするパケットが見つかるのでそれをずっと見てたんだけど、これshinhさんのパッチが不完全じゃねという感じになって調べてたらやっぱりそうだったらしい。でもexploitableには見えないのでまあ良いかと思ったけど、結構時間ロスしたなあ。ちゃんとpatchどんな感じか確認しておくべきだった。

1日目終了。僕個人で言うとこの日はかなりパフォーマンス悪かったけど、チームとしてはリプレイとかを上手く使ってまあまあの立ち回りをしていた気がする。順位は12位とかで最悪な感じの出だしになった。

 

2日目

10時開始。

初めの1時間ぐらいは進捗なかった。

11時ぐらいに4問目が公開される。30分ぐらいして人間fuzzerのmageが4問目で結構criticalっぽいクラッシュを発見していたので、それの解析に入る。30分ぐらいで解析してみて、exploitableという結論は得たんだけど、めちゃくちゃ難しい。2時間ぐらいしてPoV書ける。焦っていたのでtype2 PoVで投げようか迷ったが、type1 PoVで投げる。10チーム/14チームに通る。30分ぐらい掛けてtype2 PoVに書き直す。今思うと意外なことに他のチームはパケットリプレイを上手く使えてなかった様に思えるので、必要なかったかもしれない。4問目、DragonSectorがうちのバイナリをパクっていることに気づく。shinhさんのmitigationは確かに強力だけれども、中身を知ってる僕からするとbypassするのは容易い。30分ぐらいで"DragonSector用"(実はbinjaにも通ってしまう)PoVを書いた。これで11チームに通る。

14時の段階で5問目が追加される。17時ぐらいに前述のPoVに関しては暇になったため、5問目にもfuzzer mageが見つけた脆弱性があったので、解析に入る。30分ぐらい解析して、これきつくね?という感じがしてくる。heap上でbuffer overflowが起きるんだけど、overflowに使える文字種がalnum + アンダーバーで、bofした部分はアドレスとしてfreeの引数として渡される。こんなん無理やんけと思ったものの、おそらくどのチームもunexploitableと判断して修正して来ないだろうから、PoV書けたら結構価値はありそうだと思った。実際うちのチームも含めて、15チーム全チームがこの脆弱性を修正していなかった。1時間ぐらい経つ。2日目終了間際だったものの、結構PoVを書き進められていたが、Heapのアドレスを決め打ちしないとexploitが成功しないという壁にぶち当たって、悩んでいる間に2日目が終了した。CGC環境は"ASLRが存在していないはず"なので、Heapのアドレス決め打ちは本来は問題ないのだが、当然ちょっと考えればmitigationでASLRを追加する事ができる。どのようにやるかというと、プログラムの開始時にallocate(mmapに当たるsyscall)をランダムなサイズで呼んでやればよい。後はheap allocatorが使う領域が勝手にズレてくれて、Heap ASLRが実現される。まあ徹夜したらなんとかなるやろと思い、晩飯を皆で食べた後、部屋に戻ってもこうの動画を見ながら作業する。ちなみに終了時点でうちのチームは8位だった。昨日の12位に比べればマシ。

 

3日目

案の定徹夜した。朝5時ぐらいになっても、Heap ASLRをどうにかする方法は思いつかず、結局Heap ASLRに対応しないままExploitが完成した。僕はHeap ASLRをかなり懸念していたし、PPPとCGCに至っては、Heap ASLRに加えて、アドレスを完全に再配置してASCII ARMORを実装していた。本当にかんべんしてくれ。もこうの動画とか見てる場合じゃなかった。

10時競技開始。

完成したPoVを投げてみると8/14だった。まあまあ、考えていたよりは通っている。

30分ぐらいして、7問目が追加される。6問目が2日目の終盤に追加されていたが、IPCだったので僕は完全にノータッチにすることにしていた。Heap ASLRをbypassする方法が全く思いつかないので7問目を見ることにする。意味分からん難読化がされてる、というかこれはCでは書かれてなさそうだった。すぐに193sがやばそうなクラッシュを見つけたので、それを解析する。なんか自明にtype2 PoVが書けそうだったので、パッチ班に、「mitigationでtransmitの引数をチェックして」という風に言っておく。1時間ぐらいしてPoV書ける。type2 PoVの癖に最後にバイナリをクラッシュさせてしまうので、他のチームにはバレやすいかと思ったが、個人的には自明だと思っていたので、どちらかというとすぐにでも投げたかった。13/14に通る。同じ問題でtyageが別のクラッシュを見つけていたので、それも解析する。未初期化な値を処理に使っているっぽくて、これもtype2 PoVが書けそうな感じがした。12時ぐらいになって、徹夜で書いたPoVが全チームに通らなくなった。つらい。でもこれはどのチームもpatchはしていなくて、Heap ASLRの実装されたバイナリを各チームがパクリあいをした結果なので、僕にもっと実力があってどうにかHeap ASLRをbypass出来ていれば、全チームにずっと通っていたのかもしれない。これはどうにもならないけど惜しいことをしたと思う。30分ぐらいでtyageのクラッシュでPoVを書く。残り2時間は7問目を解析しつつ、任意のアドレスに0xffを1byteだけ書きこめる脆弱性でexploit出来ないか考えてたんだけど、結局何も進捗がないまま終わった。ちなみに8問目が12時ぐらいに公開されたらしいけどノータッチ。終了2時間前に公開はマジで意味が分からん。

 

まとめ

1日目のパフォーマンスがあまりにも悪かったこと以外は、まあまあの立ち回りが出来たのではないかなと思う。ミーティングで決めていた役割分担では僕はパケットからの脆弱性解析ということになっていて、実際にその役目はかなり果たしていたと思うし、自明なしょぼい脆弱性をパケットリプレイやmitigationで対処しているということは、人間がPoVに関してやるべきなのは、exploitableかunexploitableか微妙なラインのめちゃくちゃ難しい脆弱性を対処して、大量得点を狙うことな気がしていて、実際にそうしていたから、まあ間違ってはいないと思う。惜しむらくはやはりHeap ASLRかなあという感じで、あれは今でもunexploitableに思えるんだけど、発見された状態や各チームのパッチ状況的には、それさえbypass出来てれば、2,3個順位は変わっていたのではないかという気がしてならない。とりあえず、全体的な実力がまだまだなのが成績に反映されてるのは明らかで、そこをある程度ツールでカバー出来たのが良かったのではないか。まあツールもかなりケツファイアな状態で作ったからあれでは不完全だけど、来年も出られるとすれば、もっとマシになってくれると信じたい。

 

おまけ

Heap ASLRがbypass出来なかったpov。

LEGIT_00006 pov