もろず blog

もろちゃんがITに関しての様々なトピックを解説します

SSL3.0 徹底解剖!!! 〜 なぜ POODLE に殺されたのか 〜


f:id:chanmoro999:20090813163806j:plain
©Copyright Yasuhiko Ito


10月の中旬ころに、google のセキュリティチームから POODLE という SSL3.0 の脆弱性が報告されたというニュースを見ました

POODLE によって SSL3.0 の暗号通信が解読できることが実証されたので、今後 SSL3.0 は使わずに完全に捨てましょうという流れになっています


今回改めて調べてみると、SSL は過去にも CBC 暗号を使用した場合の脆弱性がいくつか報告されていました

  • Padding Oracle 攻撃 (2002年)
  • BEAST 攻撃 (2011年)
  • Lucky Thirteen 攻撃 (2013年)


これらの攻撃をなんとかしのいで生き残ってきた SSL3.0 でしたが、今回の POODLE が致命傷となってついに死んでしましました

SSL3.0 の何がマズかったのか、どんな弱点があったのかを見ていきましょう



この記事では
1. SSL3.0 での暗号化
2. CBC 暗号について
3. Padding Oracle 攻撃について
4. BEAST 攻撃について
5. Lucky Thirteen 攻撃について
6. POODLE について
7. TLS1.0 以降は安全なのか
8. まとめ
について説明します


記事の先頭に戻る



1. SSL3.0 での暗号化

SSL3.0 では RC4CBC のいずれかの暗号アルゴリズムを利用して通信内容を暗号化しています

今回解説する SSL3.0 への攻撃の全てで利用されているのが CBC暗号 を利用した際の脆弱性です


BEAST 攻撃が発表された時には、CBC を利用している通信が被害を受ける対象ということだったので 影響を受けない RC4 への切り替えが推奨されていたようです

ですが、RC4 は暗号アルゴリズム自体に欠陥があり解読が可能ということが明らかになりました


国内の CRYPTREC というプロジェクトが、推奨される暗号アルゴリズムのリストというのを公開しています
電子政府における調達のために参照すべき暗号のリスト(CRYPTREC暗号リスト)に対する意見募集の結果


このリストの中で、RC4 暗号は 運用監視暗号リスト に指定されており、

実際に解読されるリスクが高まったことで、推奨すべき状態ではなくなった暗号技術

という扱いになってます


一方、ブロック暗号の 3DES、AES や CBC モードは 電子政府推奨暗号リスト に指定されており、

当該技術の利用を推奨するもの

という扱いになっています


CBC 暗号は SSL3.0 の致命傷になってしまったわけですが、RC4 と違って 暗号アルゴリズムそのものに問題があるわけではないということです



じゃあなんで CBC暗号が問題になっているの??と思いますよね

これからそのカラクリを紐解いていきましょう


記事の先頭に戻る


2. CBC 暗号について

さて、SSL3.0 で問題となっていた CBC 暗号がどういった暗号アルゴリズムなのかを簡単に見て行きましょう

暗号アルゴリズムには ストリーム暗号、ブロック暗号の2つの方式が存在します


このうち、ブロック暗号には モード と呼ばれる以下のような処理方式が存在します

  • ECB (Electric Code Book)
  • CBC (Cipher Block Chaining)
  • CFB (Cipher FeedBack)
  • OFB (Output FeedBack)
  • CTR (Counter)

CBC 暗号と言っているのは、ブロック暗号を CBC モードで利用した場合ということです


ECB 以外のモードは暗号化したデータをいくつかの固定長のブロックに分割してから、ブロック単位に暗号化を行います
分割されたデータがブロック長に満たない場合は、足りない分パディングのデータを付け足します

f:id:chanmoro999:20141029013511p:plain


CBC の暗号化フローは以下のようになっています
f:id:chanmoro999:20141029013529p:plain

