Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2022-42864 — Prueba de concepto para el CVE-2022-42864 de condición de carrera en IOHIDFamily | Kitploit
Herramientas/GitHubGitHub/muirey03/cve-2022-42864
Análisis de VulnerabilidadesExplotaciónExplotación de Binarios
GitHubmuirey03/cve-2022-42864

CVE-2022-42864

Prueba de concepto para el CVE-2022-42864 de condición de carrera en IOHIDFamily

Ver Repositorio
687hace 3 añosRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2022-42864: Cookies Diabólicas

¿Qué es este repositorio?

Esta es mi prueba de concepto (incompleta) del exploit para CVE-2022-42864, una vulnerabilidad de tiempo-de-verificación a tiempo-de-uso en IOHIDFamily que fue corregida en iOS 16.2 / macOS Ventura 13.1.

¿Cuál es el estado de la prueba de concepto?

El exploit actualmente logra la misma primitiva "kfree arbitrario" utilizada en el exploit multicast_bytecopy. Sin embargo, el flujo posterior de exploit de multicast_bytecopy ha sido fuertemente mitigado, por lo que esto no es un exploit completo, solo demuestra la severidad del problema.

¿Debería ejecutar esto?

Si tienes que preguntarlo, no. Esto no hace nada útil, solo provoca un pánico del kernel. No me hago responsable por cualquier pérdida de datos o inestabilidad que este código pueda causar.

El error

El comentario de Apple del código fuente cuando se corrigió este problema lo resume muy bien:

root@kitploit:~
// Find the number of cookies in the data. The data from elementData is shared with user space and may change at any time.

Echemos un vistazo a la función antes del parche (he intentado etiquetar las líneas relevantes):

root@kitploit:~
IOReturn IOHIDDevice::postElementTransaction(const void* elementData, UInt32 dataSize, UInt32 completionTimeout, IOHIDCompletion * completion)
{
    IOReturn ret = kIOReturnError;
    uint32_t   cookies_[kMaxLocalCookieArrayLength];
    uint32_t   *cookies = cookies_;
    uint32_t   cookieCount = 0;
    uint32_t   cookieSize = 0;
    uint32_t   dataOffset = 0;
    uint8_t    *data = (uint8_t*)elementData;
    IOMemoryDescriptor *elementDesc = getMemoryWithCurrentElementValues();
    require(_elementArray && elementDesc, fail);

    WORKLOOP_LOCK;

    // Find the number of cookies in the data. Check that all cookies are valid elements.                   [1]
    while (dataOffset < dataSize) {
        const IOHIDElementValueHeader *headerPtr = (const IOHIDElementValueHeader *)(data + dataOffset);
        IOHIDElementPrivate *element = GetElement(headerPtr->cookie);
        if (!element) {
            HIDDeviceLogError("Could not find element for cookie: %d", headerPtr->cookie);
            ret = kIOReturnAborted;
            goto fail;
        }
        cookieCount++;

        require_noerr_action(os_add3_overflow(dataOffset, headerPtr->length, sizeof(IOHIDElementValueHeader), &dataOffset), fail, HIDDeviceLogError("Overflow iterating cookie data buffer %u %u", dataOffset, headerPtr->length));
    }
    // Data isn't as large as expected, don't overrun, just abort
    if (dataOffset != dataSize) {   //                                                                      [2]
        HIDDeviceLogError("Cookie data buffer is smaller than expected. %u vs. %u",
                        (unsigned int)dataSize, (unsigned int)dataOffset);
        ret = kIOReturnAborted;
        goto fail;
    }
    dataOffset = 0;

    require_noerr_action(os_mul_overflow(cookieCount, sizeof(uint32_t), &cookieSize),
                        fail,
                        HIDDeviceLogError("Overflow calculating cookieSize"));

    cookies = (cookieCount <= kMaxLocalCookieArrayLength) ? cookies : (uint32_t*)IOMallocData(cookieSize); // [3]

    if (cookies == NULL) {
        ret = kIOReturnNoMemory;
        goto fail;
    }

    // Update the elements, this replaced the shared kernel-user shared memory.
    for (size_t index = 0; dataOffset < dataSize; ++index) {    //                                          [4]
        const IOHIDElementValueHeader *headerPtr;
        IOHIDElementPrivate *element;
        OSData *elementVal;

        headerPtr = (const IOHIDElementValueHeader *)(data + dataOffset);
        element = GetElement(headerPtr->cookie);
        dataOffset += headerPtr->length + sizeof(IOHIDElementValueHeader);

        elementVal = OSData::withBytesNoCopy((void*)headerPtr->value,
                                            headerPtr->length); //                                          [5]
        require_action(elementVal, fail, ret = kIOReturnNoMemory);
        element->setDataBits(elementVal);
        elementVal->release();

        cookies[index] = headerPtr->cookie; //                                                              [6]
    }

    // Actually post elements
    ret = postElementValues((IOHIDElementCookie *)cookies, (UInt32)cookieCount, 0, completionTimeout, completion);

fail:
    WORKLOOP_UNLOCK;
    if (cookies != &cookies_[0]) {
        IOFreeData(cookies, cookieSize);
    }

    return ret;
}
  • El bucle en [1] cuenta el número de IOHIDElementValues en el buffer y almacena esta cuenta en cookieCount.
  • La comprobación en [2] (combinada con la condición del bucle while) se asegura de que el campo length de cada cabecera no se extienda más allá de los límites del buffer elementData (ni que quede corto respecto al final del buffer, aunque esto es menos relevante).
  • Una vez que todos los elementos han sido contados y verificados por este bucle, se asigna un buffer cookies al heap en [3] con un tamaño de cookieCount * 4 (o se usa un buffer de pila si cookieCount es suficientemente pequeño).
  • Un segundo bucle en [4] realiza una segunda pasada por el buffer, analizando nuevamente los IOHIDElementValues.
  • Se crean objetos para almacenar el valor de cada elemento en , utilizando el campo que fue validado en el primer bucle.

