著作一覧 |
例によってアスキードワンゴの嘉平さんにネットワークプロトコルハッカーズガイドをもらった。
この本は、抜群におもしろい。
たかだか300ページ強にこれでもかとネットワークプロトコル(といっても物理層はさすがに無い)の諸要素について解説している。
当然、やたらとすっ飛ばしているので、読者はこの本をガイドとして自力で深堀りする必要はある。
とはいえ、読むだけでもえらくおもしろい。
おもしろいのは、(最初に戻って)これでもかと諸要素について解説しているからだ。本当に奥が深くてネットワークはおもしろい!
無くても良いとはいえ、直接実行するスクリプトはPython、Wireshark(キャプチャを取るので必須)に自力で解析処理を記述するためにLua、サンプルの解析用のちゃちなチャットプログラムにC#を使っているので、これらは読めたほうが良い。(とはいえ、Java、C、Ruby、Perl、PHPあたりをまじめに使っていれば読めるはず)
というか、そもそもおれがRubyを使い始めたのは、C++でネットワークアプリケーションのクライアントやサーバーを書きまくっていたころに、手軽にソケットを叩いてテスト用のクライアントやサーバーを作れるWin32で実行できるスクリプト言語が必要だったからなのだった。というわけでネットワークプログラミングにはC系(今だとGoやRustなのだろうけど)とスクリプト(RubyとかPythonとか)の両方が使えるほうがいずれにしても良いと思う。
で、こちらも考えながら読んでいたりするので、まだ全体の2/3くらいなのだが、途中、セキュリティについての解説がある。最終的にはPKIになるとはいえ、これまた諸要素について乱数、暗号、署名、証明書などについて解説がある。
暗号化の中でブロック暗号化としてECB(使うな)とCBCが出てくる(これは当然なのだが、当然とは言わずに説明してある)。で、ブロックということはブロックに満たない場合にどうするかとしてパディングについて説明が続く。
で、CBC+PKCS#7の弱点としてパディングオラクル攻撃について2ページ程度の説明がある。おそらく、パディングオラクル攻撃について説明することで、CBCに必要となるIV(初期化ベクタ)やパディングについて明確に説明できるからだろう。と同時に、作法としてナイーブに成功失敗をクライアントに返してはならないことの説明にもなる。
というわけで、おもしろいので、以下、パディングオラクル攻撃(パディングを答えを教えてくれる水晶玉として利用する攻撃)のサンプルコードを書いてみた。実装はP.108に書いてあるとおり。下の例はAES-128で暗号化した16バイト1ブロックの暗号文とIVから平文を求める。複数ブロックの場合はIVではなく直前のブロックを利用することになる。
# coding: utf-8 require 'openssl' # 攻撃者は知らない KEY = "\f\xC0\xCE+\x04\xE8\x99Fe\xEF\xE6\xB1\xB9\x93\xDB!".force_encoding('ASCII-8BIT') # 暗号化されたメッセージ(攻撃者は当然知っている) CRYPTED = "\x9B\xF2;\xFD.\x92\x1F\x0F\x9C\x13\xA2\x8B\x9EY\xE5\xF9".force_encoding('ASCII-8BIT') # IVはメッセージと共に拾えるものとする IV = "6\xBCYZ\xA4\xB6\xAD)cat\xFB\x9F\xC9\xC5\xB2".force_encoding('ASCII-8BIT') # 復号の成功、失敗を返すサーバー側のAPI def blackbox(iv, crypted) cipher = OpenSSL::Cipher.new('aes-128-cbc') cipher.decrypt cipher.key = KEY cipher.iv = iv cipher.update(crypted) + cipher.final true rescue false end # paddingを作成するヘルパ def dummy_iv(offset, encoded) ret = ''.force_encoding('ASCII-8BIT') return ret if offset == 1 (1...offset).each do |i| c = encoded[i - 1] ^ offset ret = c.chr + ret end ret end encoded = [] # IV適用前の暗号化ブロック plain = [] # 平文(padding付き) # IVを変形してパディング1から16に相当するIV適用前のブロックを得る (1..16).each do |padding| org = IV[16 - padding] result = [] tail = dummy_iv(padding, encoded) # 総当たりで暗号文とキーにマッチするパティング相当のコードを得る (0..255).each do |x| new_iv = IV[0...(16 - padding)] + x.chr + tail if blackbox(new_iv, CRYPTED) result << x break if result.length >= 2 end end if result.length == 1 plain << (result[0] ^ padding ^ org.getbyte(0)) encoded << (result[0] ^ padding) else # 2回出現するのは実際のパディングと一致する場合 r = result.find {|e| e != org} encoded << (r ^ padding) plain << (r ^ padding ^ org.getbyte(0)) end puts plain.inspect if $DEBUG puts encoded.inspect if $DEBUG end puts plain[plain[0]..-1].reverse.map(&:chr).join()ネットワークプロトコルハッカーズガイド キャプチャ、解析、エクスプロイトの理論と実践 (アスキードワンゴ)(James Forshaw)
ジェズイットを見習え |