Exploit and defenses

会社でバイナリの脆弱性を攻撃する方法と、それに対して防御する方法の一般的な簡単な話を適当にしたので、スライドを置いておきます。

http://shinh.skr.jp/slide/exploit/000.html

なにか一般論を書いてたら終わった感。まぁそのぶん基本的なことを網羅できるかもしれません。実際どうやってんの?てのを案外知らなかったりするドメインですし。

CRC32 Quine

Quine Advent Calendar というのがあるのかーと思ったが、特にネタがなかったので一発ネタ。

http://www.adventar.org/calendars/645

$ curl http://shinh.skr.jp/dat_dir/crc32_quine1.txt > x
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100     9  100     9    0     0    344      0 --:--:-- --:--:-- --:--:--   346
$ crc32 x > y
$ diff x y

2種類あります。

http://shinh.skr.jp/dat_dir/crc32_quine1.txt

http://shinh.skr.jp/dat_dir/crc32_quine2.txt

SECCON 2014 予戦

ソロ。30位。今回はちゃんと点数集める方向でやってみよう…と途中まで思ってやってる感じだったので、ちょっとそれにしては、なイマイチ感。

http://ctf.seccon.jp/rank/

なんかエスパー力検定みたいなオーラがそこらじゅうから流れてたなあ感。

Easy Cipher / Welcome to SECCON / Get the key.txt

なんか簡単だった。

SECCON Wars

いきなりヘンな問題をやる。動画の中に QR コードが大部分隠されて流れている。人力とコードをほどほどに組み合わせてこんな画像を作って、あとは手でテキストファイルして、コードで綺麗な画像にして、 Android で読んだ。

http://shinh.skr.jp/tmp/qr.png

Shuffle / Choose the number / Reverse it

覚えてないけど簡単。

Holy shellcode

UTF-16LE で表現したヘブライ語で shellcode 作れと。大変時間かかったけど、面白かった。ヘブライ語はなんかこの文字の後にはこの文字が来れないとかあるみたいで、そのへんのヒントが

http://hebrew.pwn.seccon.jp/nikud/hebrew-utf16le.html

にあった。詳細は覚えてないし、そのへんの仕様をきちんと把握していたかは謎。

8年前くらいに作った

http://shinh.skr.jp/binary/x86_ops.txt

を適当に参照しながらやる。ちょっと悲しくなるくらいまともに使える命令がない。 xchg %eax, %XXX が全て使えるのでどう見ても役に立つんだけど、これをするとヘブライ語的な理由で後に add $XXXXXXXX, %eax がついてくるので、 EAX がぶっこわれる。まあでも EAX を好きなレジスタに送ることは…できる。一度だけ。

では EAX に好きな値を作れないとキツいぞ…ということで色々やってると、 0 で初期化できる命令列を作れた。なんと 22 byte 5 命令で、やっと AL 以外をクリアできる。

and bl, bh
mov eax, 0xd4fb2f05
add eax, 0xfb3005d0
sub eax, 0xd5fb39fb
add eax, 0x05d005d0

AL はまあ操作するオペレーションが十分にあるので、 0-255 は作れるぽかった。メモリ書き込みは stos しか見つからなかったが、 EDI から EBX を引くことはできるので、 EDI をポインタにして書いてくことはできそうだー。

というわけで shell code 作成。なのにそこからが動かないcodered動かない… /bin/sh は動いてるのに、 cat したら fork できんと言って切られる。たぶんリソース制限かなんかが厳しいのかなぁと。 python とか perl 動かしてみたり、色々試して、 open とかする shellcode 書くのは面倒だなぁと思いつつ、 /bin/cat keyword.txt を試すと動いた。

うーんなんのための制約だったのかな。これを含めて exploit は全て相手の環境を推し量る時間がふんだんに必要だったのが残念。あとこの問題はヘブライ語の仕様とか、表が色わけしてあってヒントになってくれてるんだけど、しかし明確に教えてくれない理由はなんなんだろうか…とか。