Entonces, ¿cuál es el problema? Esta función se comporta correctamente cuando elementData no es volátil, el problema surge cuando el método se llama con memoria compartida. Aquí entra el método IOHIDInterface::SetElementValues_Impl de DriverKit:

root@kitploit:~
kern_return_t
IMPL(IOHIDInterface, SetElementValues)
{
    IOReturn ret = kIOReturnError;
    UInt8 *values = NULL;
    IOBufferMemoryDescriptor *md = NULL;
    
    md = OSDynamicCast(IOBufferMemoryDescriptor, elementValues);
    require_action(md && count, exit, ret = kIOReturnBadArgument);

    values = (UInt8 *)md->getBytesNoCopy();
    
    // Post the data to the device
    ret = _owner->postElementTransaction(values, (UInt32)md->getLength());
    require_noerr_action(ret, exit, HIDServiceLogError("postElementValues failed: 0x%x", ret));

exit:
    return ret;
}

Aquí, postElementTransaction se llama con md->getBytesNoCopy(), memoria compartida con el espacio de usuario, violando la suposición de que elementData es no volátil. El contenido del buffer elementData puede cambiar después del bucle en [1], pero antes del bucle en [4], entonces, ¿qué significa esto para un atacante?

Hay dos formas en que un atacante puede abusar de esto:

  • La primera es intercambiar el length de un IOHIDElementValueHeader pequeño al final del buffer por un valor mucho más grande. Esto significa que cuando se crea el OSData en [5], se extenderá mucho más allá de los límites del buffer elementData, permitiendo a un atacante leer datos fuera de los límites usando IOHIDInterface::GetElementValues_Impl.
  • La segunda es intercambiar el length de un IOHIDElementValueHeader grande al principio del buffer por un valor mucho más pequeño. Esto hará que el bucle en [4] analice muchos más encabezados de los que fueron contados originalmente en el bucle en [1], por lo que cuando se escriban las cookies en el array cookies en [6], desbordarán el array ya que index nunca se valida contra .

En la práctica, esto permite a un atacante leer datos del heap del kernel fuera de los límites de un tamaño arbitrario, y escribir datos arbitrarios (también de tamaño arbitrario) fuera de los límites en el heap del kernel. Estas son dos primitivas poderosas.

