Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2022-42864 — Prova di concetto per la CVE-2022-42864 IOHIDFamily race condition | Kitploit
Strumenti/GitHubGitHub/muirey03/cve-2022-42864
Analisi delle VulnerabilitàExploitBinary Exploitation
GitHubmuirey03/cve-2022-42864

CVE-2022-42864

Prova di concetto per la CVE-2022-42864 IOHIDFamily race condition

Vedi Repository
68733 anni faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2022-42864: Cookie Diabolici

Cos'è questo repository?

Questa è la mia (incompleta) proof-of-concept dell'exploit per CVE-2022-42864, una vulnerabilità di tipo time-of-check-time-of-use (TOCTOU) in IOHIDFamily che è stata corretta in iOS 16.2 / macOS Ventura 13.1.

Qual è lo stato della proof-of-concept?

L'exploit attualmente ottiene la stessa primitiva "arbitrary kfree" usata nell'exploit multicast_bytecopy. Tuttavia, il successivo flusso di exploit di multicast_bytecopy è stato pesantemente mitigato, quindi non si tratta di un exploit completo, ma dimostra semplicemente la gravità del problema.

Dovrei eseguirlo?

Se devi chiedere, no. Non fa nulla di utile, provoca solo un kernel panic. Non mi assumo responsabilità per eventuali perdite di dati o instabilità causate da questo codice.

Il bug

Il commento di Apple dal codice sorgente quando questo problema è stato risolto lo riassume perfettamente:

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

Diamo un'occhiata alla funzione prima della patch (ho cercato di etichettare le righe pertinenti):

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;
}
  • Il ciclo in [1] conta il numero di IOHIDElementValue nel buffer e memorizza questo conteggio in cookieCount.
  • Il controllo in [2] (combinato con la condizione del ciclo while) si assicurerà che il campo length di ogni intestazione non si estenda oltre i limiti del buffer elementData (né possa essere inferiore alla fine del buffer, anche se questo è meno rilevante).
  • Una volta che tutti gli elementi sono stati contati e verificati da questo ciclo, un buffer cookies viene allocato nell'heap in [3] con una dimensione di cookieCount * 4 (oppure viene usato un buffer nello stack se cookieCount è sufficientemente piccolo).
  • Un secondo ciclo in [4] effettua quindi una seconda scansione del buffer, analizzando nuovamente gli IOHIDElementValue.
  • Gli oggetti OSData vengono creati per contenere il valore di ogni elemento in [5], utilizzando il campo length che è stato validato nel primo ciclo.
  • In [6], il cookie di ogni elemento viene scritto nell'array cookies allocato in [3].

Qual è il problema? Questa funzione si comporta correttamente quando elementData è non volatile; il problema si presenta quando il metodo viene chiamato con memoria condivisa. Entra in gioco il metodo IOHIDInterface::SetElementValues_Impl di DriverKit:

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;
}

Qui, postElementTransaction viene chiamato con md->getBytesNoCopy(), memoria condivisa con lo spazio utente, violando l'assunzione che elementData sia non volatile. Il contenuto del buffer elementData può cambiare dopo il ciclo in [1], ma prima del ciclo in [4], quindi cosa significa questo per un attaccante?

Ci sono due modi in cui un attaccante può abusarne:

  • Il primo è scambiare la length di un piccolo IOHIDElementValueHeader alla fine del buffer con un valore molto più grande. Ciò significa che quando l'OSData in [5] viene creato, si estenderà ben oltre i limiti del buffer elementData, consentendo a un attaccante di leggere dati fuori dai limiti usando IOHIDInterface::GetElementValues_Impl.
  • Il secondo è scambiare la length di un grande IOHIDElementValueHeader all'inizio del buffer con un valore molto più piccolo. Ciò farà sì che il ciclo in [4] analizzi molte più intestazioni di quelle originariamente contate nel ciclo in [1], quindi quando i cookie vengono scritti nell'array cookies in [6], questi traboccheranno dall'array poiché index non viene mai validato rispetto a cookieCount.

In pratica, ciò consente a un attaccante di leggere dati fuori dai limiti dell'heap del kernel di dimensione arbitraria e di scrivere dati arbitrari (ancora di dimensione arbitraria) fuori dai limiti nell'heap del kernel. Si tratta di due primitive potenti.

Scarica lo strumento