とはいえこの問題は他と比べて、出題者技術的にがんばったなー感がして、大変良かった。

jspuzzle / REA-JUU WATCH

jspuzzle はこんなの楽勝…と思ってたがなぜかはまっていた。 Holy shellcode 終わって、寝る前にメシ喰ってる間に、アホな見落しに気付いて、頭の中で解ける。

REA-JUU はこういうのやる気おきないなあとポチポチやってたら解けた。

全体的に眠くてしょうがない日だった。

Advanced RISC Machine

ARM 。うーんこのバイナリなんだろうなあ…と思いつつ、環境がわからんので、 Linux でロードするプログラムを作って qemu で動かす。 SVC の付近のレジスタ操作から、 SVC の規約が微妙に Linux と違うなーと思ってたので、そのへんはローダで命令書き換えて対処。

exploit はまあつまんないくらいすぐ見つかるので、 shellcode を書く。できたなーと思って動かすも向こうでは動かない。うーん qemu+オレオレローダとはページの設定とか色々違っても不思議はないなあ…とか思いつつ、元々コードがあるところじゃないところにコードを置いてみたりすると、なんか少し動いてる。

うーんまあ instruction cache フラッシュしてないしなあ…とか思いつつも、あれやこれや。なんか open と write ははっきり動いてて、 open が -1 でない値 (そしてその値は 3 ではない) を返してるのははっきりしてるのに、 read が全然動かない。しょうがないから…と、元々あるコードの getchar を使って読んでやると flag が読めた。なんじゃらほい。。

途中で、 SVC の引数いじったりして syscall の種類を調べてる時のエラーメッセージから、 GDB 付属の simulator だと気付く。 instruction cache とかは実装してないとかコメントで書いてあるけど、どうなってたんだろうな… FD が 3 以外返してたから、なんか改造はされてたんだろうけど。

Decrypt it (Easy)

mtime 見ると srand に渡す引数がわかるという話。