Ganando la carrera

Con condiciones de carrera, siempre buscamos formas de determinar si la carrera se ganó con éxito, para poder seguir intentando hasta lograrlo, haciendo nuestro desencadenante determinista. Afortunadamente, en el caso de esta condición de carrera, podemos hacer exactamente eso.

Para la variante de lectura fuera de límites, coloco un IOHIDElementValueHeader adicional después del encabezado cuyo length estoy cambiando, con su value establecido a la constante reconocible 0xD1AB011CAC1DF00D. Luego, al leer el valor del elemento de vuelta, sé que he ganado la carrera si veo el familiar encabezado 0xD1AB011CAC1DF00D al inicio de los datos devueltos.

Para la variante de escritura fuera de límites, coloco un IOHIDElementValueHeader después del encabezado cuyo length estoy cambiando, pero antes de los encabezados cuyas cookies serán desbordadas, esta vez con el value reconocible 0xD15EA5ED. Este encabezado estará encapsulado dentro del value del elemento más grande en el caso de que no ganemos la carrera, por lo que el encabezado solo se analizará y el valor del elemento se establecerá a 0xD15EA5ED si ganamos la carrera. Al leer de vuelta el valor del elemento, sé si tuve éxito.

La corrección de Apple

Para solucionar el problema, Apple optó por agregar un tercer bucle entre el bucle [1] y el bucle [4], validando cada campo length y luego almacenándolo en un nuevo array dataLengths, mientras se aseguraba de que el número de elementos no hubiera cambiado. El bucle final entonces usa las longitudes almacenadas en caché para sus cálculos, evitando leer del buffer otra vez.

Problemas con la explotación

El principal obstáculo a superar al explotar este problema es que el buffer del que estamos desbordando pertenece a KHEAP_DATA_BUFFERS, por lo que los objetivos de explotación son limitados. En esta prueba de concepto elegí apuntar a los encabezados kmsg, ya que son una de las pocas estructuras en KHEAP_DATA_BUFFERS que contienen punteros del kernel. La primitiva "kfree arbitrario" que obtuve usando este enfoque es la misma primitiva utilizada en el exploit multicast_bytecopy, sin embargo, el array IOSurfaceClient ahora está protegido con PAC y los clientes falsificados necesitan tener un puntero válido de vuelta al IOSurfaceRootUserClient que los creó, lo que hace que esto ya no sea un objetivo deseable para lectura/escritura del kernel.

Compilación e instalación

Apple no ha facilitado mucho la compilación e instalación de extensiones DriverKit personalizadas, especialmente sin una cuenta de desarrollador de Apple de pago, pero es posible.

Antes de comenzar, recomiendo:

  • Deshabilitar SIP
  • Establecer los boot-args amfi_get_out_of_my_way=1 cs_enforcement_disable=1
  • Ejecutar systemextensionsctl developer on
  • Reiniciar la computadora

Después de eso, deberías poder seleccionar tu equipo de desarrollador en la configuración del proyecto Xcode y el proyecto se compilará correctamente. Luego puedes ejecutar HIDDriverLoader, usar "Install Dext" para instalar la extensión DriverKit y "Trigger Exploit" para, lo adivinaste, activar el exploit.

Si esto falla, también puedes intentar compilar sin firmar usando:

root@kitploit:~
xcodebuild build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO

Y luego firmar manualmente usando:

root@kitploit:~
codesign -fs self-sign-cert --entitlements HIDDriverLoader/HIDDriverLoader.entitlements build/Release/HIDDriverLoader.app/Contents/MacOS/HIDDriverLoader
codesign -fs self-sign-cert --entitlements HIDDriver/HIDDriver.entitlements build/Release/HIDDriverLoader.app/Contents/Library/SystemExtensions/*.dext/*.driver

Reemplazando self-sign-cert con el nombre de un certificado autofirmado en tu llavero.

Gracias por leer :)

Descargar herramienta
OSData
[5]
length
  • En [6], el cookie de cada elemento se escribe en el array cookies asignado en [3].
  • cookieCount