GhostWrite アタックについて説明されているファジングの論文、RISCVuzzの論文を読んでいこうと思う。
GhostWriteの攻撃に関して、以下の3ステップを踏むようになっている:
- マイクロプログラムによる解析
- ページ・テーブル攻撃概要
- カーネルモード・マシンモード攻撃
- 秘密情報の読み取り
1. マイクロプログラムによる解析
以下のようなマイクロプログラムを考える。
; t0 = physical address, a0 = byte to be written vsetvli zero, zero, e8, m1 vmv.v.x v0, a0 ; encoded: 0x10028027 vse128.v v0, 0(t0)
このうち、vse128.v
は実際には非対応命令で、ベクトル・レジスタのデータのエンコーディング幅を128ビットで扱う命令でありC910などの実装では未対応命令となっている。
論文中では「なお、この128ビット以上の符号化はまだ標準化されていないが、「拡張されたメモリサイズを符号化するために使用されることが期待されている[17]」となっているが、実際にはこれは命令例外を出力すべきだと思われる。
が、ポイントは実際には命令例外を吐かずに、1バイトだけデータを書き込む、ということらしい。
さらに、nf
を変化させる(これは実際にはvsseg命令になる)と、nfのフィールドだけベクトルデータを書き込んでいるが、常に同じ物理メモリアドレスに書き込むことから、すべての書き込みに対して同じアドレスを使用しているものと思われる(これはRVV1.0の非対応命令のため適当に実装した?)
さらに、このvse128.v
の脆弱性は、キャッシュ・アクセスを行わずに直接メモリに書き込みを行う。
そして、MMIOを含む物理アドレス空間の任意の位置に書き込むことができる、ということになっている。
このvse128.v
命令はC910のオープンソース版では実装されていない(ベクトル命令実装は非公開?)が、実チップを使用することによって再現できる。
2. ページテーブルを攻撃する
1.を使えば、RISC-Vのページテーブルを書き換えることが可能になり、PFNを任意の物理アドレスに書き換えてそのアドレスを読み出すことが可能になる。
これは実評価ボードを使って評価され、C910のコアを使って評価したところ32~94秒で攻撃に成功した。
3. カーネルモード・マシンモード攻撃
次のステップは、攻撃者がGhostWriteを使用して、カーネルとマシンモードで任意のコードを実行し、特権を昇格させる。
これは1./2.ができるとまあできるよね、という感じで、GhostWriteを使ってシステムコールハンドラのコードを上書きし、当該システムコールをトリガしてイベントを起動することができる。
著者の検証では、OpenSBIのecallを処理する関数sbi_ecall_base_handler
の一部を上書きすることができた、としている。
4. 秘密情報の読み取り
最終的に、GhostWriteを使って暗号鍵のような秘密を間接的に読み取れることを実証する。
- 攻撃者はまず、GhostWriteを使用してRSA-CRTに使用されるパラメータを破壊する。
- これにより、サーバーは教科書的なRSAアルゴリズ ムの使用を開始する。
- 攻撃者は物理メモリ内の秘密鍵をバイト単位で破壊する。
- 各バイトについて、攻撃者は256回SSL接続の確立を試み、書き込まれた値が正しい場合のみ、攻撃者はSSL接続の成功を観測する(これにより破壊したバイトが実際には何の値だったかが判明する)
- 攻撃者はこのフィードバックを秘密鍵を回復するサイドチャンネルとして使用する。
所感:この攻撃手法はRISCVuzzでないと見つけられないか?
うーん、微妙なところだと思うが、論文中にて:
GhostWrite produces differences when fuzzing the 0.7.1 vector extension between C906 and C910. While the illegally encoded vector-store instructions generate a segmentation fault on illegal memory addresses, the C910 generates no exception.
としているので、やはり実装の差異を見ながらそれを脅威としてまで昇格させていった、という手法ということなんだと思う。
しかし速度的な問題があるかもしれないが、それだったらC910の実機の動作と、QEMUなどのエミュレータとの動作を付き合わせながら動かしても同様に発見することはできると思う。とくにこの問題は単純なデコーダミスに起因しているので、ある程度見つけやすいはずだ。