たばこライターで root 権限を取得できるのか?
(da.vidbuchanan.co.uk)- 圧電式 たばこライターだけでノートPCのDDR3メモリバスに電磁的フォールトを誘発し、権限のないLinuxユーザーから rootシェル まで取得するローカル権限昇格実験に成功
- Samsung S3520ノートPCのSODIMMデータピンに 15Ω抵抗とアンテナ役の配線 をはんだ付けし、近くでライターをクリックして特定ビットが反転するメモリエラーを発生させた
- CPython実験ではDQ7ラインのフォールトでオブジェクトポインタの bit 7 を反転させ、
bytes内部の偽bytearray構造を参照させることで、任意メモリ読み書きプリミティブを構成した - Linux権限昇格は ページテーブル を物理メモリに大量噴射した後、PTE読み取り中にbit 29のフォールトを誘発し、ユーザーアクセス可能なマッピングがページテーブルを指すようにする方式で動作する
- 成功後は
/usr/bin/suの最初のページを物理メモリ上で見つけて小さなsetuid root ELFに置き換え、ページキャッシュを汚染 した。実験環境での体感上の信頼性はSSH接続時で約50%、グラフィカルシェルでは約20%だった
ライターベースEMFI実験の出発点
- ソフトウェアバグがない場合でも、フォールト注入(fault injection) によって例外的な動作を作り出せる
- フォールト注入の方式には、ソフトウェアによる制御データ破壊、電源グリッチ、クロックグリッチ、電磁パルス、レーザーなどが含まれる
- ハードウェアへのフォールト注入は通常、フォールトを入れる 時点と位置の精度 が必要なため、専門機器とコストを要する
- 低コストなアプローチとして、RP2040ベースの PicoEMP や "Laser Fault Injection for The Masses" のような試みがある
- 出発点は、圧電式BBQ点火器をインダクタに結合して、低予算の 電磁フォールト注入(EMFI) ツールとして使うアプローチだった
- 以前には、Arduino上で動作するソフトウェアAES実装に対して DFA での攻撃に成功していた
- Nintendo Switch 2の発表が近いという文脈で、Switch 1に近いシステムソフトウェアとソフトウェアバグ不足が予想され、低コストEMFI実験を再開した
テスト対象とDDRバスのフォールト
- テスト対象は、Intel i3-2310M CPUと1GB DDR3 RAMを搭載した2011年製のSamsung S3520ノートPC
- 軽量デスクトップLinuxディストリビューションのArchを動かせて、壊れても惜しくない機材として選ばれた
- 目標は、注入したハードウェアフォールトに基づいて動作する ローカル権限昇格エクスプロイト を作成することだった
- 最も物理的に脆弱な地点として、DRAMメモリとシステムを接続する DDRバス が選ばれた
- SODIMMには読み書きデータビットを伝える64本のDQピン、DQ0〜DQ63がある
- 実験装置は、DQ26と推定されるピンに 15Ω抵抗と配線 を1本はんだ付けした構成
- 配線はアンテナのように周囲の電磁干渉を受けてデータバスへ伝える
- 抵抗は通常のメモリ動作を継続的に妨害しないよう干渉強度を制限するための要素で、実際には不要かもしれない
- 一般的な圧電式ライターをアンテナ配線の近くでクリックするだけで、memtestでメモリエラーが安定して現れた
- 表示されたエラーはすべてbit 29が反転した形だった
- はんだ付けしたピンはDQ26と推定されたが、bit 29が反転した理由としては、ピンを数え間違えたか、マザーボード側でデータラインが入れ替えて配線されていた可能性がある
- 指の反応速度レベルのため、フォールト注入の タイミング制御 は精密ではない
- フォールトが発生すると、特定の64ビット読み取りまたは書き込みで同じビットが反転する可能性が高い
CPythonでのビット反転悪用
- 最初の実験は、CPythonで サンドボックス脱出 形式のエクスプロイトを作ることだった
- CPython自体はサンドボックスではないため、実際のセキュリティ境界の回避ではなく、慣れた内部構造を使った準備段階にあたる
- この実験では、先ほどの写真のDQ26ではなく DQ7 にはんだ付けした配線を使った
- CPythonオブジェクトはガベージコレクションヒープ上にあり、オブジェクトヘッダにはrefcountと型オブジェクトへのポインタがあり、その後に型ごとのフィールドが続く
bytesオブジェクトは長さフィールドの後にデータ本体が同じヒープ割り当て内にあるbytearrayオブジェクトは長さフィールドの後に実データ保存バッファを指すポインタを持つ
- 重要な戦略は、
bytesオブジェクト内部に偽のbytearray構造をデータとして入れること- CPythonにこの偽オブジェクトへの参照を渡させられれば、攻撃者が選んだ
bytearrayの長さとポインタフィールドによって任意メモリ読み書きが可能になる
- CPythonにこの偽オブジェクトへの参照を渡させられれば、攻撃者が選んだ
- bit 7を反転させるフォールトは、64ビットワード内のポインタに 128を加算または減算する効果 を持つ
- 偽の
bytearrayをbytesオブジェクト内部の+128バイトオフセットに配置すると、bytesオブジェクトポインタがフォールトによって偽bytearrayポインタに化けうる - この変換が望ましい方向になる確率は50%
- 偽の
- 重要なのは、メモリ 内容そのもの ではなく、メモリバス上の読み書き動作をグリッチさせている点
- Rowhammerのように保存済みデータが変化する方式ではなく、バス上で進行中のアクセスが破損する
- 望むポインタアクセスがバス活動の大半を占めるよう、同じオブジェクト参照で巨大なタプルを埋めた
- CPUキャッシュがDRAMアクセスを減らすため、3MiBキャッシュより大きい構造を順次アクセスして毎回DRAMから読ませた
- Pythonの
isはポインタ比較のように動作し、ポインタが変わったかの確認に使われた
- CPythonエクスプロイト全体のソースは ddr3_dq7.py にある
TESTING変数で、ハードウェアなしにソフトウェア上でビット反転をシミュレートできる
Linux権限昇格に必要なメモリ構造
- Linuxローカル権限昇格では、キャッシュ、仮想メモリとページテーブル、TLB が重要な構造として使われる
- DRAMは比較的遅いため、CPUはL1、L2、L3キャッシュを使う
- 実験ノートPCのL3キャッシュは3MiB
- キャッシュヒット時はDRAMにアクセスせず、キャッシュミス時にのみDRAM読み取りが発生する
- キャッシュ観点での最小単位は キャッシュライン で、このノートPCでは64バイト
- 1バイト読んでもキャッシュされていないデータなら64バイトのDRAM読み取りが発生する
- DDRデータバスは64ビット幅なので、この読み取りは8回の連続アクセスburstで処理される
- x86-64の仮想メモリは4KiBページとツリー構造の ページテーブル で実装される
- このプラットフォームには4階層のページテーブルがある
- 各ページテーブルは4KiBページで、512個の64ビットPTEを含む
- 上位階層のPTEは次の階層のページテーブルの物理アドレスを指し、level 0 PTEは対象の物理ページを指す
- ルートページテーブルの物理アドレスはCR3 CPUレジスタに保存される
- PTEで必要なのは物理アドレスフィールド
- フラグビットをマスクすると、ページアラインされた物理メモリアドレスが残る
- TLBは、仮想アドレスから物理アドレスへのページマッピングをキャッシュするCPU内部ハードウェア
- 実験ノートPCの正確なTLBサイズは不明だが、およそ1024エントリ規模に見える
ページテーブルをユーザーメモリへ引き込む
- Linuxエクスプロイト戦略は、Mark Seabornの Rowhammer exploit の要素から着想を得ている
- 目標は、自分のプロセスの ページテーブル をユーザーアクセス可能なメモリへマッピングすること
- そうなれば、PTEを書き換えて任意の物理メモリにアクセスできる
- 物理メモリ配置を精密に制御する代わりに、物理メモリのちょうど 50% をlevel-0ページテーブルで埋めた
- その後、R/Wマッピングを繰り返しアクセスしてTLBを回避する
- マッピング数がTLBサイズを超えるため、各アクセスでページテーブルウォークを強制できる
- そのウォーク中のlevel-0 PTE読み取りでbit 29のフォールトを起こすのが目標
- bit 29が反転すると、PTEが指す物理アドレスは 512MiBオフセット だけ変化する
- 運が良ければ、変化後のアドレスが先ほど大量配置したlevel-0ページテーブルの1つを指す
- この場合、本来は通常のR/Wページに見えるはずのマッピングが、ページテーブルを通常のR/Wページのように露出させる
- 理論上はbit 29からbit 12まで、どのビット反転でも機能しうる
- bit 12は4KiBオフセットに相当する
- 要点は、PTEが「別の場所」を指し、物理メモリの約50%が悪用可能なページテーブルで埋まっていること
- アンテナ配線のはんだ付けは必須ではない可能性もある
- 十分に強い電磁干渉を作れれば可能性はあるが、システムクラッシュや破損のリスクははるかに高くなる
level-0ページテーブルの大量配置方法
- まずLinuxの memfd_create でメモリベースのファイルを作る
- Mark Seabornエクスプロイトの
/dev/shm/ファイルと同じ役割だが、ファイルシステムを直接触らない
- Mark Seabornエクスプロイトの
- 同じバッファを mmap で何度もマッピングする
MAP_FIXEDオプションで各マッピングを仮想メモリ上で2MiBアラインに強制する- このアラインメントによって毎回新しいlevel-0ページテーブルの生成が保証される
- Linuxにはプロセスごとのマッピング数、つまりVMAに約2^16の制限がある
- 各マッピングを32MiB長にして、1つのマッピングで16個のlevel-0ページテーブルを生成させる
- 各マッピングは仮想アドレス空間では32MiBを占めるが、PTEは同じ物理ページを指す
- 物理メモリコストはlevel-0ページテーブルだけ
- この方法で、メモリが埋まるまでページテーブルを大量配置できる
物理メモリ読み書きとsuページキャッシュ汚染
- フォールトを待ちながらR/Wマッピングを繰り返しアクセスし、想定と異なる値が返ったらフォールトを検出する
- 返ってきたデータがPTEのように見えれば、ページテーブルへのR/Wアクセスを得た状態
- 次の段階は、このページテーブルがどの仮想アドレスに対応するかを見つけること
- PTEを物理アドレス0を指すように書き換える
- 再びR/Wマッピングを走査し、どのマッピングが変わったかを調べる
- PTEを書き換えても、MMUは即座には気付かない
- 仮想-物理マッピングがTLBにキャッシュされているため
- ユーザー空間から直接TLBをflushする方法が分からないため、数千個のR/Wマッピングに繰り返しアクセスしてTLBを新しい値で埋め、古い値を追い出した
- この時点で、物理メモリ全体 への読み書きアクセスが可能になる
- その後
/usr/bin/su実行ファイルを読み取り専用で開き、最初のページをmmapする/usr/bin/suは setuid root 実行ファイル- 物理メモリ全体を走査して同じページを見つける
- 見つけた物理ページへの書き込みアクセスで、rootシェルを起動する4KiB未満のELF payload に置き換える
- Linuxは次に
suが実行されると、その最初のページはすでにメモリ上にあると判断し、ディスクから再読込しない- 汚染されたページキャッシュを再利用して注入済みELFを実行する
- 注入されたELFは
echo 1 > /proc/sys/vm/drop_cachesでページキャッシュを空にし、その次のsu実行は正常動作に戻る
- Linuxエクスプロイト全体のソースは linux_x86_64_lpe.c にある
信頼性と環境上の制約
- デモ実行では運良く、ライターを1回クリックしただけで良いグリッチが発生した
- それ以前の多くの試行ではシステム全体がクラッシュしていた
- エクスプロイト全体の信頼性は厳密には測定されていない
- ノートPC画面を消してSSHで接続した状態では、体感で約50%に見える
- グラフィカルシェルでは約20%に近い
- 実験システムは統合グラフィックスを使用していた
- GPUのメモリアクセスがエクスプロイトを妨害している可能性がある
- pipewire、sshd、systemd関連のバックグラウンドサービスとswapも有効だった
- 現実的なデスクトップLinux環境を保つための選択で、無効化すれば信頼性が上がる可能性がある
- RAMがもっと多く搭載されていれば、より高い比率をページテーブルで埋められるため、全体の信頼性が上がる可能性がある
想定される活用と残された疑問
- Windowsで信頼性の高いEMFIローカル権限昇格が可能なら、TPMベースのアンチチートがシステム上で許可されるソフトウェアを制限する状況に影響しうる
- AndroidのSafetyNetやPlay Integrity検査にも同様の話はありうるが、スマートフォンにグリッチ用モッドチップを入れるのはより難しい
- 低レベルの性能最適化では、ページテーブルやTLBの知識が直接重要でないことも多いが、このエクスプロイトでは仮想メモリという幻想を保つ構造そのものが直接の攻撃対象になる
- 適用範囲にはまだ確認すべき点が多い
- DDR4、DDR5でも動作するか
- ARMでも動作するか
- ECCのさまざまな種類、特にDDR5 Link-ECCがどの程度緩和するか
- RP2040のようなデバイスで類似フォールトを電子的にトリガーする最も単純な方法は何か
- ハイパーバイザー脱出に使えるか
- WebKitエクスプロイトやNintendo Switchカーネルエクスプロイトにできるか
2件のコメント
ライターから点火装置を外してゲームセンターのコインを積み増しするのを思い出しますね
Hacker News のコメント
ここでのインスピレーションは Switch 2 でルート権限を得ることで、Linux で root を取ったのは概念実証だった。
目標は、実用的に悪用可能な根本的セキュリティ脆弱性を示すことではなく、TPM やゲームのカーネル権限アンチチートを破らずに、自分のハードウェアの本当の所有権を取り戻すことに近い。
印象的だし、ソフトウェアの自由を守ろうとする取り組みは歓迎するが、それならむしろ代替を支持したい。なぜ彼らにインストールベースのように見える数字と利益を与えるのか。最初から root アクセスが機能として提供されている Steam Deck や他の携帯機を買えばいい。
記事はとてもよく書かれていて挑戦もすごいが、自分の頭は「物理アクセスがあればもう終わり」というハッキングの常識のほうに反応した。
最初に思い浮かんだのは、物理アクセスがあれば BIOS を再フラッシュし、ドライバのバックドアを入れ、ライブ OS で起動して
/etc/{passwd,shadow,groups, etc}をいじればいい、というようなことだった。だが、ディスクが暗号化されていると物理アクセス攻撃の大半は不可能になることを思い出し、そうなるとこの手の攻撃はものすごく魅力的に見える。アンテナのアイデアは、干渉装置を内蔵したハードウェアへ拡張できるし、無線媒体で外部と通信させて攻撃者がリモートで干渉を起こすこともできる。そこに、被害者を訪問させるようだます攻撃者制御の Web サイトまであれば、現実味が出てくる。
この電磁干渉手法は CPU 自体をだます方式なので、新しいページングアルゴリズムが出てこない限り、どう直せるのかよく分からない。
私の PC もフルディスク暗号化を使っているが、起動すると復号に使うキーファイルが使用され、その瞬間から物理的にアクセス可能な PC になる。
気に入った。要するに、メモリの読み書き中に静電気によるビット反転が起こり、はんだ付けまで行えば、「安全な」ポインタを自分の望む悪性ポインタへ決定論的に変えられる、ということだ。
歴史的に、物理アクセスに対する見方は「相手がデバイスを手に入れたらゲームオーバー」だった。TPM と信頼実行環境は、この見方を「ユーザーが物理アクセスを持っていても、エンクレーブ内部の特定の演算は信頼できる」へと変えた。
次の段階が一番興味深い。はんだ付けなしでも、ある程度信頼性のある結果を得られるだろうか。電気的干渉を扱うためにすでに多くの工夫が入っているので、はるかに難しそうだが、可能かもしれない。ライターを押すたびに 64 ビット読み取りでランダムな 1 ビットが反転し、エクスプロイトが例えば 4 つのビット反転のうち 1 つだけでも動作するなら、平均試行回数はそれほど多くないかもしれない。
メモリのどの部分でも望む時点で望む値に変えられるので、ランダムなビット反転に期待する必要はない。単にプログラム全体を注入すればいい。
タイトルだけ見て、誰かがタバコ用ライターの root 権限を取ったという記事かと思い、十分信じる準備ができていた。
両親のオーブンも定期的にソフトウェアアップデートを受けているので、ライターが「スマート」かどうか疑いもしなかった。
当然、ライダーを動かし、同時に強烈な LED フラッシュとフェード、触覚ショック、効果音を出すためのチップも必要だ。誰かデモとして作ってほしい。小さなリボルバーみたいな形だといいが、指とホットドッグの安全のために仮想メモリコントローラを強化する必要がありそうだ。
はんだを溶かすことがここまで複雑になり得るとは。だから、ライターをルート化したという記事も十分信じられる。
80〜90年代のシドニーのゲームセンター機でやっていたエクスプロイトを思い出した。学校のガスストーブには、僕らが「クリッカー」と呼んでいた圧電点火装置が付いていて、ストーブから取り外せた。
そのクリッカーを近所のゲームセンターに持っていき、CRTの角の1つでカチッと押すと、システムにショックが入り、ゲームのクレジットが増えた。おそらくCRTの接地が、物理的にコインを確認する装置と同じアース線を共有していたからだと思う。
しばらくすると店主たちが気づいて警報のようなものを追加したが、それまでは本当に最高の時代だった。
数年続けていたら店主たちが気づき、ゲーム筐体を透明なプラスチックで覆い始めた。同時に、筐体がプラスチックで密閉されると換気のために背面に穴を開けたのだが、竹の棒でコイン投入を登録するレバーを押せることに気づいた。
すると換気口を背面ではなく上に移してレバーに届かないようにしたが、今度は返却口からコインを押し上げるとコイン登録レバーに触れられることが分かり、無料プレイは続いた。最後には返却口の箱の中に鋭いネジを打ち込み、指が切れるようにしてきたので、その後僕らはSEGAを買った。本当に面白かった。
従業員が無料でテストできるよう意図的にそう設計されていたのかは分からないが、友人は裏に這い込んで、ただで延々とプレイしていた。
「Jungle Jive」版は、タバコ用ライターの電気点火装置で金属の投入口に弱いショックを与えると、機械の反対側からトークンが出てきた。あまり速く何度もカチカチやると警告モードに入った。1人でもできたが、最適構成は従業員を見張る1人、クリック担当1人、回収担当1人の3人組だった。
https://www.amazon.com/CYBERPUNK-Outlaws-Hackers-Computer-Fr...
オーストラリア人として読むと別の意味に解釈できる。交渉力次第では、タバコ用ライター1つだけでもrootを得られる。
面白いエクスプロイトであるだけでなく、CPUでキャッシュが動作する仕組みを説明する素晴らしいミニ入門にもなっている。
1年ほど前、コンピュータがどのように動き、どのように作られているのかを、最小単位である論理ゲートから始めて説明していた記事が投稿されていた記憶がある。あのWebサイトが何だったか覚えている人はいる?
https://www.nand2tetris.org/course
「15オームの抵抗1つとワイヤ1本をDQ26にはんだ付けしただけだ。ワイヤはアンテナのように動作し、近くの電磁干渉を拾ってデータバスに直接流し込む」
本当に見事なハックだ。ライターで電磁干渉を作る方法とは。DDRバスの横で火をつけて、何が起きるか見てみる必要がありそうだ。
もちろん、先にメモリにアンテナをはんだ付けしなければならないなら可能だ :-)
それでも、こうしたグリッチを実際に悪用する方法についての、優れた綿密な記事だ。タバコ用ライターは、データセンターの裏口をうろつきながら管理者がタバコを吸いに出てくるのを待つ用途にも使える。
タイトルを見たときは、USB-Cタバコ用ライター機器で現代自動車をハックする話だと思った。