受信日 (通知者:JPCERT/CC)
JVN 一般向け公開日
概要
UNLHA32.DLL は, LZH, LZS 形式に対応した圧縮・展開等の書庫操作を行うライブラリーです。 UNLHA32.DLL には自己解凍書庫ファイルの作成機能が実装されていますが, 作成された自己解凍書庫ファイルには DLL を読み込む際の検索パスに問題があり, 当該書庫と同一ディレクトリーに存在する特定の DLL を読み込んでしまう脆弱性 (CWE-427) が存在します。
影響を受けるモジュール及びシステム
- UNLHA32.DLL Ver 3.00 より前の版を使用して作成された 32 ビット版自己解凍書庫ファイル (WinSFX32U, WinSFX32M, WinSFX32)
- 上記の自己解凍書庫を配布ファイル若しくはインストーラーとして使用しているアプリケーションやサイト等
詳細情報
自己解凍書庫とは, 実行形式ファイルとすることで, 別途アーカイバーのようなツールを必要とすることなく書庫の展開操作を行えるようになっている書庫のことです。
UNLHA32.DLL を使用して作成されるものの内, 32 ビット版である WinSFX32U, WinSFX32M 及び WinSFX32 は起動時にシステムライブラリーを読み込みますが, 当該書庫と同一ディレクトリーに存在する特定の DLL を読み込んでしまう脆弱性が存在し, 任意のコードを実行されたり, クラッシュを引き起こされる可能性があります。
想定される影響
自己解凍書庫を実行することにより, 当該書庫の実行に使用したアカウントの権限で任意のコードを実行されたり, サービス運用妨害 (DoS) 攻撃を受ける可能性があります。
対策方法
UNLHA32.DLL を最新版 (Ver 3.00.0.5 以降。) へバージョンアップした上で, 自己解凍書庫を作成し直してください。 [ダウンロード]
本脆弱性の影響を受けるのは自己解凍書庫の実行時のみのため, 展開済みのファイルやインストールされたプログラム等を改めて展開・インストールし直す必要はありません。
なお, 新版の自己解凍書庫については使用実績が少ないことから, ダウンロード・アップロード時に各種対策ソフトの検疫により削除される可能性があります。 UNLHA32.DLL Ver 3.00 の配布ファイルである ULH3300.EXE については, Symantec へ検体提出してあり 5 月 12 日以降については削除されないことも確認しています。 確認できた限りにおいては, カスペルスキーなど数社の対策ソフトも大丈夫なようです。
回避方法
- 自己解凍書庫が置かれているディレクトリー上に当該書庫以外何もファイルが存在していないことを確認した上で実行する。
- 何らかのアーカイバーを使用して自己解凍書庫に格納されているファイルを直接展開する。
技術情報
Windows においては, ほぼ全てのプログラムが "KERNEL32.DLL" や "USER32.DLL" といったシステムライブラリーに実装されている Win32 API を使用しています。 DLL の利用については, 「存在するか判らない DLL」「必ずしも必要としない DLL」であれば, LoadLibrary() API を使用して DLL のロードを行った上で使用したい API の呼び出し先を GetProcAddress() で得る…といった手順を踏むことになります。
一方, 「Win32 API を使用するために KERNEL32.DLL や USER32.DLL をロード」といった, 「必ず使用する DLL」「システムファイルとして Windows に必ず含まれている DLL」を利用する際には, 例えば USER32.DLL であれば USER32.LIB といった, それぞれの DLL に対応したインポートライブラリーを使用することにより, DLL のロードや呼び出し先の取得といった手順を Windows に任せる…という手法が一般的に採用されます。 インポートライブラリーを使用した場合, プログラムの実行ファイル内に「当該プログラムが使用する DLL と (例えば Win32 API などの) 呼び出す機能」が一覧されたインポートテーブル…つまり, 外部参照の一覧表が作成されます。
インポートテーブルが含まれるプログラムを起動すると, 実際に当該プログラムへ制御が渡る前の段階で, Windows のシステムライブラリーである NTDLL.DLL に存在するイメージローダーが, インポートテーブル上にある外部参照の情報を読み込んで, 必要な DLL のロードと機能の呼び出し先の取得を行います。 読み込んだ DLL がインポートテーブルをもっていれば同様の手順を再帰的に行います。 これらの作業は, 全ての外部参照が解決されるまで繰り返されます。
この際, Windows は定められた優先順位に従って複数のディレクトリーを検索して DLL を読み込みます。 例えば Safe DLL search mode が有効の場合は次のようになります:
- プログラム (今回であれば自己解凍書庫) の置かれているディレクトリー
- ウインドウズ・システムディレクトリー
- 16 ビット用システムディレクトリー
- ウインドウズディレクトリー
- カレントディレクトリー
- 環境変数 PATH で指定された各ディレクトリー
この時に, プログラムの置かれているディレクトリーが最も優先順位の高い検索対象となっているため, システム DLL と同じ名前の細工された攻撃用 DLL をプログラムと同じディレクトリーへ置くことで, 優先的に攻撃用 DLL を読み込ませることが可能となります。
DLL の読み込み時には必ず初期化が行われることから, 初期化ルーチンにおいてプログラムの実行に使用したアカウントがもつ権限の範囲で任意のコードを実行することが可能となります。 初期化ルーチンを利用できることから, 細工された攻撃用 DLL がシステム DLL の仕様を満たしている必要はありません。
補足情報
今回の脆弱性は, MHSVI#20100824:「LHMelt における安全でないライブラリーのロードによりリモートでコードが実行される脆弱性」と同じ脆弱性ではありますが, 置かれている状況は大きく異なります。 今回「安全でないライブラリーのロード」を行っているのは Windows システムの一部である NTDLL.DLL (上のイメージローダー) であり, しかもそれは「ユーザーが起動しようとしたプログラムへ制御が渡る前」に行われています。 つまり, プログラム側には通常の方法では対処のしようがないことになります。
この点は市販ソフトや巷に溢れている Windows プログラムも同じで, 言ってしまえば「ほぼ全てのプログラム」が該当してしまうことになります。 「市販ソフトなど一般的なアプリケーションでは, 書き込みに管理者権限を必要とする "C:\Program Files" ディレクトリー配下へインストールされるケースが多く, その分安全性が若干高まっている」に過ぎません。 反対に, インストーラーやツール, 自己解凍書庫といったプログラムは「置き場所 (実行場所) を選ばず使われることから, 脆弱性として問題になっている」といえます。
修正版自己解凍書庫の限界
今回の脆弱性の素となっている NTDLL.DLL (イメージローダー) の仕様ですが, MSVCRT.DLL や CTL3D.DLL, CTL3DV2.DLL といった DLL に始まり, MFC32.DLL などの MFC ライブラリーで大問題となってしまった「バージョンヘル」の軽減策として, 「プログラムと同じディレクトリーへ DLL を配置することで使用 DLL をオーバーライドする」という手法が定着してしまったことから, 今となっては仕様変更を行えない事態に陥ってしまっています。 また, 過去のしがらみから, 本来行われるべき「(システムライブラリーについて) システムディレクトリー以外から読み込まない, 読み込めないようにする」といった保護策についても, 一部のシステム DLL にしか講じられていません。
この辺りは MSDN でも説明されていて, それを避けるために用意されているのが, SetDllDirectory() や SetDefaultDllDirectories(), SetSearchPathMode(), LoadLibraryEx() (の追加機能) といった API 群, そしてリンカーとヘルパーライブラリーによる「DLL の遅延ロード」です。 遅延ロードは, ランタイムの一部ともいえるヘルパーライブラリーがプログラムの起動後に LoadLibrary() + GetProcAddress() を行う機能で, 先に書いた「プログラム側が LoadLibrary() + GetProcAddress() する」のと本質的に変わりありません。
そこで, プログラム側 (自己解凍書庫側) でこれらの機能を使って脆弱性の回避を行うことになるわけですが, それには以下のような制限が付きまといます:
- KERNEL32.DLL の遅延ロードを行うことは出来ない (これが読み込まれていないと, そもそも DLL のロードを行えない…どころか, ほぼ何も出来ない。)
- SetDefaultDllDirectories() などの設定に因らない, 独自ルールの検索と読み込みが行われる DLL の存在。 Windows 7 での SSPICLI.DLL 辺りが該当し, 一捻り加えた対応を行わないと, 素直な対策だけではハイジャックが成立してしまう。 遅延ロードなどは逆にハイジャックへ寄与することになるので注意。
- 自身 (自己解凍書庫) に接触してくる他プログラム (IME や (システムを含む) 監視ツール・対策ソフトの類い) の存在。 「英仏環境 (キーボード) だと大丈夫なのに日本語環境だとハイジャックが成立」などはこれ。
- システムにより保護されている DLL と保護されていない DLL の, 呼び出し状況による依存関係の変化で生じる影響。 「対策済みとされていたソフトが BCRYPT.DLL 辺りで転ける」などはこれ。
結果, 修正版の自己解凍書庫には以下のような限界が存在します。
- Windows 10 や Windows 8/8.1 については現状回避できているものの, OS のアップグレードや修正パッチの適用, 対策ソフトの変更といった環境の変化により DLL の依存関係が変化することで, 対応できなくなる可能性がある。
- Windows 7 については, 少なくとも SSPICLI.DLL について独自ルールの検索と読み込みが行われていることから, 追加の対策が必要となる (対応済みではあるものの, 危険性が高いといえる。)
- Windows Vista については, KERNEL32.DLL が NTDLL.DLL により読み込まれる時点で APPHELP.DLL に対する DLL ハイジャックが成立してしまうことから, 完全な対応は不可能。
なお, APPHELP.DLL に限って言えば, 単に同名の DLL を作成した程度では, 初期化ルーチンを含めて実際にコードが実行されることはない。 ただし, DLL のロードが試みられるため, 実行ファイル以外の形式が使われた場合, システムによる「○○は Windows 上では実行できないか、エラーを含んでいます。」の警告が表示される。 どちらの場合も, 実際にロードされるのはシステムディレクトリー上の正規版 APPHELP.DLL となる。
- Windows XP 以前についてはシステムに保護機能が備わっていず, 多数の DLL が Vista での APPHELP.DLL と同じ状況に陥っていることから, 対応自体が不可能。
- Windows 7/Vista については, KB2533623 の更新プログラムを適用していることが前提。 これが適用されていないと SetDefaultDllDirectories() API などが使えないことから, 根本的に対応が破綻する。
Windows XP 以前の OS はともかく, Windows 7 や Vista については, 可能であれば「システムディレクトリー (若しくは正規なものと判明している DLL が置かれているディレクトリー) 以外に存在する SSPICLI.DLL や APPHELP.DLL について, 実行やアクセスを拒否する設定を行う」ことで, 危険性の軽減を図ることが可能です。
余談
今回の脆弱性については, 元々 7 年近く前となる前回の対応時に判明していて, しかも昨年 (2016 年) 1 月にはソフトウェア作者筋の繋がりから「こんなネタで手入れが始まったよ~」と情報ももたらされていたのですが, 「対応必須の Win32s で動作しなくなるし, 元々 Win の仕様バグで大多数のプログラムが影響を受ける上にプログラム側で対処しきれない以上, MS が修正すべき問題だよね~」「んだんだ」とスルーしていたのが実情です。 (^^;)
しかし, この春ついに順番が回ってきたらしく, しかも「対応が無理なら情報だけでも公開させて欲しい」という話だったことから, 「それって一層ダメなパターン…」というわけで, 今回対応するに至りました。 Win32s には根性で対応しました。 (笑)
更新履歴
- 脆弱性情報の公開 [May.15,2017]
- 参考情報に JVN の情報 (JVNTA#91240916) を追加 [May.25,2017]
- 「修正版自己解凍書庫の限界」の項目について, Windows Vista での APPHELP.DLL に関する問題の情報を更新 [Aug.19,2017]
- 参考情報に「Windows アプリケーションにおける DLL 読み込みに関する脆弱性について」(MHVI#20170718) の情報を追加 [Aug.23,2017]
- 参考情報に IPA の情報「【注意喚起】Windowsアプリケーションの利用における注意」を追加 [Sep.30,2017]
- JVN での情報公開に伴う情報を追加 [Jan.31,2019]
参考情報