画像の解析とレンダリングは、最新のオペレーティング システム (OS) の基本機能です。画像解析は簡単にアクセスできる攻撃対象領域であり、そのような機能でのリモート コード実行や情報漏えいにつながる脆弱性は、攻撃者にとって価値があります。このマルチパート ブログ シリーズでは、Windows OS に組み込まれている画像パーサーと関連するファイル形式をレビューします。具体的には、ハーネスの作成、コーパスの検索、脆弱性を見つけるためのファジングについて説明します。このシリーズのパート 1 では、カラー プロファイルに注目しています。これは、画像フォーマット自体ではなく、画像に定期的に埋め込まれているものです。
ICC カラー プロファイルとは何ですか?
ウィキペディアは、 ICC カラー プロファイルの十分すぎる説明を提供しています。 (ICC). プロファイルは、デバイスのソースまたはターゲットの色空間とプロファイル接続空間 (PCS) の間のマッピングを定義することにより、特定のデバイスまたは表示要件の色属性を記述します. この PCS は CIELAB (L*a*b*) のいずれかです.または CIEXYZ. マッピングは、補間が適用されるテーブルを使用して指定するか、変換用の一連のパラメーターを介して指定できます。 」
簡単に言えば、ICC カラー プロファイルは、画像に埋め込まれ、ICC がサポートするソフトウェアが画像を処理するたびに解析されるバイナリ ファイルです。
仕様
ICC の仕様は約 100 ページあり、ざっと目を通しやすいはずです。仕様を読むと、ファイル形式、さまざまな種類のカラー プロファイル、および色変換の背後にある計算をよりよく理解できます。さらに、ファイル形式の内部構造を理解することで、ファジングの最適化、適切なコーパスの選択、ファジング辞書の作成に使用できる情報が得られます。
Windows のカラー マネージメントの歴史
Windows は、Windows 95 で Image Color Management (ICM) バージョン 1.0 の出荷を開始し、Windows 98 以降ではバージョン 2.0 を出荷しました。 Windows Vista 以降では、Windows Color System (WCS) 1.0 に対する大幅なオーバーホールが行われました。 ICC カラー プロファイルはバイナリ ファイルですが、WCS カラー プロファイルはファイル形式として XML を使用します。このブログ投稿では、ICC カラー プロファイルに焦点を当てます。
Microsoft には、サポートされている Windows APIのリストがあります。 OpenColorProfileなど、明らかに名前が付けられた API のいくつかを調べると、MSCMS.dll に実装されていることがわかります。この DLL は一般的なエントリ ポイントであり、Microsoft の Color Management Module (CMM) および Adobe の CMM などのサード パーティの CMM のロードをサポートします。 Microsoft の CMM (ICM) は、system32 ディレクトリに ICM32.dll として存在します。
Windows の CMM は、Windows 95 の時代にサードパーティによって作成されたものであり、現在でも多かれ少なかれ同じコードで出荷されています (数十年にわたるセキュリティ修正が適用されています)。このような古いモジュールを見ると、新しい脆弱性を発見できる可能性があります。しかし、これは小規模なモジュールでもあり、内部の製品セキュリティ チームと外部の研究者の両方による複数回のレビューとファジングを経た可能性があり、私の希望はある程度低下しています。 ICM32 の最近の脆弱性を探すと、Project Zero と ZDI の研究者による 2017 年から 2018 年にかけて複数のバグが見つかりましたが、2019 年以降は比較的沈黙しています。
ハーネスを作る
MSDN には ICM API のリストがありますが、Windows で ICC 関連の操作に使用される API シーケンスを見つける必要があります。 API シーケンスを見つける方法の 1 つは、Windows DLL と EXE の逆アセンブリを検索して、使用されているカラー プロファイル API を見つけることです。もう 1 つのアプローチは、Little CMS (LCMS) などのオープン ソース カラー マネージメント システムのハーネスを見つけることです。これらは両方とも、カラー プロファイルを開いて色の変換を作成する機能を備えた非常に小さな API セットを指すことになります。
この情報を考慮して、単純な初期ハーネスが作成されました。
|
リスト 1: ハーネス
コーパスと辞書のハンティング
複数のカラー プロファイルを提供するサイトは、インターネット上で見つけることができます。カラー プロファイルのもう 1 つの主なソースは画像です。多くの画像ファイルにはカラー プロファイルが含まれていますが、カラー プロファイルをスタンドアロン ファイルにダンプするには、プログラミングやツールが必要です。
仕様に目を通すだけで、7 つの異なるカラー プロファイルすべてから少なくとも 1 つのサンプルがコーパスに含まれていることを確認できます。これをコード カバレッジ情報と共に使用して、ファジング用のコーパスの最初のセットを準備できます。
ファザーが追加のコード パスを見つけるのに役立つディクショナリは、仕様をくまなく調べて一意のタグ名と値のリストを作成することで準備できます。 LCMS などでのオープンソースのファジングの試みから辞書を見つけることもできます。
ファジング
16 コアのマシンを使用して、最初のコーパス セットでハーネスをファジングしました。 MSCMS.dll と ICM32.dll からのコード カバレッジ情報は、ファザーのフィードバックとして使用されました。クラッシュは数日以内に現れ始めました。
CVE-2020-1117 — InitNamedColorProfileData のヒープ オーバーフロー
次のクラッシュは、境界外を読み取ろうとしているときにicm32!SwapShortOffsetで発生します。
|
リスト 2: クラッシュ情報
icm32!SwapShortOffsetは unsigned short 値を読み取り、それらをbswapして同じ場所に保存するため、読み取りプリミティブと書き込みプリミティブの両方がクラッシュします。
unsigned __int16 *__fastcall SwapShortOffset(void *sourceBuff, unsigned int offset, unsigned int len) endBuff = (sourceBuff + len); |
リスト 3: 逆コンパイルされた SwapShortOffset
クラッシュ関数icm32!SwapShortOffsetは、バグの根本原因をすぐには示しません。そのためには、 icm32!InitNamedColorProfileDataまで 1 回呼び出す必要があります。
__int64 __fastcall InitNamedColorProfileData(__int64 a1, void *hProfile, int a3, _DWORD *a4) { … … errCode = CMGetPartialProfileElement(hProfile, ‘ncl2’, 0, pBuffSize, 0i64); // ncl2 要素のサイズを取得 if (エラーコード) errCode を返します。 minSize = pBuffSize[0]; もし ( pBuffSize[0] < 0x55 ) 最小サイズ = 0x55; pBuffSize[0] = minSize; outBuff = SmartNewPtrClear(minSize, &errCode); // ncl2 のバッファを割り当てます … … errCode = CMGetPartialProfileElement(hProfile, ‘ncl2’, 0, pBuffSize, outBuff); // ncl2 要素をバッファに読み込む if (!errCode) { … … totalSizeToRead = カウント * totalDeviceCoord; if ( totalSizeToRead < 0xFFFFFFFFFFFFFFAEui64 && totalSizeToRead + 0x51 <= pBuffSize[0] ) // totalSizeToRead + 0x51 <= 要素サイズ? { currPtr = outBuff + 0x54; // 間違ったオフセット 0x54 が使用されています … … 行う { SwapShortOffset((currPtr + 0x20), 0, 6u); … – カウント; }while(カウント) |
リスト 4: 逆コンパイルされた InitNamedColorProfileData
ここで、コードは「ncl2」タグ/要素を読み取り、ファイルからストリームのサイズを取得しようとします。バッファーが割り当てられ、要素 ‘ncl2’ の完全なコンテンツを読み取るためにもう一度同じ呼び出しが行われます。このバッファーは、デバイス座標のカウントと数を見つけるために解析され、読み取り/書き込みがバッファー サイズで終了することを確認することによって値が検証されます。ここでの脆弱性は、検証に使用されるオフセット (0x51) が、バッファー ポインターを進めるために使用されるオフセット (0x54) よりも小さいことです。このエラーは、3 バイトの範囲外の読み取りおよび書き込みを提供します。
これに対する修正は非常に簡単でした。検証オフセットを 0x54 に変更することで、Microsoft はこのバグを修正しました。
その他の脆弱性
以前の脆弱性を見ると、サイズの読み取り、割り当て、およびコンテンツの読み取りにCMGetPartialProfileElement関数を使用するパターンが見られます。この種のパターンは、制約のないサイズやサイズにオフセットを追加する際の整数オーバーフローなどのバグを引き起こす可能性があります。私はこの機能を追求し、そのようなインスタンスが ICM32.dll 内に存在するかどうかを確認することにしました。
未チェックのオフセット アクセスを持つ 3 つのインスタンスを見つけました: CMConvIndexToNameProfile 、 CMConvNameToIndexProfile 、およびCMGetNamedProfileInfoProfile 。これらの関数はすべて、エクスポートおよび文書化された MSCMS 関数 (それぞれConvertIndexToColorName 、 CMConvertColorNameToIndex 、およびGetNamedProfileInfo ) からアクセスできます。
__int64 __fastcall CMConvIndexToNameProfile(HPROFILE hProfile, __int64 a2, __int64 a3, unsigned int a4) { … … errCode = CMGetPartialProfileElement(hProfile, ‘ncl2’, 0, pBuffSize, 0i64); // 読み取りサイズ if (!errCode) { allocBuff = SmartNewPtr(pBuffSize[0], &errCode); if (!errCode) { errCode = CMGetPartialProfileElement(hProfile, ‘ncl2’, 0, pBuffSize, allocBuff); // バッファに読み込む if (!errCode) { SwapLongOffset((allocBuff + 12), 0, 4u); // 12 > *pBuffSize ? SwapLongOffset((allocBuff + 16), v12, v13); |
リスト 5: 逆コンパイルされた CMConvIndexToNameProfile
CMConvIndexToNameProfileと他の 2 つの関数で発見されたバグは、「 ncl2 」要素の最小長チェックがなく、オフセット 12 と 16 が読み取りと書き込みの両方で直接アクセスされることです。 allocBufferの値が 12 より小さい。
Microsoft は、これらの機能を使用する Windows バイナリがないため、これら 3 つの脆弱性を直ちに修正しないことを決定しました。これとは別に、これらの API を使用している Windows またはサードパーティ ソフトウェアは見つかりませんでした。
結論
このブログ シリーズのパート 1 では、カラー プロファイルを調査し、ハーネスを作成し、コーパスを探し、複数の脆弱性を発見しました。第 2 部では、比較的あまり話題にされていない脆弱性クラスである初期化されていないメモリについて取り上げます。
参照: https://www.mandiant.com/resources/blog/fuzzing-image-parsing-in-windows-color-profiles
Comments