
ENLBufferPwn脆弱性に関する情報とPoC
| CVE: | CVE-2022-47949 |
| CVSS v3.1: | 9.8/10 (緊急) |
| 著者: | PabloMK7, Rambo6Glaz, Fishguy6564 |
| 報告: | 2021年8月8日 (マリオカート7) 2022年4月14日 (影響を受けたWiiUおよびSwitchゲーム向け) |
| 開示: | 2022年12月22日 |
ENLBufferPwn は、Nintendo 3DS以降の任天堂ファーストパーティ製ゲーム複数作品に共通するネットワークコードの脆弱性で、攻撃者が被害者とオンライン対戦を行うだけで、被害者の本体上でリモートからコードを実行できるようにするものです(リモートコード実行)。この脆弱性は2021年に複数の人物によって独立して発見され、2021年から2022年にかけて任天堂に報告されました。最初の報告以降、任天堂は影響を受ける多くのゲームでこの脆弱性を修正しています。このリポジトリの情報は、任天堂の許可を得た上で安全に開示されています。
この脆弱性は、CVSS 3.1計算機で 9.8/10 (緊急) と評価されています。
これまでに脆弱性の影響を受けていたことが判明しているゲームの一覧は以下のとおりです(記載されているSwitchおよび3DSゲームはすべて脆弱性を修正するアップデートを受けているため、現在は影響を受けません):
ENLBufferPwn の脆弱性は、任天堂のファーストパーティ製ゲームの多くで使用されているネットワークライブラリ enl (マリオカート7では Net) に存在するC++クラス NetworkBuffer のバッファオーバーフローを悪用します。このクラスには、他のプレイヤーから届いたデータをネットワークバッファに格納する Add と Set の2つのメソッドがあります。しかし、これらのメソッドはどちらも、入力データが実際にネットワークバッファに収まるかどうかをチェックしません。入力データは攻撃者が制御できるため、攻撃者とオンライン対戦セッションを行うだけで、遠隔の本体でバッファオーバーフローを発生させることができます。適切に行われた場合、被害者は自分の本体で脆弱性が発動したことに気づかない可能性もあります。このバッファオーバーフローの影響はゲームによって異なり、ゲームのメモリへの無害な改変(3DSでホームメニューを繰り返し開閉するなど)から、以下で示すような本体の完全な制御奪取といったより深刻な動作までさまざまです。
このレポートの残りはマリオカート7に焦点を当てます。3DSには(例えばASLRなどの)セキュリティ対策がないため、この脆弱性の最も深刻なケースだからです。ただし、Switchゲームでは、攻撃者にデータを送り返すために使用される NetworkBuffer を乗っ取る(そしてヒープやコードへのポインタをリークさせる)ことで、ASLRをバイパスできる可能性があります。
以下の動画は、マリオカート7におけるこの脆弱性の深刻なケースを示しています。攻撃者が制御する本体(左側)が、無改造の本体(右側)を完全に乗っ取ります。ユーザーが行う操作は、攻撃者とのオンライン対戦セッションに参加することだけです。この例では「コミュニティ」機能を使用しています(「コミュニティ」を使用したのは、脆弱性を隔離された環境で安全にテストし、公開ロビーでプレイしている他のユーザーに影響を与えないようにするためです)。乗っ取りは、ROPペイロードを遠隔の本体にコピーしてから実行することで行われます。このROPペイロードは、OSの他の脆弱性を利用して権限を昇格させ、完全な制御を獲得します。動画では、遠隔の本体がCFWインストーラ(SafeB9SInstaller)の実行を強制されています。同じ手法を使用すると、理論上はアカウント情報やクレジットカード情報の窃取や、本体に内蔵されたマイク/カメラを使用した不正な音声/映像の録画が可能になります。
前述のとおり、この脆弱性はクラス NetworkBuffer の2つのメソッドにおける確認漏れが原因です。以下は、このクラスとメソッドのC++による実装です:
class NetworkBuffer
{
public:
u8 bufferType;
u8* dataPtr;
u32 dataSize;
u32 currentSize;
void Set(u8* newData, u32 newDataSize);
void Add(u8* newData, u32 newDataSize);
}
void NetworkBuffer::Set(u8* newData, u32 newDataSize)
{
memcpy(this->dataPtr, newData, newDataSize);
this->currentSize = newDataSize;
}
void NetworkBuffer::Add(u8* newData, u32 newDataSize)
{
memcpy(this->dataPtr + this->currentSize, newData, newDataSize);
this->currentSize += newDataSize;
}
ご覧のとおり、Set と Add のどちらも、受信した newDataSize がバッファサイズ(dataSize)に収まるかどうかをチェックしていません。この NetworkBuffer クラスはオンラインのプレイヤー間でデータをやり取りするための汎用コンテナとして使用されるため、newData の内容と newDataSize の値は攻撃者が制御でき、バッファオーバーフローを実行できます。
ネットワークライブラリのもう1つの特徴は非同期であることです。これにより、データがバッファに格納されている間もゲームは他の操作を実行できます。これを実現するために、ダブルバッファ方式が使用されています。これにより、ゲームは新しいデータを受信している間にも、NetworkBuffer 内の受信済みデータにアクセスできます。2つ目の NetworkBuffer が満杯になるとバッファが入れ替えられ、このサイクルが繰り返されます。
このPoCでは、Miiデータの受信に使用されるダブル NetworkBuffer (bufferType = 9) が悪用されます。ヒープ割り当ての順序により、NetworkBuffer オブジェクトとその内容はたまたま隣り合わせに配置されます。また、ダブルバッファの2つのバッファはメモリ上で連続しています。次の図は、バッファ割り当て後のメモリの様子を示しています:

