
CVE-2018-4343: macOSおよびiOSのGSSCredデーモンにおける解放後使用(use-after-free)の概念実証
macOS および iOS 上で root として動作する com.apple.GSSCred XPC サービスは、"move" コマンドを適切に実装しておらず、do_Move 関数内で use-after-free 状態を引き起こします。GSSCred サービスは、iOS 上のデフォルトのアプリケーションサンドボックスからアクセス可能です。
このプログラムは、脆弱性を利用して GSSCred サービスをクラッシュさせます。GSSCred 内でのコード実行を達成するには、競合ウィンドウ中に制御されたデータで解放済みメモリを上書きすることに依存します。macOS High Sierra 10.13.2 Beta 17C79a でテスト済みです。
以下は、Heimdal-520 の do_Move の関連部分で、重要でないエラーチェックは省略されています:
//
// 1. from と to は、XPC リクエストからデシリアライズされた完全に制御可能な UUID オブジェクトです。
//
CFUUIDRef from = HeimCredMessageCopyAttributes(request, "from", CFUUIDGetTypeID());
CFUUIDRef to = HeimCredMessageCopyAttributes(request, "to", CFUUIDGetTypeID());
...
//
// 2. credfrom と credto は、from と to の UUID で検索された HeimCredRef オブジェクトです。
// CFDictionaryGetValue() は参照を追加せずにオブジェクトを返します。from と to の UUID が
// 同じ場合、credfrom と credto は両方とも同じオブジェクトを参照することに注意してください。
//
HeimCredRef credfrom = (HeimCredRef)CFDictionaryGetValue(peer->session->items, from);
HeimCredRef credto = (HeimCredRef)CFDictionaryGetValue(peer->session->items, to);
...
//
// 3. credfrom がディクショナリから削除されます。外部参照が1つしかなかったため、
// これにより credfrom が解放されます。
//
CFMutableDictionaryRef newattrs = CFDictionaryCreateMutableCopy(NULL, 0, credfrom->attributes);
CFDictionaryRemoveValue(peer->session->items, from);
credfrom = NULL;
...
//
// 4. この時点で credto を確認します。credfrom と credto が同じオブジェクトを指している場合、
// credto は解放された HeimCredRef オブジェクトへの NULL でないポインタです。
//
if (credto == NULL) {
...
} else {
//
// 5. ここで credto をデリファレンスし、解放されたメモリから読み取った値を
// CFDictionaryRef オブジェクトとして CFDictionaryGetValue() に渡します。
//
CFUUIDRef parentUUID = CFDictionaryGetValue(credto->attributes, kHEIMAttrParentCredential);
...
}
このコードは以下の処理を行います:
from と to) をデシリアライズします。リクエストは完全に制御可能なので、これらの UUID の値を任意に設定できます。これら 2 つの UUID が同じかどうかのチェックはありません。from と to) に対応する HeimCredRef オブジェクト (credfrom と credto) を検索します。peer->session->items ディクショナリは、現在接続されているクライアントプログラムに代わって GSSCred が管理するすべての資格情報を格納します。CFDictionaryGetValue 関数は HeimCredRef オブジェクトへの参照を返しますが、それらの参照カウントを増加させないことに注意してください。特に、from と to が同じ UUID の場合、credfrom と credto は両方とも、参照カウントが 1(含む CFDictionary によって保持)の同じ HeimCredRef を指します。credfrom が peer->session->items ディクショナリから削除されます。これは通常安全ですが、 と が異なる UUID の場合、 オブジェクトは解放され、その後は二度と参照されません。しかし、 と が同じ場合、 が後で参照されるため問題が発生します。このプログラムは、この競合ウィンドウを獲得しようとはしません。代わりに、HeimCredRef のデストラクタに attributes フィールドをゼロにさせ、CFDictionaryGetValue 内で NULL ポインタのデリファレンスを引き起こします。
ビルドするには make を実行します。さまざまなビルドオプションについては Makefile の先頭を参照してください。
エクスプロイトを実行すると、GSSCred と交換される XPC メッセージのシーケンスが表示されます:
$ ./GSSCred-move-uaf
create: <dictionary: 0x7ff359e07740> { count = 1, transaction: 0, voucher = 0x0, contents =
"attributes" => <dictionary: 0x7ff359e06b60> { count = 5, transaction: 0, voucher = 0x0, contents =
"kHEIMObjectType" => <string: 0x7ff359e06a00> { length = 19, contents = "kHEIMObjectKerberos" }
"kHEIMAttrBundleIdentifierACL" => <array: 0x7ff359e06a70> { count = 1, capacity = 1, contents =
0: <string: 0x7ff359e06aa0> { length = 1, contents = "*" }
}
"kHEIMAttrUUID" => <uuid: 0x7ff359e06b20> AB000000-0000-0000-0000-000000000000
"kHEIMAttrStoreTime" => <date: 0x7ff359e06c60> Sat Dec 09 15:09:56 2017 PST (approx)
"kHEIMAttrType" => <string: 0x7ff359e06ce0> { length = 17, contents = "kHEIMTypeKerberos" }
}
}
Event: <error: 0x7fff9959cc60> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x7fff9959cfd0> { length = 22, contents = "Connection interrupted" }
}
move: <error: 0x7fff9959cc60> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x7fff9959cfd0> { length = 22, contents = "Connection interrupted" }
}
"Connection interrupted" XPC イベントは、XPC 接続が中断されたことを示しており、おそらく GSSCred がクラッシュしたためです。
GSSCred-move-uaf のコードはパブリックドメインとして公開されています。礼儀として、このコードの一部を参照または使用する場合は、私に帰属していただくようお願いします。
fromtocredfromfromtocredtocredto が NULL かどうかをチェックします。credfrom と credto は等しく、credfrom は NULL ではなかったため、else ブランチに入ります。credto をデリファレンスして attributes フィールドを読み取り、それを CFDictionaryGetValue の最初のパラメータとして渡します。credto が指す解放済みメモリがその間に再割り当てされ、attributes フィールドの位置が特別に細工された偽の CFDictionary オブジェクトを指すように変更された場合、このステップでコード実行が可能になるはずです。