と思ったら2面があった。なにやら数学的に意味がありそうな話だ!でもどうせ平文の最初は SECCON{ だろう、うん3つある暗号文の最初は SECCO だ。あとは N{ と } を固定して全探索…数学はどこへ。

Japanese super micro-controller

SH 。ということは、どうせこれもシミュレータだろう、あたりー。今回はラクちんだなーしかもまたつまんないくらい exploit 自明だなーと shellcode 作る、手元で動く、向こうで動かない。

またかー。

なんか SH の方は、どうもコード空間にコピーしたコードが一切動いてないような感じ。謎だなぁ困ったなぁと思うが、困っててもしょうがないので、自分で一切コード書かず、 ROP だけで処理する感じで対処。なんか ROP に便利な感じのものがいろいろあったので、まぁそういう意図だったのかな…しかしわざわざシミュレータのコードに手を入れるとか、そういうとこだけ工夫するような運営かな…不明。

あと ARM 同様 FD が乱数になってた。かえり値をちゃんと持ちまわるのはちょっと面倒だったので、単に適当な数字をたくさん試すコードを何度かサブミットしてるうちに通った。

まぁ GDB のシミュレータだとわかれば楽勝ですよ。

Let's disassemble

エスパー。エスパーだなぁ、 x86 じゃないなぁ、 CISC だけどさすがに英語圏の人が参加してるから z80 とかそういうのじゃないよねえ。と思いつつ、色んな CISC を試し、 Java bytecode とかも検討したあと、 z80 だと判明する。がー。

だいたい objdump が前提だよなあこれ。あと、 00 が来ると何かえせばいいのかわからなかったんだけど、まぁ 100 回のうちに来ない時にクリアできた。意味不明だ…

ROP: Impossible

頑張ってみたけど解けなかった問題も書いてみよう。これが筆頭、というかこれは解きたかったんだけど、時間勘違いしてて、どう考えても解けない時間しか残らなかったので、 Let's disassemble 見てたのだった。

jmp/call で chain を作るーて趣旨だと思うんだけど、そうだとすると一度やってみたかったんだよな。結構時間をかけてバイナリ内を調べて、使えそうな材料は色々あるんだけど、 call で飛んだ先で、余計なものがスタックにつまれすぎてて、大変むずかしそうに見えた。

感想

エスパーと QR コード多すぎで途中で萎えた感。趣旨わかってないけど、 QR はこれ本当に部分から復元しろってだけの問題なのかな…

static link について

案外、 static link ってわかってないもんです。というかリンカってわかってないもんです。そして案外はまるものです。以下のクイズに答えられるでしょうか。

クイズ1

$ nm main.o  # int main() {}
0000000000000000 T main
$ nm foo.a   # void foo() { bar(); } void baz() {}

foo.o:
                 U bar
0000000000000010 T baz
0000000000000000 T foo
$ nm bar.a   # void bar() {} void baz() {}

bar.o:
0000000000000000 T bar
0000000000000006 T baz
$ gcc main.o foo.a bar.a

最後のコマンドで、何が起きますか?

  • 普通にリンクできる
  • undefined reference to `foo'
  • undefined reference to `bar'
  • multiple definition of `baz'

クイズ2

$ nm main.o  # int main() { foo(); }
                 U foo
0000000000000000 T main
$ gcc main.o foo.a bar.a

クイズ1との違いは main.o だけです。最後のコマンドで、何が起きますか?

  • 普通にリンクできる
  • undefined reference to `foo'
  • undefined reference to `bar'
  • multiple definition of `baz'

クイズ3

$ nm main.o  # int main() { bar(); }
                 U bar
0000000000000000 T main
$ gcc main.o foo.a bar.a

クイズ1,2との違いは main.o だけです。最後のコマンドで、何が起きますか?

  • 普通にリンクできる
  • undefined reference to `foo'
  • undefined reference to `bar'
  • multiple definition of `baz'

クイズ4

$ nm main.o  # int main() { bar(); }
                 U bar
0000000000000000 T main
$ nm foo.a   # void foo() { bar(); }

foo.o:
                 U bar
0000000000000000 T foo
$ nm bar.a   # void bar() { foo(); }

bar.o:
0000000000000000 T bar
                 U foo
$ gcc main.o foo.a bar.a

最後のコマンドで、何が起きますか?

  • 普通にリンクできる
  • undefined reference to `foo'
  • undefined reference to `bar'
  • multiple definition of `baz'

クイズ5

$ nm main.o  # int main() { foo(); }
                 U foo
0000000000000000 T main
$ gcc main.o foo.a bar.a

クイズ4との違いは main.o だけです。最後のコマンドで、何が起きますか?

  • 普通にリンクできる
  • undefined reference to `foo'
  • undefined reference to `bar'
  • multiple definition of `baz'

クイズ6-10 (飽きたので雑な出題)

クイズ1-5のそれぞれのケースについて、

$ gcc main.o foo.o bar.o

として foo.a と bar.a のかわりに foo.o と bar.o をリンクした場合、何が起きますか?

おまけ

クイズ1-5について、

$ gcc foo.a main.o bar.a

$ gcc foo.a bar.a main.o

にした場合、いろいろ挙動が変わりますが、なぜでしょう。 1-10 に正解できる人はこれも正解できると思われるのであまりクイズにする意味は無い感じなので、おまけです。

解答

リンカの .a や .o に対しての挙動について知っていれば、ラクに全問正解できると思います。全問正解できなければ、いくつあってても、あまり大差無くわかってない、という感があります。

正解は…

  • クイズ1: 普通にリンクできる
  • クイズ2: multiple definition of `baz'
  • クイズ3: 普通にリンクできる
  • クイズ4: undefined reference to `foo'
  • クイズ5: 普通にリンクできる
  • クイズ6: multiple definition of `baz'
  • クイズ7: multiple definition of `baz'
  • クイズ8: multiple definition of `baz'
  • クイズ9: 普通にリンクできる
  • クイズ10: 普通にリンクできる

です。いやー何故か偉そうな文体で書いてるから、こういうの間違ってると恥ずかしいから確認した…

説明

まず、リンカのコマンドラインは基本的に左から右に眺めていきます。 .o ファイルがあると、その中にある全てのシンボルをリンクします。クイズ6-8はこの理由で単に baz が2回出現してしまっていて、クイズ9-10は循環参照があるけど両方ちゃんと定義してあるから大丈夫、ということになります。

.a は知らない人にはやや意外性のある挙動を示します。 .o を眺める時、未定義だったシンボルの一覧をリンカは覚えています。 .a に出くわすと、その中の各 .o を眺めてみて、未定義だったシンボルの入ってる .o を全てリンクします。この際、 .o についでに入っている、使用されてないシンボルなんかもリンクされます。

クイズ1では、 main.o を見て未定義シンボルが無いので、 foo.a と bar.a からは何もリンクされず、 main だけのバイナリができます。

クイズ2では、 main.o を見て foo が未定義なので、 foo.o をリンクします。すると bar が未定義なので、 bar.o もリンクします。そして baz が多重定義になります。

クイズ3では、 main.o を見て bar が未定義なので、 bar.o をリンクします。 foo.a はスルーされるので、 main, bar, baz が定義されたバイナリになります。

クイズ4,5 は循環参照です。 4 の方は main.o と bar.o だけを取ってきてしまって foo が未定義となり、 5 では main.o => foo.o => bar.o と芋蔓式に取ってくる必要ができて、無事リンクできます。

循環参照を解決する技

普通、クイズ1-3のようなケースで悩むことはあまり多くはなくて、4-5のような循環参照で悩むことが多いのではないかと思います。これを解決する方法はいくつかあります。クイズ4のケースを例に使います。

$ nm main.o  # int main() { bar(); }
                 U bar
0000000000000000 T main
$ nm foo.a   # void foo() { bar(); }

foo.o:
                 U bar
0000000000000000 T foo
$ nm bar.a   # void bar() { foo(); }

bar.o:
0000000000000000 T bar
                 U foo
$ gcc main.o foo.a bar.a
bar.a(bar.o): In function `bar':
bar.c:(.text+0xa): undefined reference to `foo'
collect2: error: ld returned 1 exit status
素人くさい方法

まず、よく使われているであろう、 undefined reference が出るまでひたすら .a を足していくという技。

$ gcc main.o foo.a bar.a foo.a

個人プロジェクトとかでは別にいいと思います。わかりやすいですし。ある程度大きい規模の開発ではこの方法ではスケールしないんでないかと思います。

-u

シンボル foo は未定義なんだよ、と明示的に教えてやる方法。リンカオプション -u でリンカに未定義なシンボルを教えてやることができます。 -Wl を使うと gcc はリンカにオプションを伝えてくれます。

$ gcc main.o -Wl,-ufoo foo.a bar.a

foo.a と bar.a をセットでライブラリとして提供してる場合で、明確に拾いたいシンボルがわかってる場合は、この方法が一番リンクにかかる時間とかが少なくて良いと思われます。例えば SDL_main のような。ライブラリとかで無い場合はシンボルの一覧を管理するのがめんどくさいと思われます。

--start-group

最初に書いたような、ひたすら .a を足していく、というのを自動的にやってくれるオプションがあります。 --start-group と --end-group がそれ。

$ gcc main.o -Wl,--start-group foo.a bar.a -Wl,--end-group

とすると、未定義シンボルがなくなるまで、 foo.a と bar.a を循環してくれます。昔は濫用すると遅かったようですが、今ではあまり気にならないのではないかな…と思います。

--whole-archive

少し毛色が違うものですが、この .a は .o みたいな感じで全部シンボル拾ってきておくれ、と指示するオプションがあります。

$ gcc main.o -Wl,--whole-archive foo.a bar.a -Wl,--no-whole-archive

ただ、あまりこの用途で使うことは無いと思います。

よくあるのは、 shared object を作る時に使うケースではないかと思います。さっきの foo.a から foo.so を作ることを考えます。

$ gcc foo.a -shared -o foo.so
$ nm -D foo.so
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w _Jv_RegisterClasses
0000000000200830 B __bss_start
                 w __cxa_finalize
                 w __gmon_start__
0000000000200830 D _edata
0000000000200838 B _end
00000000000005e8 T _fini
00000000000004a8 T _init

大変です。 foo が入ってません。これは基本ルールを思い出すと簡単で、 foo.a を眺めた時に、特に未定義なシンボルが無いので foo.a がスルーされたというだけの話です。

こういう、この .a に入ってるシンボル全部入ってる .so 作ってくれーという時に --whole-archive は便利で、

$ gcc -Wl,--whole-archive foo.a -Wl,--no-whole-archive -shared -o foo.so
$ nm -D foo.so | grep foo
0000000000000685 T foo

などと。無事入ってました。

shared object

はまぁ割とみんなが想像するような挙動をするというか、リンカの段階では難しくなくて、ローダの側でやや難しい感じですね。

libc.a

libc.a に入ってる .o が、1関数につきひとつあるみたいな状態なのは、 static link した時のバイナリサイズを最小にするため、というようなことが予想できますね…

はてなブログにおひっこし

なにやらリダイレクトとか含めて、簡単に移動できそうだったので、はてなブログにおひっこししてみました。サイドバーとかついてブログぽくなりました。

動機はエゴサーチで、 shinichiro_h てのが twitter で検索できないんですよねなんか。

CSAW CTF 2014

https://ctf.isis.poly.edu/scoreboard

前回 (HITCON CTF 2014) 参加した時に、これは面白いけど一人でやると真剣にしんどいなーと思ったので、まわりの人を適当に誘ってチームで参加してみました。名前のアイデアが誰もなかったので、僕が適当に決めた nop-or という名前で。由来は自社名。

僕は真剣にやるけど他の人は暇な時に適当でよいよーというスタンスだったんだけど、妙に真剣に参加してくれる人が多くて、結果は13位。予想より良い結果だったと思う。

残った問題は cryptography * 2 と recon * 2 だけ。チームが全部解けてると、 write-up 見なくてもどういう問題でどう解いたのか勉強が終了してて、それも良いなと思った。

メンバーは僕以外は全員初参加で、7人くらいいたんだっけ? ksnctf を紹介してあった人もいたので、少しだけ練習した人もいたけど、そういう人達は一生懸命 "FLAG_" という文字列を探してたりして、むしろ練習は逆効果だったのではないか。

僕は簡単な問題は初参加の人たちが景気付けに解いてくれるだろーという期待のもと、バイナリばかり読んでた。初参加の人が楽しめるようにという少しの配慮と、自分がバイナリだけ読みたいという本音が混じった一石二鳥の作戦だった。あとで思うに別に少しの配慮はいらなかった。

時系列で

saturn (Exploitation 400)

ほどほどに難しそうなやつ、てことで400。もう内容覚えてないけど3つくらいコマンドがあって、1つ目のコマンドで得た情報を2つ目にわたすと良いとかそんなだっけ。超簡単で、なんだこのコンテストレベル低いて聞いてたけど低すぎないか…と思ってたが、後になって思うとほどほどに難しかった。むしろ例年よりは難化していたらしい。

trivia 10 一問を他の人が解いた直後がこれということで、簡単さがしのばれます。というか他の人が起きてくるころには解けてたと思う…

pybabbies (Exploitation 200)

これは他の人がやると良さそうな問題だなーと思ってたら、まさに向いてそうな @phoenixstarhiro が起きてきたので、やってみたら、と勧めてみたらサックリと解いておでかけしに行った。かっこいい。

s3 (Exploitation 300)

ish も見てたのだけど、どうも s3 の方が簡単な気がしてきたのでこっちにシフト。 key value store を CUI で操作する。

あまり理屈はわかってないけど、作ったキーを更新してから表示すると、何故か最初に渡した文字列におもむろに call してくれるという問題だった。やることは簡単だけど、コードがでかくて読むのがめんどくさかったなあと思った。

やること簡単と言いつつ、 x86-64 の shellcode を書くのに少しだけ時間がかかっていかんなと思った。 syscall のかわりに int 0x80 と書いたりとね…

dumpster diving (Forensics 100)

起きてきた @mayahjp が Firefox のコアがーとか言ってる。コアか少し面白そうだな解くのは任せるにしても、問題の雰囲気見てみるかと objdump -s してみる…と flag{...} があって解けてしまった。 "FLAG_" を探していたらしい…

Obscurity (Forensics 200)

@mayahjp がマウスドラッグしたらフラグあった楽勝とか言ってた。おいこれなんで 200 なんだと思った。

why not sftp? (Forensics 200)

@mayahjp が wireshark の使いかたを理解して瞬殺した。おいこれなんで略。

bo (Exploitation 100)

これも strings で瞬殺されてた。実行する必要すら無いてすごい。

Big Data (Networking 100)

@mayahjp が wireshark ながめてログの文字つないでいけば良いと気付いたぽい。今思うとこの人エスパー力問われる問題の悪口言いながらエスパー力問題ばっか解いてるな、ツンデレかな…

eggshells (Reverse Engineering 100)

fork bomb が入ってるので、二人くらいのマシンが破壊される。誰だったかが utils.pyc を strings して終了。 strings で 3 問解けるとか…

psifer school (Cryptography 200)

明らかに簡単な問題が解かれ終わって、 free lunch is over なムードになったころ、少しずつ進めていた omoikane さんが解く。暗号 3 つ詰め合わせって感じ?

ish (Exploitation 300)

ish わからないな、スタックの一部を表示させることはできるんだけど、どうやってもスタックずれないような?と思ってた。少し寝て起きたあと、いややっぱ明らかにスタックの一部を表示させることができるんだから、どっかずらせる場所あるだろうと思ったら意味不明に alloca ぽいことしてる場所があったので、適当にずらせると判明。

わかってからも適切な調整が結構大変だった。まぁ良い問題だったのかなと思うんだけど、コードやけに大きいなぁというのと、 shell 実行したいな…と思うのとで不満気味であった。

csaw2013reversing2.exe (Reverse Engineering 200)

おにゅーのマシンを買ってきたとか言ってた @ttuusskk が別におにゅーじゃない Windowswindbg で step 実行しながらメモリ見てたらあったとか言って解いた。

wololo (Exploitation 400)

地道に iOS 用ぽい ARM コードを gas に喰わせられるように変換できるスクリプトを書いて、あとは実行しながらデータベースを調整していったら解けた。変換なんてしなくても読めばわかる気もするんだけど、動いてるものが無いものを考えるの苦手なんだよな。よく考えるとコード読みとかも gdb 使ってやるとか結構多い。

Julian Cohen (Recon 100)

@mayahjp が起きたらヒントが出てたとかで、アカウント作って解いた。

aerosol can (Reverse Engineering 500)

この問題は大変だけどやるだけの問題に見えて、そのやるだけ度が簡単そうなら解いちゃおう、と僕も見てたのだけど、その大変度合いがすさまじそうだったので、早い段階で放棄した。そしたら @phoenixstarhiro が根性で解いた。正直時間内に解けないかもな、と思ってたので、やはりこの人の根性はすごいなと思った。

greenhornd (Exploitation 400)

いやーしんどかった。バグの位置は教えてくれてるので、ちょっとしたチェックを理解すれば、あとはちょっと ROP してメモリ確保してから shellcode を渡すだけなんだけど、なんせ Windows なんだ…

例のごとく exe を linux のメモリ空間に mmap して実行したいとこだけ実行できるようにしながら、任意コード実行できるようにした。

shellcode は shell-storm で適当に拾ってきた cmd.exe 動かすやつだとうまく使えなくて、まぁ AppJailLauncher てのがシステムコールをいくつかつぶしていて、問題に書いてある通り ReadFile WriteFile だけで解けて趣旨なのかな、と理解した。しかしそんなことはなくて cmd.exe 動かせたらしい…

さてこれのためのコードを書くのはすごく大変だった。とりあえず kernel32.dll 内の関数の位置が EC2 で借りた Windows 2012 Server と同じ CreateFile なら動くよ、ってのを書いてみたが、本番環境では動かない。うーんまあ Windows 8 って言ってるから違うんだろうなぁと。 @ttuusskk が Windows 8 あるとのことで kernel32.dll をくれたのはいいが、どう見てもこれは 64bit バイナリでござる。

まぁ勉強もかねて Export Table 読むコード書くか、と書いた。ただ疲れていたので、無駄に時間がかかった。あと昔同じようなコード書いた記憶があるね…

そんでそれをアセンブリに手で変換。めんどくさいめんどくさい。。できた!と思うも動かない。まぁ Windows のバージョン依存はなくなっているので、自分の Windows 7 マシンで試してみると、たしかにクラッシュしている。

最初は shellcode 実行開始時点で ebp が腐っているという、自分の凡ミスだったんだけど、あとは gas の謎のバグとたたかっていた。なんでクラッシュするのーと windbg で step 実行していくとなぜか call が少しズレる。実際コード見てみるとズレてるので、なんか gas のバグだったんだと思う。 label のまわりに nop を入れまくって回避。 linux 上で windows バイナリ作って、それを windows にコピーして windbg 、ていうイテレーションがめんどくさくてめんどくさくて…

とまあ解けたけど気力が燃えつきた。

xorcise (Exploitation 500)

@tzik_tack が地道にがんばっていた。進捗を聞くと、 return address が書きかえられて、その下の stack に好きなデータを 128 byte だか書けるようになった、とか言っている。えっそれもうおわってるんじゃ…と思ってハラハラしてたんだけど、 greenhornd 終わって燃えつきかけだったので、進捗を共有してた docs を見てみる。

これはすごいなーと感心する。ループの中でループ変数が変わってて、これは大変だ、と思う。しかし感心するけど、ここまで行ったら、んーやはりこの問題終わってるように見えるよ…と手元で同じのを再現してみて、僕が pass 取れるコードを作ると同時くらいに @tzik_tack も終了。

あとはなんか簡単な手続きで flag をもらえたぽい。

hashes (Web 300)

何人かが頑張っていた。主に @mayahjp と @ttuusskk かな。 @mayahjp が jQuery が古いとか言うので、 jQuery脆弱性を僕がぐぐってみたところ、トップに CVE 。見てみると location.hash て書いてある。これじゃね、と言ったら @mayahjp がすごい勢いで色々やって flag をもぎとっていた。

weissman (Reverse Engineering 300)

aerosol can で燃えつきてないのがすごい @phoenixstarhiro が、地道にフォーマットを解釈していって、読みにくい jpg を復元して解いた。いやー感心した。

Fluffy No More (Forensics 300)

これも主に @mayahjp と @ttuusskk が色々探してた。諦めて @mayahjp が寝たあとで、少し僕も見てみるかとファイルを展開とかしてみる。多い!こんなもん見る気起きない、とあきらめる。しかし彼らの残した記録を読んでみると、なにやら不審な pdf の URL がある。

ファイル一つなら読む気力残ってるな、と pdf に決め打ってながめる。そういえば steganography 的なの見てないな、と画像を取り出したくなった。でもよく知ってる画像のヘッダは中に無いぽい。 dff-gui とかいうファイル解析ツールにかけてみる…が、 pdf はサポートされてないぽい気がした。

うーんどういうふうに画像入ってるのかな、と中身を lv でながめてると、どうも圧縮されてるらしい記述がある。そして圧縮されててすごくサイズが小さいところとかもある。これ flag 隠しそうな場所じゃね、と思う。

deflate を自分で展開するのがめんどくさかったので、適当にぐぐって調べてみる。ねむくて珍妙なコマンドラインインターフェイスの理解に時間がかかったけど、展開できた。あとは lv でふたたびながめてると \xXX\xXX と続く不審な文字列。ちょっと見るとこれ ASCII の範囲じゃん、と irb に入れたら flag ゲット。まぁ pdf 取るところまでをやったのがえらいと思う…

cfbsum (Cryptography 300) 解けなかった

@kinaba が最初の方考えてたけどわからないまま歩きに行ってしまったので、少しだけ遊んでみたりした。最初よくわからんなーという感じだったんだけど、 CFB でぐぐると、ああこういうのがあるのかと勉強になった。 initialization vector を guess するのかな、と思って、実際それがあってたぽい?ので、早い段階で CFB というのを理解してやってみていれば…と少し悔やまれる。

Feal (Cryptography 300) 解けなかった

omoikane さんががんばってたけど解けなかったようだった。この手のやつ解けた試しがないから write-up 読もうと思う…

Recon * 2

左はよくわからない。右は dota2 ならゲームっ子として解かねば!と思って色々探したけどわからなかった。

まとめ

チームでやると負担がかなり少ないなあと感じた。一人でやると、バイナリ読みたいけど、みんなが解いてるアレをやってみた方がいいかな…とか気が散るんだよね。というかチームでプログラミングコンテストに参加したのってはじめてな気がしますね…

irbrc を書いたら ruby 1.9 とだいぶ和解できた話

いつも言ってることだけど、 ruby 1.9 からの m17n のせいで、バイナリツールとしての ruby が色々不便になって、参っていました。特に、 'a' の ASCII code を知るのに、

irb(main):001:0> 'a'[0]
=> "a"

とかやるクセがついてて、これで "a" と出るとか、そういう比較的どうでもいいところで参ってました。

しかしまぁどの環境も ruby 1.9 以降になってるわけで、なんとかしないとなーということで、 IRB の inspector いじって色々やってみて、結果として 1.8 時代より便利になって、最近は割と普通に使えている。

https://github.com/shinh/test/blob/master/.irbrc

何をしてるかというと、評価した結果が文字とか数値だった場合に、補助情報を色々出すようにしてある。で、

irb(main):001:0> 'a'
=> a (97)
irb(main):002:0> 99
=> 99 (0x63 'c')
irb(main):003:0> 2**25
=> 33554432 (0x2000000 33M)
irb(main):005:0* 0xf7e04000 + 0x41060
=> 4158935136 (0xf7e45060 4158M)

などなど。最初の2つは文字と数字の変換結果がついでに出てて便利みたいな話。3つ目は 33M など、数字のだいたいの大きさが出ると便利という話。最後のはアドレス計算とかで特に最近こういうのよくやるので、 16 進数出てると便利。

それ以外にも色々歴史的にちょっとずつ足している機能があって、よく使うのだと、

irb(main):016:0> (99999*29999).cint
=> -1295097295 (0x..fb2ce6231)

31bit overflow してる時に、 C の整数だといくつ、ってのが意外とぱっとわからなかったりするので。 cuint もあるけど、まぁそっちはあまり使わない。

irb(main):010:0> asm("xchg eax, edx")
000000 92                                               >.<
000001
=> nil
irb(main):011:0> disasm("\x55\x57\x31\ff")

/tmp/irb_disasm20140904-6135-1vk633j:     file format binary


Disassembly of section .data:

00000000 <.data>:
   0:   55                      push   %ebp
   1:   57                      push   %edi
   2:   31 0c 66                xor    %ecx,(%esi,%eiz,2)
=> nil

ちょっとアセンブルした結果とか、逆アセンブルした結果とかを見たいことがよくあるので。

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