復号化は全く逆のフローにするだけです
f:id:chanmoro999:20141029013539p:plain


暗号アルゴリズムという所に DES や AES といった暗号アルゴリズムが当てはめられるので、CBC モード自体は暗号アルゴリズムそのものとはちょっと違います

この CBC 暗号についても詳しく書きたいところですが、本来の趣旨とちょっとズレてしまうので割愛します

知らない人は調べてみてください!
暗号利用モード - Wikipedia



要は、暗号アルゴリズムへの入力に XOR によるスクランブルをかけていて、復号化の時はその逆、というのが CBC のポイントになります
これにより、暗号化した後の文字列が毎回同じになることを防いでいます



以上が CBC 暗号のざっくり説明です



これから紹介する攻撃は、この CBC 暗号の性質を上手く利用して暗号を解読することに成功しています

それでは、SSL3.0 に対する攻撃方法を見て行きましょう


記事の先頭に戻る


3. Padding Oracle 攻撃について

2002 年に CBC 暗号のパディングを利用した解読方法を示したレポートが発表されました
https://www.iacr.org/archive/eurocrypt2002/23320530/cbc02_e02d.pdf


CBC の性質を上手く利用すると暗号化されたデータを解読できてしまう!という内容です

このレポートがどんなことを言っているのか見ていきましょう



RFC で定められている CBC の仕様では、パディングに使う文字列はパディングの長さと同じ値をセットすることになっています

パディング長が1であれば末尾に1の文字列がセットされ、パディング長が2であれば末尾に22 の文字列がセットされます
333、4444 というようにパディングの長さに応じてセットされる値が決まります

RFC 2040 - The RC5, RC5-CBC, RC5-CBC-Pad, and RC5-CTS Algorithms
※7.6 Final block processing にパディングの仕様が定義されています


そして、パディングが改ざんされたかどうかをチェックする仕組みはここには入っていません


それでは、レポートに書かれている攻撃の方法を見ていきます


解読したい暗号文から対象のブロック {C} を抜き出して、攻撃用ブロック {y} をくっつけた {y\ |\ C} をサーバーに送る処理を考えます

f:id:chanmoro999:20141104014057p:plain

ここで、{y} の末尾の 1byte には "トライする値 XOR 1" の値をセットしておきます


もし、トライする値が {D_k(C)} と同じ値だった場合、{C} を復号した結果である {p} の末尾が 1 になります
※同じ値で XOR をかけると 0 になるという性質を利用しています


このときサーバー側ではパディングの完全性をチェックできないので、この末尾が元から1なのか、改ざんされた結果1になっているのかを区別できません

正常なパディング値と判断してしまい、パディングチェックがOKと返却してしまいます

これにより、トライした値が正解だったというのが攻撃者にわかってしまいます

f:id:chanmoro999:20141104014113p:plain


そして、もともとの暗号文からブロック {C} の1つ前のブロックとの XOR をとることで、目的の値を得る事ができます

あとは 1byte づつ解読の対象をずらして、同じようにトライすれば 1byte づつ簡単に解読できてしまうことがわかります

f:id:chanmoro999:20141104014414p:plain



ですが、攻撃のためにはクライアントを遠隔操作して任意のリクエスト送信させたり、通信パケットを全て傍受できる必要があります
また、SSL で利用される CBC暗号には本文の改ざんをチェックするハッシュ値が付与されているので、攻撃ブロックの影響がパディング以外の部分に及ぶと別のエラーとなってしまいます

この攻撃を成立させるにはいろいろハードルがありすぎて実際には実行できないよね、ということで当時はすぐに問題となるものではなかったようです



ちなみに、Oracle という単語の意味は、"神様のお告げ" 的な意味でした
※あの世界的パッケージベンダーのことではないようです

暗号化された文字列のパディングチェックを行う処理は第3者からはその中身が全くわからないので、
"神様のお告げ" ということなんだと勝手に解釈しました


