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

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

HITCON CTF 2014

CTF ていう、セキュリティコンテスト的なやつ、前から少し気になってたんだけど、おすすめしてもらったこともあって初参加してみました。結果は 20 位。大変たのしかった。

リポジトリ: https://github.com/shinh/hitcon-ctf-2014

vash 10

練習的なやつ。いきなりホームディレクトリに flag てファイルがあるので、それの中身を提出してみるが、ダメ。おかしーなーと find とかして他のファイルを探してみるけど、特に何もなく。

そのうちわかるだろう、と、あまり気にせず次へいった。真相は運営のアレコレでまだ submit が動いてなかったらしい。 TLE とか ICFPC とかの脱力運営に慣れてるので、こういうのでハマらない…

rsaha

RSA を解け的な問題。ただし、通常 m^e mod n だけしか教えてもらえないはずなのに、 (m+1)^e mod n もわかる。

これは3次の項が消えるので、 e が phi(n) と互いに素という条件を満たせてなくて、ダメってことかな…と思った。正しいかは知らない。そうだとして、はてこの間違った e を選んだ時の破りかたなんてわからないぞ…と考えてた。

ついでに web service を攻撃する系も見てみるが、よくわからない…と考えてるうちにバイナリ問題が追加されたのでそっちへ。

rsbo 150

ディスアセンブリ見てると心がいやされるなーと。いやされる。なんだか知らんが、おもむろに flag のファイルを読んだあと、メモリをクリアしてから意味不明な入出力をする、という素晴らしいプログラムらしい。

入力の部分、 0x70 確保してから最大 0x80 読むとかしてて、少しだけあふれちゃっているようなので、ありがたく return address を書き換えさせていただこうと思う。しかしあふれてる量が少なすぎるので、これどーしたもんかなーと考えてた気がする。あと RWX な領域もないし、 RELRO も入ってた…と思う。

しかしまぁ案ずるより生むがやすしで、実際はじめてみると、 0x80 読む関数に一段階目でもう一回飛んで、今度は大幅に色々書けるので、 return to libc で適当に open から return した先が引数の stack ぶん捨てて ret するコード片で、そこから read に行って引数捨てて write に飛ぶ、みたいな経路を作ってやった。

都合の良いコード片は ROPgadget というやつを見つけたので、それを使って探してみた。これ便利だな。

あたり前のように書いてるが、人生二度目の buffer overflow で実際に攻撃してみた経験なので楽しかった。

ここで vash も投稿できると気付いて2問。 3:42 (3時間42分経過という意味)

ty 150

rsbo やってる最中に増えてたバイナリ問題をやる。 Aarch64 です。 Aarch64 の objdump とかは入れてあったんだけど、 gcc とか qemu の使う lib とかがセットアップされてなかったので、入れながら他の問題眺めたりとか。

この問題は、なんか入力を受けて、そのコードを実行してくれます、と。ただし、実行する前に /dev/urandom からの入力との xor を取るので、ランダムなコードを実行することになって死ぬ。

ええとどうやったんだっけ。これまたオーバーフローさせれるんで、 /dev/urandom を open した fd の入ってるスタックの中身を 0 に変えてやって、 stdin からゼロを流し込んで xor を no-op 化する、て感じだったと思う。

あとは Aarch64 のコードを頑張って書いてやる感じ。 Aarch64 てことを除けば前の問題の方が難しかったと思う。しかし data 領域が実行可能ってのは、もはや馴染みがなくて、違和感があるっていうか、最初はどうやって protect 外そうとか無駄なこと考える感じになっていた。 6:52

tarmful 128

他の問題にハマってる間に、 tarmful という雑用ぽい問題が増える。単に 1024 回、ヘンなファイル名とか経由しながら圧縮されてるファイルを展開するだけ。適当なスクリプトで解決。 9:32

hop 350

ハマってた問題だと思う。 Windows バイナリがあって、 flag を入力すると Correct! てでるようになってるんだけど、単に strcmp してくれてるわけじゃなくて、入力1文字ごとに jmp table でどこかに飛んで、ってのを繰り返して判定しているらしい。 jmp table の関係を探らないといけない。

さて Windows バイナリですよ…ということで WINE を使っていたものの、イマイチ不便で、 cygwin64 などをセットアップしてみたり、しかし Windows というやつも不慣れで不便だなぁ…となる。このあたりでパスワードをチェックする部分は特にシステムの関数とか呼ばない気がしてた。

それなら比較的お得意のやつですなーと、 Linux 上で Windows バイナリ内のそのチェックする部分をロードしてみてやる。うまく動く。こうなりゃしめたもんと、 jmp table の連鎖になってることとか、そういう構造がすぐわかる。

でー、 jmp table の連鎖からグラフ構造ができたのだけど、しかしこのグラフ構造、最短経路を取れば正解というわけではなく、ちょうど40文字で到達できるパスが正解という感じ。どうすればいいんだこれは、とはまる。

バイナリの内容わかってから解けないのはくやしいなーと思いながら寝る。

起きてから、アレコレとやってるうちに、それぽい単語が頻繁に出たりしてくるので、それを正解と仮定してアレコレ調整…みたいなことをやってると、正解に辿りつく。 15:05

TLE でもこういうのあった気がするなー。

mid

これはハマって解けなかった問題。 ACM ICPC 形式の問題ということで、長い数値列の median を求めよと。ただしメモリ制限が厳しいので頑張ってね、と。

