defcon ctf qual 2016

binjaの人たちすごいなと見てた。今回は結構な時間参加できたんだけど、まあひょっとしたら節約できた時間もあるかもね、てくらいの貢献度だったと思う。まあ入れてもらってからまともに時間使って参加した回が無かったくらいの勢いだったから、少しはなんかできて良かった。

b3s23

ライフゲーム15ターン動かした後の状態をshellcodeとして実行します、て問題。空間がループしてないタイプのライフゲームなんで、端っこの方は大変使いにくい…ので最初の命令で端じゃないところに実行飛ばして、あとは適当に、て方針で。うまく使えるジャンプ作るのが結構大変で、適当な形にグライダーぶち当てて都合よく出てくるパターンを探した。

うまいこといって、 read(0, buf, buf) とかが呼べたけど buf をちょっと動かさないとなってあたりで、別の人が1行目だけでできたぽかったので、放棄。終わってからローカルで shellcode 動くところまで作っておいた。

https://gist.github.com/shinh/1891e3f346a1255fc06e8a7cbf63c756

15
       o oo
ooo    oo o


     oo
     o o                         o
     o         oo   oo          o o    oo                             o    o   oo     oo  oo oo
              o  o  oo  o oo     o    o  o   oo oo          oo        o   o o o  o    oo  oo oo
               oo       oo o           oo    oo oo          oo        o    o   oo

kiss

レジスタを全消去の後 RAX RBX RCX RDX はヒープ内の好きな場所指した状態、それで RIP を好きなとこ飛ばせますよって問題。 RSP をまともな値にせんとどうしようもないやろ…ということで RSP をリストアするであろう、 libc の longjmp とか setcontext とかを眺める。 RSP の値の一時退避先が R[ABCD]X なら良いだろう、てことで。まあでも R8 とか使ってて惜しかったりするものの見つからない。

で少し探索範囲を広げて RSP いじってるとこ探すと

   498b0:	48 89 dc             	mov    %rbx,%rsp
   498b3:	5f                   	pop    %rdi
   498b4:	48 85 ff             	test   %rdi,%rdi
   498b7:	74 08                	je     498c1 <addseverity@@GLIBC_2.2.5+0x591> ; [L5010]
   498b9:	e8 22 d9 ff ff       	callq  471e0 <setcontext@@GLIBC_2.2.5>

というのが見つかったので、これ都合良くないですか…と聞くと、他の人が使えそうといいうことで、後はやってくれた。

終わってからこれ何かなーと調べてみると、 makecontext の実装に使ってる、トランポリンにあたるコードらしい。 makecontext て戻り値の書き換えとか使ってるんだな…

crippled

寝て起きて二度寝するかなとその前にケータイを見ると、私が得意そう、と言われてる問題に気付く。やってみてわかったことはこんなもん誰でも解けるってことだけど、まあ簡単そうだったのでやった。

改造した C コンパイラを適当にカンで入力送って、 read(3, buf, 99) を成功させろ、て問題。ただし read を使おうとするとリンクエラー。 write のコードをダンプしてみると EAX セットするところすっとばせば ebx/ecx/edx がセットされつつ int 0x80 してくれて良さそうだったので、適当にアドレス探して終了。なんか足し算が引き算になったり、そういうよくわからん改造がいろいろされてたみたい。

int main() {
  char buf[99];
  void (*fn)()=((int)&main)+26;
  //write(1,fn,555);
  write(1,"hey",3);
  fn(3,buf,99);
  write(1,buf,99);
}

step

特定の4Bの鍵を使うとコードの一部が復元されるぽかったので、適当に総当たりしつつそれっぽいアセンブリが出るのを探した。その後予定があったので脱出。

そこまではクソ簡単で、その後は SIGTRAP が激しく起きつつ2段階目の復元が行なわれて、後は普通の rev ぽい感じ、という感じぽかった。

crunchtime

昔あった pwn の穴をふさいだよ、て問題。コードサイズ小さいので一通り眺めると、リークはたくさんあるけど、どうもexploitableなとこが見つからないなあとぼんやりしている。

他の人がフラグレジスタいじったら文字列命令の挙動変えれる、と言って、それだってことになる。 direction flag ていうやつ、全く知りませんでした。こんなのあったんだなあ…という感じ。このバグは最初の方の実験で引き起こしてて、あれヘンなこと起きてるな、てことは思ってたのに、すっかり忘れててダメだなあ…と思いました。

でまあ他の人がマジメにダンプ読んでるぽかったので、適当に遊んでると、割と簡単に RIP を書き換えれることに気付く。ただし RIP は CRC を計算した結果になってるので…ということで適当に総当たり書くも、ああ毎度違う値計算する必要ある上に時間制限あるからキツいか、と思う。

CRC は Quine 書いた時に、計算で解を出せるよね…?とか思ってたけど、そこらでコード見つからないな、と探す。そのうち他の人が過去の問題で CRC 探しに使った問題を出してきてくれたので、少しいじって今回のパラメータで動くようにする。

あとは、スタックのいじれるところ的に pivot ぽいことしないとだな…とかアレコレしてるうちに、スタックの大きいアドレスもいじれるようにやってた他の人が解いてくれる。

まとめ

書いてみると思ってた以上に何もしてなかった。まあいつもより他の人と少し協力とかできたのは良かったと思う。思うに最初の解析とか方針定まってからの実装とかでは、速度的に貢献できないなっていう。特に前者。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h