記事の先頭に戻る


4. BEAST 攻撃について

SSL3.0 で暗号化された通信の中から、 Cookie の値を解読することに成功したのが BEAST 攻撃です
https://bug665814.bugzilla.mozilla.org/attachment.cgi?id=540839


Padding Oracle 攻撃と違って サーバーからのレスポンスを元に解読するのではなく、暗号化されたブロックを比較して平文を解読する攻撃です
暗号文の値を比較し、一致する暗号文が得られるまで攻撃を繰り返すことで解読します

また、BEAST 攻撃では解読の対象を cookie の値に絞っています
これによって、cookie に保持しているセッションID が盗聴され、アカウントを乗っ取るような攻撃に発展されることができます



本来 CBC 暗号では、毎回同じ暗号文にならないようにするために 初期化ベクトル {IV} を使ってデータにスクランブルをかけているので、こういった攻撃は成立しないはずです


ですが、SSL3.0、TLS1.0 にはここに重大な欠陥があり、前回の通信の一番最後の暗号ブロックを次回の {IV} として再利用するというプロトコル仕様になっています
つまり、通信を傍受できていれば次に利用される {IV} の値が簡単に分かってしまうんです

そのため、 {IV} を打ち消す値を先頭のブロックに XOR しておくと、1ブロック目だけはスクランブルを無効化することが可能になります
これにより、暗号化アルゴリズムだけを攻撃の対象にすることが可能になります


どういうことなのか、仕組みを説明します

f:id:chanmoro999:20141104015942p:plain