以降、画像の上部のバッファを Buffer0、下部のバッファを Buffer1 と呼びます。この知識を利用すると、攻撃者は Buffer0 でバッファオーバーフローを発生させ、Buffer1 の属性(dataPtr メンバを含む)を上書きできます。3DSゲームはASLRを実装していないため、遠隔の本体のすべてのメモリ位置が既知であり、dataPtr を任意の場所に向けることができます。ゲームがバッファを入れ替えると、新しいデータがその任意の場所にコピーされます。以下の手順を実行することで、任意のサイズのペイロードを遠隔の本体にコピーできます:
Buffer0 で送信します。これにより、遠隔の本体で Buffer1 の dataPtr を上書きするオーバーフローが発生します。Buffer1 の内容を任意のデータで設定します。これは、遠隔の本体の Buffer1 の dataPtr が指す任意のアドレスにコピーされます。1 から再度開始します。以下は、ゲームの通常動作と、その後に上記の手順を実行した場合のアニメーションです。

このリポジトリには、この脆弱性を悪用して、コンパイラフラグに応じて以下の操作を実行するPoCが含まれています:
本体間の通信はUDPで行われるため、このPoCはパケットロス検出を一切実装していないことに注意してください。最良の結果を得るには、PoCコードにパケットロス処理を実装するか、本体を同じネットワーク上に配置してパケットロスの可能性を減らす必要があります。
脆弱性のある NetworkBuffer クラスの修正案も提供されています。
この脆弱性はWiiU版とSwitch版のマリオカート8にも存在しますが、実証されているのはWiiU版のみです。
この脆弱性は複数のユーザーによって独立して発見されましたが、その多くは脆弱性情報を非公開にしておくことを選択しました。しかし、以下の人々が任天堂への安全な開示を担当しました:
法律で認められる範囲で、
PabloMK7, Rambo6Glaz and Fishguy6564
は、ENLBufferPwn に関するすべての著作権および関連する権利または隣接する権利を放棄しています。
この作品は以下の国から公開されています:
スペイン。