Specture & Meltdown
更新履歴
- 2021.03.24
はじめに
- このドキュメントでは,IntelのCPUにある脆弱性のSpectureととMeltdownについて検証してみるコードを実行する.
今回使ったデバイス
- うちの中でのさうこうスペックのWindowsマシン!
- ちなみに,CPUにパッチは適用済み.
InSpecture
- 検証ツールはこちら
- GRC - InSpectre - Gibson Research
- https://www.grc.com/inspectre.htm
- ダウンロードページ.
- ダウンロードしたファイル.
- 実行してみる.
- パッチを当てているからか,プロテクションが効いているので,Disableにしてみる.
- 管理者権限が必要.
- 管理者で実行.
- 次のように表示.
- Disableボタンを押してみる.
- これで対策が無効化された模様.
leaky.pageを使う
- 以下のページで紹介されてたサイトにアクセス.
- CPU脆弱性「Spectre」の概念実証と脆弱なエンドポイントを探す拡張機能「Spectroscope」をGoogleが公開
- https://gigazine.net/news/20210315-spectre-proof-of-concept/
こ
のサイトは、JavaScript で記述された Spectre 脆弱性のプルーフオブコンセプトをホストしています。このサイトは、Linux
上のインテル® Core™ i7-6500U プロセッサーで動作する Chrome 88
用に開発・最適化されています。他のCPU(ベンダーや世代が異なる)、OS、Chromiumフレーバーでも動作することが確認されていますが、設定を
調整する必要があったり、動作の信頼性が低かったり(あるいは全く動作しなかったり)する可能性があります。この概念実証の目的は、ウェブベースの
Spectreエクスプロイトの実現可能性を示すことであることに注意してください。あなたのデバイスが脆弱であるかどうかを確認するためのテストではあ
りません。すべてのコードは公開されており、GitHubで見ることができます。
このデモは3つのパートに分かれています。
CPUの投機的実行による副作用を観察するためのタイマーの較正。
JavaScriptの配列のメモリレイアウトを推察するデモ。
ブラウザのレンダラープロセスのメモリをリークするSpectreプルーフオブコンセプトそのものです。
Webへの影響や、Webサイトを守る方法について詳しく知りたい方は、ブログ記事をご覧ください。
- とのこと.
Spectreの概念実証を実行する前に、過渡的な実行による副作用を観察する方法が必要です。
最も一般的なものは、キャッシュサイドチャネルです。メモリアクセスのタイミングを計ることで、選択されたアドレスがキャッシュにあるのか(アクセスが速
い場合)、メモリからロードする必要があるのか(アクセスが遅い場合)を推測することができます。後日、Spectreのガジェットを使って、秘密の値を
インデックスとしてJavaScriptの配列にアクセスします。どの配列のインデックスがキャッシュされたかをテストすることで、その秘密の値を復元す
ることができます。
こ
のような小さなタイミングの違いを測定するには、高精度のタイマーが必要です。これについては、Michael Schwarz、Clémentine
Maurice、Daniel Gruss、Stefan Mangardによる素晴らしい論文があります。"Fantastic Timers and
Where to Find Them: High-Resolution Microarchitectural Attacks in
JavaScript
"という論文があります。その一例として、SharedArrayBufferを単調な時計として使用する方法が紹介されています。ワーカースレッドでカ
ウンタをインクリメントし、2番目のスレッドから高精度のタイムスタンプとして読み取ることができます。特にSharedArrayBufferは、最近
ではサイトがクロスオリジンで分離されている場合にのみ利用可能ですが、この論文には他にも多くのタイマーが記載されています。
- 画面中央にあるrunボタンを押す.
キャッ シュタイマーの反復回数。 値を大きくすると、キャッシュタイマーのタイミング差が大きくなります。この値を変更した場合は、必ずキャリブレーション・ステップを再度実 行してください。Cache timing threshold in ms: キャッシュのヒットとミスを決定するために使用するしきい値です。タイマーの繰り返しを変更する際に調整する必要があります。安定したタイ マーを使用:安定したタイマーを使用した場合、タイミングの差は小さくなりますが、偽陰性の数は少なくなります。
- とのこと.
- 何度か実行したけれど,違いは分からず...
- メモリレイアウト推論
- コー
ドが特定のキャッシュセットにアクセスしたかどうかをテストできるようになったので、これを使って JavaScript
オブジェクトのメモリレイアウトを推測することができます。JavaScriptの配列に異なるオフセットでアクセスした場合、どのオフセットがどの
キャッシュセットに対応するかを推測することができます。L1キャッシュセットは通常、アドレスの6ビットから11ビットに基づいて選択されるので、これ
は配列要素のページオフセットに直接変換されます。キャッシュセットの境界を検索することで、下位6ビットを推測することができます。例えば、配列のイン
デックス19がキャッシュセット5にあり、インデックス20がキャッシュセット6にある場合、インデックス20がキャッシュセットの開始点であることがわ
かります。
このデモでは、何個のインデックスをテストするか、また各テストを何回繰り返すかを制御できます。コードは、キャッシュセットの境界を見つけるために記述されたパターンを見つけようとします。
こ のデモが成功すると、ちょうど16のインデックス増分の後に前進するキャッシュラインにヒットするはずです。16という数字は、キャッシュラインのサイズ (64バイト)を要素のサイズ(4バイト)で割ったものです。また、コードで使用されていると思われるため、常にヒットを示すキャッシュセットがいくつか あり、ノイズも多く見られるはずです。しかし、インクリメントのパターンは目立ち、簡単に認識できるはずです。
なお、このコードは手法を示すための単なるデモであり、Spectreデモのキャリブレーションには使用できません。なぜなら、メモリレイアウトは実行のたびに変化し、再計算が必要になるからです。
- テストしてみる.
- 2回目.
- 次.
- いよいよSpectreの実機デモです。このデモを試す前に、これまでのステップが成功したかどうかを確認してください。
実 行をクリックすると、次のようなことが起こります。まず、コードがメモリのレイアウトを設定します。まず、コードはメモリのレイアウトを設定します。 JavaScriptの配列を作成し、その後にUint8Arrayの束を作成します。前のページと同様に、JavaScriptの配列のページオフセッ トが推測されます。すべてのUint8ArraysはJavaScript配列の後ろに固定されたオフセットであるため、これによりすべての Uint8Arraysのページオフセットもわかります。
Uint8Array の内部オブジェクトには、lengthフィールドとデータへのポインタが格納されています。この2つのフィールドが別々のキャッシュラインにあるように、 配列の1つを選択します。これにより、投機的実行の際に、キャッシュから長さをフラッシュしつつ、バッキングストアへのポインタを追いかけることができま す。これが、ページオフセットを知る必要がある理由です。
シングルビットをリークするためのSpectreガジェットの要点は次のようなものです。
- こ
のコードでは、Uint8Arrayにアクセスして1ビットをマスクアウトし、それを使ってプローブ配列にアクセスしています。ビットが0であれば、プ
ローブ配列はオフセット0でアクセスされ、そうでなければオフセット0x800でアクセスされます。L1タイマーを使って、どちらのアクセスかをテストす
ることができます。
全体の流れは以下のようになります。
配列の境界内にあるインデックスを使用してガジェットを数回実行し、分岐予測器をトレーニングします。
配列の長さのフィールドをキャッシュから取り出します。次にガジェットを呼び出したとき、CPUは長さのチェックが成功すると推測します。
アウトオブバウンズのインデックスを使用してガジェットを実行し、L1タイマーを使用してビットをリークします。
う まくいった場合は、右のようなメモリの16進ダンプが表示されます。Uint8Arraysはそれぞれ0x20のAで埋められています。この結果はノイズ を含んでいますが、Aははっきりと見えるはずです。Spectreガジェットの実行オプションの最小値と最大値を使用して、ノイズを減らすことができま す。デモを実行した後は、下部にあるボタンを使って結果を公開することを検討してください。
- 実行してみる.
- 実行結果は次のように.
$ tail -f spectre.log
[!] error: could not infer memory layout
[*] retrying. (9 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 4%, false negatives: 93%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (8 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 3%, false negatives: 95%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (7 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 0%, false negatives: 100%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (6 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 0%, false negatives: 98%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (5 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 5%, false negatives: 93%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (4 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 6%, false negatives: 92%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (3 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 4%, false negatives: 94%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (2 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 5%, false negatives: 94%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (1 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 5%, false negatives: 94%
[!] error: too many wrong false negatives in leak test (> 20%)
[!] no retries left
- 次は,プロテクトをEnabledにして実行.
$ tail -f spectre.log
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 4%, false negatives: 94%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (9 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 5%, false negatives: 95%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (8 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 0%, false negatives: 99%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (7 retries left)
[*] inferred memory layout: array index 90 is in cacheSet 33
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 0%, false negatives: 99%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (6 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 5%, false negatives: 94%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (5 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 0%, false negatives: 100%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (4 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 4%, false negatives: 94%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (3 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 4%, false negatives: 94%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (2 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 0%, false negatives: 99%
[!] error: too many wrong false negatives in leak test (> 20%)
[*] retrying. (1 retries left)
[*] inferred memory layout: array index 74 is in cacheSet 32
[*] array elements page offset: 0x6d8
[*] first typedArray at 0xf80
[*] found typedArray with desired alignment (@0x358)
[*] false positives: 3%, false negatives: 95%
[!] error: too many wrong false negatives in leak test (> 20%)
[!] no retries left
- んー.わからん.