解読したい値を {P3} 、トライする値を {P'3} とします
そして、{IV} の値は今まで傍受した通信の内容から分かっています

次におくるリクエストの値を操作して

{Pi=IV⊕C2⊕P'3}

が先頭のブロックになるようデータを送信します


{Pi} が暗号化されたデータを {Ci} とすると、

{Ci=IV⊕Pi\\\ \ \ \ =IV⊕IV⊕C2⊕P'3\\\ \ \ \ =C2⊕P'3}

となります

もし、トライした値が解読した値と一致していたら

{P'3=P3}

ということなので、

{Ci=C2⊕P'3\\\ \ \ \ =C2⊕P3\\\ \ \ \ =C3}

となり、暗号文が一致するということが分かります


これにより、トライした暗号文が目的の暗号文と一致すれば {P3} の解読に成功したと、攻撃者に分かってしまうんです



ただ、単純に1ブロック全てを総当たり攻撃で解読しようとすると、256^16 通り全てをトライする必要があります
※ブロック長が 16byte だった場合


1byte 以外の文字が全て分かっていれば総当たり攻撃の対象を 1byte だけに絞りこむことができるので、トライする総数がグッと減ります

これはパディングオラクル攻撃と同じアイディアですね



具体的には、以下のような POST リクエストを送信したときに、リクエストヘッダーを先頭から解読していくことで cookie 値の解読を実行しています

POST /AAAAA HTTP1.1\r\n[リクエストヘッダー]\r\n[リクエストボディー]

リクエストパス、リクエストボディは攻撃者が自由に変えられるので、1byte だけ分からない状態を簡単に作り出すことを可能にしています



この攻撃への対策では、最初のブロックの前に 1バイトのダミーブロックを毎回追加するように各ライブラリの実装が改良されました

これによって、最初のブロックには IV ではなくダミーブロックとの XOR がかかるので、スクランブルを打ち消すことを不可能にしています



記事の先頭に戻る


5. Lucky Thirteen 攻撃について

Lucky Thirteen 攻撃は、パディングオラクル攻撃を実証することに成功しています
http://www.isg.rhul.ac.uk/tls/TLStiming.pdf


SSLTLS では、受信したデータが改ざんされていないかをチェックするために、MAC (Message Authentication Code) というハッシュ値を付加しています

この MAC の計算に HMAC-SHA1 が利用されていた場合、

  • パディング長が 0 の場合は 5 回ハッシュをかける
  • パディング長が 1 以上の場合は 4 回ハッシュをかける

という、ものすごく微妙な違いが発生します


これによって、パディング長が 1 以下の時は ほんの少しだけ処理時間が短くなるので、レスポンスまでの時間がほんのちょっとだけ速くなります

この微妙な処理時間の違いを観測でき、暗号ブロックの解読が可能ということが実証されました


パディング長が1の時は、復号化した後の平文が 11 で終わっているということになります
また、復号化した後の平文がたまたま 11 となる場合なので、
1 / (2^8)^2 = 1 / 2^16 = 1 / 65,536
の確率でこれを見るけることができます

これさえ見つかってしまえば、末尾の 2byte を特定できることになります

あとはパディングオラクル攻撃と同様に 1byte づつずらしていくことで、解読を進めていくことが可能です

f:id:chanmoro999:20141104021615p:plain


処理時間の違いから、パディングが 11 かを判定するオラクルを作り出すことに成功したわけです


この攻撃では、処理時間にわずかな差がでることが利用されていたので、処理時間の差がでないように実装を修正することで対応されました


記事の先頭に戻る


6. POODLE について

10月に報告された POODLE も、パディングオラクル攻撃を実際に実現できるというものです
https://www.openssl.org/~bodo/ssl-poodle.pdf


SSL3.0 の仕様ではパディング長がブロック長の範囲内かというチェックしかせず、残りのパディングの部分にどんな値が入っているかをチェックしていません
なので、末尾の値さえ正しければ正当なパディングがセットされていると判断してしまいます

POODLE はこれを利用しています

RFC 6101 - The Secure Sockets Layer (SSL) Protocol Version 3.0
※5.2.3.2. CBC Block Cipher にこのあたりの仕様が書かれています



まず、最後のブロックがパディングだけで埋まるように長さを調整したリクエストを用意します

このとき、ブロック長が 16byte だったとすると、最後のブロックは
15151515151515...1515
という値になってます


次に、送信される暗号文の中から、末尾のブロックを解読したいブロックに差し替えます

この時、運良く末尾が 15 となれば、正常なパディングとしてサーバーに受理され解読に成功したとわかります

f:id:chanmoro999:20141106092558p:plain

あとは対象を 1byte づつずらすことで解読を進めることができます


ただ今までの攻撃とは違って、XOR をかける {C_3} の値はランダムに変化します
なので、攻撃者が任意にインクリメントすることはできず、{1\ /\ 256} の確率で正解のバイト列が発生します


これは完全にプロトコルの欠陥をついてます
もうどうしようもねえ!ということで、SSL3.0 はとうとう死んでしまったわけです


記事の先頭に戻る


7. TLS1.0 以降は安全なのか

4つの攻撃について簡単に解説しましたが、SSL3.0 は死んでも TLS1.0 以降はまだ生きています
ホントに大丈夫なのかが気になりませんか?


Lucky Thirteen 攻撃、BEAST 攻撃は実装を改良することで回避する対策が取られています
SSL3.0 も TLS1.0 も同様の修正が入れられました

ですが、POODLE は特に修正は行われずに、SSL3.0 を使わなくするというだけの対応になっています


なぜ TLS1.0 以降は POODLE の影響を受けないんでしょう?


TLS 1.0 からは、パディングの長さだけでなく、パディングにセットされる値もチェックする仕様が RFC に明記されていました
また、パディング長を隠蔽するために、 0〜256 までのパディング長をセットできるようになっています

RFC 2246 - The TLS Protocol Version 1.0
※6.2.3.2. CBC block cipher にパディングの仕様が書かれています


RFC の記述には

Each uint8 in the padding data vector must be filled with the padding length value.

とあるので、パディングには必ずパディング長と同じ値をセットすることが確かに明記されていますね


念のため OpenSSL のソースも見てみましたが、ちゃんとパディングチェックの処理が実装されていることが確認できました

s3_cbc.c tls1_cbc_remove_padding() ※OpenSSL 1.0.1j

/* The padding consists of a length byte at the end of the record and
 * then that many bytes of padding, all with the same value as the
 * length byte. Thus, with the length byte included, there are i+1
 * bytes of padding.
 *
 * We can't check just |padding_length+1| bytes because that leaks
 * decrypted information. Therefore we always have to check the maximum
 * amount of padding possible. (Again, the length of the record is
 * public information so we can use it.) */
to_check = 255; /* maximum amount of padding. */
if (to_check > rec->length-1)
	to_check = rec->length-1;

for (i = 0; i < to_check; i++)
	{
	unsigned char mask = constant_time_ge_8(padding_length, i);
	unsigned char b = rec->data[rec->length-1-i];
	/* The final |padding_length+1| bytes should all have the value
	 * |padding_length|. Therefore the XOR should be zero. */
	good &= ~(mask&(padding_length ^ b));
	}


なので、いまのところは TLS1.0 に有効な攻撃方法は見つかっていません


日立さんの情報では、alexa の日本トップ200 のサイトが、どのプロトコルに対応しているかを調査していました
HIRT-PUB14013:SSL 3.0 の脆弱性 (CVE-2014-3566) 対策:Hitachi Incident Response Team:日立


この中では 68% のサイトが SSL3.0 に対応しています
更に、TLS1.0 までをサポートしているサイトは 30% です

万が一 TLS1.0 にも致命的な脆弱性が見つかった場合は SSL3.0 と同様に廃止になる可能性もあります


今の時点でTLS1.1 以上のプロトコルにも対応できるようにしておく、というのは検討したほうがよさそうです

記事の先頭に戻る



8. まとめ

今回はあまり細かすぎないように解説したつもりですが、SSL3.0 のどんなところが弱点になっていたかが伝わったでしょうか?

送信する暗号文やサーバーレスポンスの内容から解読のためのヒントを漏らしてしまっていて、それにより総当たり攻撃の対象を絞り込めたことが問題です


今回紹介した攻撃では、全て総当たり攻撃が利用されていました
本来は CBC 暗号を総当たりで解読しようとすると1ブロックづつ解読する必要があります

BEAST 攻撃の部分でも書きましたが、ブロック長が 16byte の場合 256^16 通りが全て候補になります


これがどれだけ途方もない数かというと、

256^16 = 340,282,366,920,938,463,463,374,607,431,768,211,456

これくらいとんでもない量です

もはや何て読んだらいいかはわかりませんが、39桁です


たまたま同じものが見つかれば解読できてしまいますが、その可能性が限りなく0 に近いということでセキュリティが担保されています


ですが、今回紹介した攻撃では、解読の対象を 1byte に絞り込めたことで、たった 256通りで網羅できる状況を作り出していました

このように、総当たり攻撃を成功させるには いかに攻撃対象の総数を小さくするかがポイントになります



僕らが普段使うパスワードであっても、実は人の名前だったり、辞書に出てくる単語の組み合わせしか使っていないかもしれません

たったそれだけの情報で、無限に見えたはずの対象を 見渡せる数まで一気に絞り込むことができてしまうんです

ちなみに、クラッカーは1つのIDあたり500種類のパスワードを試すらしいです



個人的な感想ですが、こういう事例を見ると暗号ってけっこうギリギリなラインの上に成り立ってる感じがしませんか?

なんとなく解読はムリだろう!という人間の感覚と、数学的なロジックとの間に大きな乖離があるからそう感じるんでしょうか


ともかく、セキュリティを考える上では、1つのテクノロジーに頼るのではなく、2重、3重の防護ネットを張ることがやっぱり大切なんだなあと思いました



今回はそんなセキュリティのお話でした!


記事の先頭に戻る