んー pipe かな、てことで pipe にデータを置いてやるプログラムを作る。しばしバグとか取って、んーこれでいいと思うんだけどなーというものが全然通らない。

最初は bss 領域使うとなぜかメモリリミットが実は越えてたとかで、そっちも修正したり。でも最後まで通らなかった。終わったあとの話によると pipe でいいらしいし、 return まで行ってから sleep すると 1 秒余計に消費するので、答えが間違ってたってことですかねえ…しかしテストケースはそれなりにあるんだけど。

polyglot 500

mid や callme 、あるいは他の問題に無駄な時間を使ってるあいだに、 polyglot という問題が追加される。超得意分野なので、俺を誰だと思ってる的な余裕で瞬殺。いや、それなりには苦労した。 Haskell, Python, C は意外と polyglot 力が低い言語なんですよ。

この問題は最初の解答者だったらしく、主催からどんな解答だったん? みたいなメールが来る。このあとたしか寝た。 22:25

Pneu 80

起きて、他の問題にハマって、やる気を失なってる間にクソゲーが増える。クソゲーだが解けそうなのでがんばって解いた。意外と時間がかかった… 30:07

sha1lcode 300

callme と 24 にハマりすぎてたので、他のバイナリぽいの見るか…と見る。なんだこれは得意分野じゃないか。

自由な入力を、 SHA1 してコード領域に置いてから実行する、って話。適当にランダムな SHA1 結果のコードを見てみるも、当然ながら使えそうなものは無い、からもっとちゃんと狙ってやらないといけない。

高度な数学とか使わなくても、適当に探せば 2,3 bytes のコード片は見つかることは、 ShaFuck を潰した時 にもやったので、同じことをやる。

今回は、先頭に次の code chunk の終端 2,3 bytes 前に jmp するコード片と、終端でなんか 1 命令実行するコード片を探した。 36:38

ducky 250

なんか (){}; を使わずに C コードを書けという、どこかで見たような話が…技を借りるぞーということで kik さんの技を使って倒す。 37:34

24 256

いやーハマっていた。与えられた4つの数字と、四則演算子を組み合わせまくって Python3 で eval して、その結果を 24 にするクイズを 24 回解けというもの。

普通に Ruby で書いて、累乗とか // も必要かーと足して、色々足して、でも最後の問題が解けない。最後はヘンな工夫がいるのかなーとずっと考えていた。

しかしどうも結論としては Ruby で書いてたせいで、 Python のシミュレーションが不十分だったらしく、手であれこれやってるうちに、アレっこれ Python複素数出るからこれかな…とかやってるうちに、複素数使わず手で解けた。

反省としては、嫌いでも Python 使いましょう。 39:49

callme 350

長い時間考えてた問題。ほぼ諦めて日曜の深夜に寝て、月曜の朝起きてから、解法らしきものが見つかって、なんとかまにあった。

sprintf での buffer overflow があるんだけど、 stack smashing protector がかかってるので、 return address を書き変えたところで、 ret するまでに __stack_chk_fail する。

しばらく勘違いしてたんだけど、この問題はよく見ると RWX なページはあるし、 RELRO もかかってない。つまりなんでもいいから RWX なとこに飛べばそれで勝ちで、そこが問題の本質なんだなぁとか考えるもわからない。

しょうがないので起きてからやったのは等価ぽい C のコードを書いてみたりして。生システムコールで入出力してるとかが気になったりしたけど、たぶん関係なかった。

でまぁ C のコードながめてると、 sprintf に渡してる書き込み先は stack なんだけど、よく考えると format 文字列自体も stack のだいぶ高位のアドレスなんだよな、と気付く。つまり sprintf で書いてる最中に format 文字列が変化して、当初の予定よりどんどん伸びていく…技が可能だ。つまり、 "%s%s\n\0" みたいな format だったはずなのに、自由に設定できる2個目の %s を解決してみると、 "aaaa%d%d%d.." みたいな感じで書きかわってるーみたいなことができると。

あとは、なんであるか不明機能であるところの %n を使って、 __stack_chk_fail の GOT をいじって RWX な自由な空間に飛ばしてやる。思うに __stack_chk_fail の GOT とか、もうちょい防衛してやってもいい気がするんだけど。

あと、これで死ぬ時に行く先である、 __fortify_fail は stack の argv[0] を出力しようとするから、書き変えてやれば memory exposure になる とか知って少し脱力した。今回 SSP に対する攻撃手段を色々調べていて、今回は全く役に立たなかったというかむしろ混乱したけど、しかし勉強になった。

あとは時間に間に合うかーという感じで、あれこれやって、あわてて shell code 書いて、なんとか通った。 shell code くらいはいい感じのを自分で作っておくと良いんだろうな。 46:50

感想

たいへん楽しかった。普段は守る側…に自分が直接いるわけではなく、しかし守る側の人を比較的近い距離で眺める、みたいなことが多いだけに、知ってる知識を逆側に使えるのは楽しい。曖昧な理解になってるところがハッキリするのも良い。

MD5 とか RSA とか、暗号系も興味があるんだけど、全く手が出なかったのは残念だった。まぁ一人だと時間的にもキツいが、それでも勉強しておきたい感はある。ネットワーク系も手が出なかったんだけど、しかしそっちはそもそも興味があまりなくて…

あと一人は楽しいものの、やはりキツい。問題が増える速度の方が解く速度より圧倒的にはやい。チームでやってる人は、何人くらいでやってるのかなぁ。こういうルールだと、組んでやるのもやってみたくはある。

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