WorstFit: Windows ANSI の隠れたトランスフォーマーを公開
TL;DR
- Windows の内部文字セット変換機能である Best-Fit を悪用し、新しい攻撃面を発見した。
- この機能をパス・トラバーサル、引数インジェクション、リモートコード実行(RCE)などの実際の攻撃に転用できる。
- 問題の根本原因はコンパイラの挙動、C/C++ ランタイム、開発者の誤りにある。
- オープンソースエコシステムにおける修正導入の難しさも議論される。
Windows エンコーディング解読
初期: ANSI とコードページ
- Windows は当初 ANSI エンコーディングを使用しており、これは特定の言語には効果的だったが、混在した文字セットを処理できなかった。
- 様々なコードページが存在し、各コードページは特定の言語をサポートしている。
ユニコード時代: UTF-16
- Windows は1990年代半ばにユニコードへ移行し、ほぼすべての言語の文字を単一の標準で表現できるようになった。
- 初期は UCS-2 を使用していたが、すぐに UTF-16 にアップグレードされた。
二重エンコーディング時代
- Windows は以前の ANSI コードページをサポートするために、2 つの API バージョンを実装した。
- ANSI API と ユニコード API があり、開発者は必要なデータ形式を簡単に取得できる。
Best-Fit のメリット
- Windows の "Best-Fit" 文字変換は、UTF-16 から ANSI へ変換する際に、対象コードページに存在しない文字を処理する方法である。
- たとえば、
∞ 記号は Windows-1252 コードページにないため、Microsoft はそれを 8 にマッピングする。
WorstFit: Windows の新たな攻撃面
🔥 東アジアの悪夢 - CVE-2024-4577
- CVE-2024-4577 は、中国語または日本語のコードページで構成された PHP-CGI サーバーを、単純な
?%ADs リクエストで破壊できる脆弱性である。
- Best-Fit の動作により、U+00AD(ソフトハイフン)がダッシュ (-) にマッピングされてバイパスが可能になる。
🔥 ファイル名の悪用
- ファイル名処理において WorstFit を悪用し、パス・トラバーサル・ペイロードに変換することができる。
- たとえば、Chrome V8 の Developer Shell (
d8.exe) は ANSI API を用いて現在の作業ディレクトリを取得している。
🔥 引数分割
GetCommandLineA の出力を操作して、WorstFit の挙動をコマンドライン構文解析で悪用できる。
- たとえば、
" --use-askpass=calc " の入力で、システム上で calc.exe が実行できる。
結論
- Best-Fit の挙動は、システムレベルでの変換過程において攻撃面を提供し、これはさまざまなツールで脆弱性を引き起こす可能性がある。
- 標準ライブラリやプログラミング言語の関数では、この種の攻撃を完全に防ぐことはできない。
1件のコメント
Hacker News コメント
Microsoft は少なくとも1年前からこの問題を認識していた。CA2101 という特別なコード解析ルールで
best fitマッピングの使用を推奨していないことを示していた。セキュリティ脆弱性には言及していたが、細かい点は曖昧だった。この問題はシステム的なものだ。Microsoft は Unicode を ASCII に変換するための「best fit」コードマッピングを提供している。このマッピングは多くの場所で使われており、Microsoft が下位互換性を重視するため継続して含めておく必要がある。基本的にデフォルトであらゆる場所に組み込まれている。
主に不正なコードポイントをスラッシュ、ハイフン、引用符などに変換する形で悪用される。現代のプログラミング言語では正しく評価されるが、シェルコマンドや Win32 API に渡すと問題が生じる。
curl のメンテナーは「curl が被害者」と言っているが、問題の原因は他にある。サーバー側でユーザー入力を検証する際と、システムライブラリに適用する際で処理が異なると問題が起きる。
Win32 空間で「best fit」変換を選択的に無効化することが解決策になる可能性がある。オープンソースの提供側はこれをベストプラクティスとして追加できる。
Windows は、マリガン(Munchkin)のカードゲームのように、複数の機能が偶然組み合わさって大きな脆弱性を生み出すことがある。ANSI サブシステムを UTF-8 に変換することでこの問題を軽減できる。
Microsoft は NT 3.5 以来、ANSI の段階的な廃止と Wide Character API の使用促進を進めてきた。しかし、Microsoft の C/C++ ランタイムライブラリの実装方式が主な障害になっている。
A系関数を使って Unicode 変換失敗を報告せず、best fitアプローチを採用する。Microsoft がすべての Windows エディションでデフォルトで UTF-8 を有効化する可能性は低い。古いアプリケーションが特定のコードページや1バイト文字に依存しているからだ。
win32 xxxAAPI で Best-Fit 論理を除去する方が、問題が少なくなるだろう。アプリケーションで「Ansi」コードページを UTF-8 に強制設定する方法は2つある。1つは Manifest ファイルを使う方法、もう1つは「App Locale」ツールを使う方法。
個人の Windows コンピュータで UTF-8 モードを設定して、このバグから回避できた。古い外国製ゲームで文字化けが起きたため、そうしていた。
この問題の解決は、単に
main()を wide-character 版に置き換えるだけでは難しい。すべての変数をwchar_t *に変換する必要があり、これは苦痛でエラーを起こしやすい。charを使い続けることができる。ANSI や OEMCP 文字列と UTF-8 文字列を混在させないように注意する必要がある。Windows API が best-fit 変換を提供していることは知っていたが、デフォルト動作だったとは知らなかった。この機能は無効化されるべきだ。
ベータチェックボックスが ActiveCodePage を UTF-8 に設定するのと同等かを気にしていた。GDI はプロセス単位のコードページではなくグローバルなコードページだけを従う。UTF-8 を完全に選択できないのは残念。