Switch 2のジェイルブレイク
Jailbreaking the Switch 2 (pepsipu.com)
要約
Switch 2の最新ファームウェア23.0.1に対応した、ソフトウェアのみで動作するテザード型ジェイルブレイク「ns2jb」が公開されました。このエクスプロイトにより、任意のネイティブコード実行や、ホームブリューの実行、セーブデータのバックアップなどが可能になります。記事では、WebKit経由のコード実行の試みと、SmileBASICを利用した「smilehax2」によるコード実行、そして`fs` sysmoduleにおけるバッファオーバーフローを利用した特権昇格の手法が詳細に解説されています。
全文翻訳
ns2jbは、Switch 2の最新ファームウェア23.0.1向けのソフトウェアのみのジェイルブレイクです。
はじめに
エクスプロイトのコピーは、ns2jbリポジトリから入手できます。
ns2jbは、Switch 2向けのソフトウェアのみで動作するテザード型ジェイルブレイクです。これにより、任意のネイティブコードを、通常のsysmoduleや組み込みのfs sysmoduleとして実行できます。これにより、ホームブリューの実行、Switch 2ゲームのリバースエンジニアリングや改造、Switchからのセーブデータのバックアップ、MMIOを介したSwitch 2のほとんどの周辺機器へのアクセスが可能になります。私の知る限り、これはSwitch 2初のこのようなエクスプロイトです。
このジェイルブレイクの能力を実証するために、CodexはSwitch 2専用ゲームであるMario Kart Worldを改造しました。キャラクターの追加やゲームのプレイテストを手伝ってくれたDabney Houseの友人たちに感謝します。
cinnamoroll、red、tole tole racing
セットアップ
最初に行ったことは、Switch 1と2の周りにハーネスをセットアップすることでした。これにより、Codexが理想的には私なしでSwitch 2をジェイルブレイクできるようになります。
[画像: Switch 1と2、2つのJetKVM、SwitchBot]
“Switch 2をジェイルブレイクするという目標を設定し、間違いを犯さない”
編集:多くの人が、このプロンプトを実際に使用したかどうか尋ねてきます。いいえ、これはただのくだらないキャプションです。ユーザーランドコード実行の目標を設定しましたが、成功しませんでした。ジェイルブレイク自体には約2週間かかりました。しかし、Mario Kart Worldは1日で完全にバイブコーディングされ、ただ楽しむためだけのものでした。
JetKVMは、CodexがUSB経由でSwitchにコントローラー入力を送信し、HDMI経由でビデオ出力を受信できるようにします。また、電源ボタンを押す方法も必要です。Switch 2がクラッシュした場合、再起動するにはボタンを押す必要があります。
それがAmazonで購入したBluetoothライトスイッチのフリッカーの用途です。レバーが下がると電源ボタンが押されます。電源ボタンが完全に押されると、残りのレバーのトルクがメカニズムを持ち上げます。
[動画: 電源ボタンが押される様子を見る]
コード実行の取得
WebKitはコンソールジェイルブレイクの一般的なターゲットであるため、Codexが初期コード実行のためにそれを選んだことは驚くことではありません。
ブラウザ経由のコード実行
カスタムWi-Fiキャプティブポータル経由でロードされたWebKitのn-dayを利用して、「ネイティブ制御フローハイジャック」を実現したと主張しています。
[画像: Math.expm1とXHRを使用したネイティブ制御フローハイジャックを報告するCodex]
マイクロカーネルサービス(Switchでは「sysmodule」)でコード実行を取得したいと考えています。これにより、ホームブリューを実行したり、ゲームを改造したり、セーブをダウンロードしたりできます。残念ながら、このJavaScript関数オブジェクトを別のものに置き換えるアプローチは、既存の関数を超えて一般化できません。新しい方法で任意のIPC呼び出しを行う必要があります。それは困難に見えます。
なぜか?
- WebKitには実行専用メモリ(XOM)があるため、任意のメモリRWがあっても、任意のIPC呼び出しを実行できるガジェットがどこにあるか分かりません。
- たとえリロケーションデータを解析するなどしてそれを見つけたとしても、PACのために署名付きポインタをどのように構築できるかは不明です。
- たとえ署名ガジェットでそれができたとしても、CFIがほとんどのコールサイトの型チェックを行うため、このポインタをどのように呼び出せるかは不明です。
Codexはこれらのステップを完了するために目標をリセットしました。これはサイドベットです。成功するかもしれませんが1、現実的に考えてみましょう。WebKitへのアクセスしやすさと任天堂のBBP報酬は、おそらく多くの人がここに目を向けるよう動機付けたでしょう2。
それが実行されている間、他の選択肢を探ってみましょう。
smilehax2経由のコード実行
私のアイデアは、Switch 1ゲームをターゲットにすることでした。理由は2つあります。
- それらは開発者によって作成されています。PACとCFIに対処する必要性は低いでしょう。特にSwitch 1はPACをサポートしていないためです。
- Switch 1の信頼のルートは所有されているため、コードを復号化して読み取ることができます。
ブラウザ攻撃面の利点(JavaScriptからのオーケストレーション、既存のn-dayの使用など)を維持しつつ、ブラウザを埋め込んでいるSwitch 1ゲームを見つけることで、WebKitの緩和策の欠点を回避できるでしょうか?Electronのアナロジーですが、ゲーム用なので、Discord RCEのようなエクスプロイトを実行できるかもしれません。現時点では、このアプローチの発見を公に開示することはできませんが、幸いなことに、他のSwitch 1ゲームも存在します。
特に、SmileBASICに興味があります。これは、smilehaxで以前にハッキングされ、3DSでユーザー空間コード実行を可能にしたためです。これは巨大なC++コードベースであり、BASICインタープリタに悪用可能なメモリ破損があれば、別のものがあるはずです。
Codexはインタープリタ内で有用な32ビット整数オーバーフローを発見しました。配列から要素を削除する際、インタープリタはposition + count <= array.lengthをチェックしていましたが、その合計はラップして負になる可能性がありました。残りの要素を移動してギャップを埋めるために、次のように左にシフトする必要があります。
memmove(
backing + position * 8, // start of the gap
backing + (position + count) * 8, // elements after the gap
(length - position) * 8 // bytes to copy
);
文字列配列Rの長さが4で、REMOVE R,&H60000000,&H3FFFFFFFを実行したとします。position + countは0x9fffffffにラップします。負の符号付き32ビット整数として、それはarray.lengthより小さいです。8倍の乗算もラップするため、最終的にmemmove(backing, backing - 8, 32)を実行し、前の8バイトを配列に引き込みます。コマンドはOut of memoryエラーを報告しますが、コピーはすでに発生しているため、問題ありません。
これは、SmileBASICの任意の配列の8バイト前に、配列自体へのポインタが含まれているため有用です!REMOVEの後、R[0]に対する操作は、配列のバッキングを文字列として扱います。
これはSwitch 1ゲームだったため、SmileBASICエージェントは私のSwitch 1でエクスプロイトを処理でき、WebKitエージェントはSwitch 2で処理できました。しばらくすると、ROPを取得しました。
[画像: HELLO FROM SWITCH 2と表示されたエラーダイアログ]
sysmodule RWの取得
さて、より特権の高いプロセスを侵害する必要があります。私たちは通常のsysmoduleと組み込みsysmoduleに興味があります。
どのsysmoduleを追求すべきでしょうか?コードを持っていないため、どのsysmoduleがどの特権を持っているかは不明です。
いくつかのCodexインスタンスにsysmoduleコード実行を取得する目標を与えました。ほとんどはオーディオとldnに焦点を当てることを選択しました。オーディオとldnはSwitch 2でバグがあったためだと思います。CodexはNVサービスも調べました。GPUドライバは一般的に有望だからでしょうか?
fs sysmodule
その間、私は組み込みのfs sysmoduleを調べ始めました。理由は次のとおりです。
- Switch 1のコードは、復号化されたコードを提供できることを示しています。
- コード量が多いです。
- ファイルシステムAPIはあまり変わっていないため、Switch 1の
fsモジュールと似ている可能性が高いです。
最初の理由が私にとって最も魅力的です。fsから有用なジェイルブレイクを取得できない場合でも、少なくとも他のsysmoduleの一部を復号化して、Codexがそれらにバグを見つけられるようにできます。ldrやroのような他のsysmoduleは、実行可能メモリをマッピングする権限を持っているため3魅力的ですが、特にSmileBASICからは攻撃対象領域が大きくありません。後でそれらに戻りましょう。
パス正規化におけるバッファオーバーフロー
IFileSystemハンドルには、GetEntryTypeというIPCコマンドがあります。これは、Path::Normalizeで正規化されたパス文字列を取ります。
バックスラッシュを含む親ディレクトリコンポーネント(例: /.. )を持つパスは、ヘルパー関数を呼び出し、0x301バイトの一時ヒープバッファを割り当てます。パスはコピーされますが、バッファよりも大きい場合があり、バッファオーバーフローを引き起こします。笑
オーバーフローさせるための良い犠牲者割り当てが必要です。Codexは、BufferedStorageオブジェクトがファイルのキャッシュエントリを割り当てることを発見しました。そのエントリにダーティビットが設定されている場合、ファイルへの読み書きはキャッシュデータへのポインタをたどります。これをハイジャックして任意の読み書きが可能です4。
ここから、メモリをスキャンしてSmileBASICの接続オブジェクトを見つけ、保存されたPIDを1に設定しました。これにより、私のプロセスは「FullPermission」を持つことになり、特権的なファイルシステム操作を実行できます。内部ストレージとすべての通常のsysmoduleコードをダンプしたので、Codexはオフライン分析を実行でき、私は次にどこへ行くべきかを理解できました。
いずれかの通常のsysmoduleとしてのコード実行の取得
ホームブリューや改造は通常、独自のネイティブコードを実行することに依存します。ダンプを見ると、実行可能コードをマッピングする権限を持つ唯一の通常のsysmoduleはroです。これらのsysmoduleはすべてCFI、PAC、XOMを持っています。インターフェースも非常に最小限のようです。roで直接メモリ破損を探すよりも、ロジックバグを追求する方が有望に見えました。
fsが復号化されたコードを提供するという最初のポイントに戻りましょう。これは、通常のsysmoduleのコードをダンプするのに役立ちました。このインターフェースの意図された機能は何でしょうか?プログラムのロードですか?
ゲームと通常のsysmoduleは、Nintendo Content Archives(NCA)として配布され、ExeFSと呼ばれるファイルシステムイメージが含まれています。これには、メイン実行可能ファイルmainとプログラムメタデータmain.npdmのファイルが含まれています。
プログラムをロードするために、組み込みのldr sysmoduleはfsにExeFSを開くように要求し、fsはファイルシステムへのIPCハンドルで応答します。fsは、NCAヘッダーに関する2つの暗号情報も提供します。ヘッダーのSHA-256ハッシュと、そのハッシュに対する開発者の署名です。ローダーはこれらの情報を使用して2つの署名チェックを実行します。
- NPDMのプロセス権限と開発者の公開鍵に対する任天堂の署名は有効か?
- NCAヘッダーハッシュに対する開発者の署名は有効か?
理論的には、これは暗号学的に健全に見えます。
- NPDM署名はプロセス権限と開発者キーを固定します。
- キーはNCAヘッダーの署名を固定します。
- 署名はNCAヘッダーのハッシュを固定します。
- ハッシュはNCAヘッダーのコンテンツ(ファイルシステムハッシュを含む)を固定します。
- それらのハッシュは実行可能コンテンツを固定します。
しかし、実際には、提供されるファイルシステムがNCAヘッダーのファイルシステムハッシュと一致することを保証するのはfsモジュールの責任であり、それにはARWがあるため、最後の主張については懐疑的です。
任意の読み書きからネイティブコード実行へ
実際、ARWを使用すると、ローダーのExeFSオブジェクトを、私たちが作成した一時ファイルシステムに置き換えることができます。このファイルシステムは、ローダーの最初のチェックのために有効な開発者main.npdmを提供する必要がありますが、実行可能ファイルmain自体は任意のもので構いません。ローダーがファイルシステムを開く時点までに、fsは元のファイルシステムに対して有効なNCAヘッダーハッシュと署名をすでに提出しているため、2番目のチェックはパスします。
実行する書き込みは2つあります。
- 最初の書き込みは、ローダーのExeFSのドメインテーブルエントリに、私たちが作成した一時ファイルシステムオブジェクトを配置します。この一時ファイルシステムには、元の
main.npdmと私たちの置き換えられたmainが含まれています。 - 2番目の書き込みは、ローダーのExeFSオブジェクトを、私たちの一時ファイルシステムのドメインテーブルエントリに配置します。これは、リファレンスカウントを整合させ、
fsがクラッシュするのを防ぐためだけです。
これらの書き込みは、ローダーがExeFSファイルシステムを要求した後、fsが後続のファイル要求を処理する前に行われる必要があります。これをどのように保証するのでしょうか?
fsプロセスは、そのIPC接続のリストを維持しています。接続に新しいIPCメッセージがある場合、fsはそれを処理します。任意の読み書きを使用して、ローダーのIPC接続のためにfsが待機しているハンドルを、SmileBASICから制御できるイベントハンドルに置き換えることができます。これにより、fsがローダーメッセージを処理するタイミングを制御できます。
これにより、main.npdmを持つ任意のプログラムとして任意のコードを実行できます!
組み込みsysmoduleのネイティブコード実行の取得
どのようなmain.npdmがありますか?まあ、すべてです。ARWを使用すると、ローダーのfsp-srv接続を使用して、任意の通常のsysmoduleまたはゲームのNPDMをコピーできます。
この機能を使用して、roのNPDMを使用して独自のコードを起動します。前述のように、roのNPDMは、カーネルSVC(SetProcessMemoryPermission、MapProcessCodeMemory、MapProcessMemoryなど)を呼び出す権限を与えます。この場合、MapProcessMemoryを使用して、fsの既存のコードの書き込み可能なビューを作成します。このビューを読み取ることで、fsのコードをダンプすることもできます。
しかし、これらの呼び出しにはプロセスのKProcessハンドルが必要です。fsのハンドルを取得するには、ARWを使用してIEventNotifierの4バイトハンドルを0xffff8001に上書きし、SmileBASICからGetEventHandleを呼び出します。カーネルはこの値を「現在のプロセスのハンドル」と解釈するため、SmileBASICはfsのKProcessハンドルを取得します。
このハンドルをカスタムroプロセスに送信し、fsのコードをパッチします。
結論
いずれかの通常のsysmoduleの権限で任意のコード実行が可能になるのは非常に強力です。各デバイスのドライバを最初にエクスプロイトすることなく、ハードウェアを調査できます。これがホームブリューコミュニティがCFWに向けて進歩するのに役立つことを願っています。
短期的には、興味深いと思われるいくつかの次のステップがあります。
fssysmoduleエクスプロイトをWebKitに移植すること。どのバグを悪用したいかが分かった今、任意のIPC呼び出しを実行できることはそれほど価値がありません。WebKitの既存のファイルシステムコードで使用されているデータを変更することで、fsでARWを実行できる可能性があります。- このジェイルブレイクはテザード型であり、
fs以外の組み込みsysmoduleを侵害する方法は現在ありません。fsは組み込みsysmoduleであるため、SMはfsからのサービスACLチェックをスキップし、smhaxと同じ機能を提供します。他の組み込みモジュールにバグが見つかるかどうか疑問です。 fsでのネイティブコード実行により、その暗号化ハードウェアを直接オーケストレーションできます。例えば、NCA4の復号化に使用されます。- エクスプロイトはSwitch 1でもテストされましたが、まだパッケージ化していません。モッドチップの機能は提供しませんが、外部ハードウェアは必要ありません。
今のところ、今後のSwitch 2のホームブリュー、エミュレーション、改造のすべてを見るのが楽しみです。
フットノート
-
成功しませんでした。↩
-
WebKitエクスプロイトを試すことは本質的に悪いことではありません。価値のあることが分かっているsysmoduleにバグがある場合、データのみの攻撃でWebKitでそれを悪用しようと戻ることは理にかなっているかもしれません。これは、WebKitはオフラインでアクセス可能であり、任天堂がeShopタイトルのようにそれを撤回できないためです。しかし、私たちの研究のこの段階では、イテレーション速度のコストに見合うものではありません。↩
-
いつか任天堂が、カーネルまたはセキュアモニターのみが復号化できる暗号化されたブロブを介して、通常のsysmodule、さらにはゲームコードをマッピングするようになるのか疑問に思います。その場合、ユーザー空間プロセスはシステム上のコードを一切観察できなくなります。ユーザー空間にRXマッピングは存在しなくなります。新しいプロセスを開始するローダーモジュールでさえ、提供する暗号化されたブロブをロードするために、より高い例外レベルに要求するでしょう。ゲームが実行時に独自のコードを読み取れないなどのトレードオフがあるでしょう。しかし、任天堂はすでにJIT sysmoduleのような動的コード機能を削除し始めています。Switchがこの方向に向かっているのか疑問です。幸いなことに、その日は今日ではありません。↩
-
当初、Codexの読み書きプリミティブは、バッキングバッファポインタを変更するたびにキャッシュオブジェクトでバッファオーバーフローを繰り返していました。これは、各変更がシステムをクラッシュさせる可能性があるため、多くのポインタを連続してたどる際に安定性に影響します。ここでと同様のトリックを使用できます。オーバーフローの一部を使用して、最初のキャッシュエントリのバッキングバッファポインタを別のキャッシュエントリのメタデータに向けさせることができます。その後、さらにオーバーフローをリスクすることなく、2番目のキャッシュエントリのポインタを置き換えることができます。↩