CRIME攻撃 : Juliano Rizzo氏とThai Duong氏によって発見された圧縮オラクル攻撃 CVE-2012-4929;
圧縮オラクル攻撃では、選択された平文と未知の平文の混合に対して適応型データ圧縮を使用すると、圧縮テキスト自体の内容がその後暗号化されていても検出可能な、圧縮テキストの長さの内容依存の変化が生じる可能性があります。これは、注入された既知の平文がメッセージの秘密部分の未知の内容と部分的にでも類似している場合を検出するためにプロトコル攻撃で使用でき、秘密テキストの一致を探す複雑さを大幅に低減します。CRIME攻撃とBREACH攻撃は、この現象を使用したプロトコル攻撃の例です。
CRIME攻撃を使用すると、暗号化データの長さを利用して、クライアントからサーバーに送信された暗号化データを取得できます。メッセージまたはHTTPリクエストの暗号化に使用された秘密鍵を取得することはできません。
CRIME攻撃の仕組みを説明する記事は多数ありますが、私がインターネットで見つけた中で最高の解説は次のとおりです :
この攻撃は実際には複雑ではありません。本当に興味深いのは、理論とは少し異なる実装です。
記事に記載されている単純な方法を見てみましょう。仕組みを理解するのに良い方法です :
攻撃者は(例えばJavaScriptを使って)クライアントが送信するリクエストを制御できます。目標は秘密のCookieを取得することです。攻撃者は次のような複数のリクエストを送信し、暗号化データの長さを確認します :
| リクエスト | 長さ |
|---|---|
| GET /cookie= DATA cookie=quokkalight | 80 |
| GET /cookie=a DATA cookie=quokkalight | 81 |
| GET /cookie=b DATA cookie=quokkalight | 81 |
| GET /cookie=. DATA cookie=quokkalight | 81 |
| GET /cookie=q DATA cookie=quokkalight | 80 |
cookie=q が秘密のCookie内の cookie=quokkalight と一致するため、暗号化データの長さは同じになり、攻撃者は1バイトを見つけたことがわかります。
しかし、この方法は時々失敗し信頼できないため、代わりに別の方法を使用します。
まず、見つけたい文字と、初期リクエストには存在しない複数の特別な文字(例: chr(i) + "#:/[@/&")を続けたリクエストを送信します。次に、ペイロードを "#:/[@/&" + chr(i) のように反転させた2番目のリクエストを送信し、2つの長さを比較します。len(enc(req1)) < len(enc(req2)) の場合、1バイト見つかったことになります。このメソッドは two_tries と呼ばれ、はるかに信頼性が高いです :
攻撃者は1バイトを見つけました !
if len(enc(request1)) < len(enc(request2)):
print("found byte")
私が実装した two_tries メソッドは完全に再帰的ですが、なぜでしょうか? 圧縮が複数のパターンと一致するため、1度に複数のバイトが見つかることがあるからです。
秘密を cookie=quokkalight とします。two_tries アルゴリズムを実行すると、次の結果が見つかります :
result 1: cookie=quokie=quokie=quokie=quokie=quokie=
result 2: cookie=quokkalight
アルゴリズムは、可能な解をすべて見つけるために、ツリー内のすべてのパスを辿る必要があります。 すべての結果は、次のように表現できるツリーとして見ることができます :

ストリーム暗号モードに対するCRIME攻撃の概念実証は、ファイル : CRIME-RC4-poc.py にあります。これは前述の説明をPythonで実装したものです。
python3 CRIME-RC4-poc.py
完全なデモと結果 :
CBC暗号モードがAESまたはDESで使用される場合、攻撃はRC4ほど単純ではありません。すべてがブロックに分割されるため、攻撃はもう少しトリッキーです(それほどでもありません)。
例えば、CBC暗号モードでAESを使用するとします。ブロックは16の長さに分割され、len(data)%16 != 0 の場合、末尾にパディングが追加されます。
CBCモードでは、payload=rand というペイロードは16の長さになり、12にはならないことに注意してください。暗号化の前にデータの末尾にパディングが追加されるためです。したがって、以前の攻撃は機能しません。
例 :
この例では、バイトを追加または削除しても長さは常に同じになるため、パディングだけが変わります。攻撃者は暗号化データしか見ることができず、暗号化データを使ってパディングを知る方法はありません。
解決策: CBCモードの仕様を利用し、GARB変数にランダムな値を追加して(攻撃者はGETおよびPOSTデータを制御できるためGETパラメータに)パディング長を1にします。
バイトがパターンと一致する場合、長さは同じになり、一致しない場合、長さは異なります。
次に、RC4の部分で説明したのと同じ方法を使用するだけです。その前に1ステップ追加します。
adjust_padding() を呼び出しますtwo_tries_recursive() を呼び出すと、秘密のFLAGを見つけることができます !python3 CRIME-cbc-poc.py
近日公開予定...
\ /
\ _ /
----/_\----
x--------------( . )--------------x
x|x | |_|\_/|_| | x|x
x x x x
https://www.nccgroup.trust/globalassets/our-research/us/whitepapers/ssl_attacks_survey.pdf https://github.com/cloudflare/cf-nocompress https://www.ekoparty.org/archive/2012/CRIME_ekoparty2012.pdf https://security.stackexchange.com/questions/19911/crime-how-to-beat-the-beast-successor/19914#19914
| バイトqを取得するリクエスト | 長さ |
|---|
| GET /cookie=a~#:/[@/& DATA cookie=quokkalight | 81 |
| GET /cookie=~#:/[@/&a DATA cookie=quokkalight | 81 |
| GET /cookie=b~#:/[@/& DATA cookie=quokkalight | 81 |
| GET /cookie=~#:/[@/&b DATA cookie=quokkalight | 81 |
| GET /cookie=q~#:/[@/& DATA cookie=quokkalight | 80 |
| GET /cookie=~#:/[@/&q DATA cookie=quokkalight | 81 |
| ブロック1 | ブロック2 | ブロック3 | 長さ |
|---|
| GET /cookie= DA | TA cookie=quokka | light + PAD(11) | 48 |
| GET /cookie=a DA | TA cookie=quokka | light + PAD(10) | 48 |
| GET /cookie=b DA | TA cookie=quokka | light + PAD(10) | 48 |
| GET /cookie=. DA | TA cookie=quokka | light + PAD(10) | 48 |
| GET /cookie=q DA | TA cookie=quokka | light + PAD(11) | 48 |
| ブロック1 | ブロック2 | ブロック3 | ブロック4 | 長さ |
|---|
| GET /GARBc | ookie= DATA coo | kie=quokkalight + PAD(1) | 48 | |
| GET /GARBc | ookie=a DATA coo | kie=quokkalight | PAD(16) | 64 |
| GET /GARBc | ookie=b DATA coo | kie=quokkalight | PAD(16) | 64 |
| GET /GARBc | ookie=. DATA coo | kie=quokkalight | PAD(16) | 64 |
| GET /GARBc | ookie=q DATA coo | kie=quokkalight + PAD(1) | 48 |