Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2022-42864 — Prova de conceito para a condição de corrida CVE-2022-42864 do IOHIDFamily | Kitploit
Ferramentas/GitHubGitHub/muirey03/cve-2022-42864
Análise de VulnerabilidadesExploraçãoExploração de Binários
GitHubmuirey03/cve-2022-42864

CVE-2022-42864

Prova de conceito para a condição de corrida CVE-2022-42864 do IOHIDFamily

Ver Repositório
6872há 3 anosRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2022-42864: Biscoitos Diabólicos

O que é este repositório?

Este é o meu exploit de prova de conceito (incompleto) para o CVE-2022-42864, uma vulnerabilidade de tempo de verificação e tempo de uso (TOCTOU) no IOHIDFamily que foi corrigida no iOS 16.2 / macOS Ventura 13.1.

Qual é o estado da prova de conceito?

O exploit atualmente alcança a mesma primitiva de "kfree arbitrário" usada no exploit multicast_bytecopy. No entanto, o fluxo de exploração subsequente do multicast_bytecopy foi fortemente mitigado, portanto isto não é um exploit completo, apenas demonstra a gravidade do problema.

Devo executar isto?

Se você precisa perguntar, não. Isto não faz nada útil, apenas causa um pânico do kernel. Não me responsabilizo por qualquer perda de dados ou instabilidade que este código possa causar.

O bug

O comentário da Apple no código-fonte quando este problema foi corrigido resume isto muito bem:

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.

Vejamos a função antes do patch (tentei rotular as linhas 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;
}
  • O loop em [1] conta o número de IOHIDElementValues no buffer e armazena esta contagem em cookieCount.
  • A verificação em [2] (combinada com a condição do loop while) garante que o campo length de cada cabeçalho não se estenda para fora dos limites do buffer elementData (nem pode ficar aquém do final do buffer, embora isso seja menos relevante).
  • Uma vez que todos os elementos foram contados e verificados por este loop, um buffer cookies é alocado no heap em [3] com um tamanho de cookieCount * 4 (ou um buffer de pilha é usado se cookieCount for suficientemente pequeno).
  • Um segundo loop em [4] faz então uma segunda passagem pelo buffer, analisando novamente os IOHIDElementValues.
  • Objetos OSData são criados para armazenar o valor de cada elemento em [5], usando o campo length que foi validado no primeiro loop.
  • Em [6], o cookie de cada elemento é escrito no array cookies alocado em [3].

Então, qual é o problema? Esta função se comporta de forma totalmente correta quando elementData é não volátil; o problema surge quando o método é chamado com memória compartilhada. Conheça o método IOHIDInterface::SetElementValues_Impl do 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;
}

Aqui, postElementTransaction é chamado com md->getBytesNoCopy(), memória compartilhada com o espaço do usuário, violando a suposição de que elementData é não volátil. O conteúdo do buffer elementData pode mudar após o loop em [1], mas antes do loop em [4], então o que isso significa para um atacante?

Existem duas maneiras de um atacante abusar disso:

  • A primeira é trocar o length de um IOHIDElementValueHeader pequeno no final do buffer por um valor muito maior. Isso significa que quando o OSData em [5] for criado, ele se estenderá muito além dos limites do buffer elementData, permitindo que um atacante leia dados fora dos limites usando IOHIDInterface::GetElementValues_Impl.
  • A segunda é trocar o length de um IOHIDElementValueHeader grande no início do buffer por um valor muito menor. Isso fará com que o loop em [4] analise muitos mais cabeçalhos do que foram originalmente contados no loop em [1], então, quando os cookies forem escritos no array cookies em [6], eles transbordarão para fora do array, pois index nunca é validado contra cookieCount.

Na prática, isso permite que um atacante leia dados do heap do kernel fora dos limites de tamanho arbitrário e escreva dados arbitrários (novamente de tamanho arbitrário) fora dos limites no heap do kernel. Estas são duas primitivas poderosas.

Vencendo a corrida

Com condições de corrida, sempre procuramos maneiras de determinar se a corrida foi vencida com sucesso, para podermos continuar tentando até conseguir, tornando nosso gatilho determinístico. Felizmente, no caso desta condição de corrida, podemos fazer exatamente isso.

Para a variante de leitura fora dos limites, coloco mais um IOHIDElementValueHeader após o cabeçalho cujo length estou alterando, com seu value definido como a constante reconhecível de 0xD1AB011CAC1DF00D. Então, ao ler o valor do elemento de volta, sei que venci a corrida se vir o familiar cabeçalho 0xD1AB011CAC1DF00D no início dos dados retornados.

Para a variante de escrita fora dos limites, coloco um IOHIDElementValueHeader após o cabeçalho cujo length estou alterando, mas antes dos cabeçalhos cujos cookies serão estourados, desta vez com o value reconhecível de 0xD15EA5ED. Este cabeçalho será encapsulado dentro do value do elemento maior no caso de não vencermos a corrida, então o cabeçalho só será analisado e o valor do elemento será definido como 0xD15EA5ED se vencermos a corrida. Ao ler de volta o valor do elemento, sei se fui bem-sucedido.

A correção da Apple

Para corrigir o problema, a Apple optou por adicionar um terceiro loop entre o loop [1] e o loop [4], validando cada campo length e armazenando-o em cache em um novo array dataLengths, garantindo que o número de elementos não tivesse mudado. O loop final usa então os comprimentos em cache para seus cálculos, evitando ler do buffer mais uma vez.

Problemas com a exploração

O principal obstáculo a superar ao explorar este problema é que o buffer do qual estamos estourando pertence a KHEAP_DATA_BUFFERS, então os alvos de exploração são limitados. Nesta prova de conceito, optei por mirar nos cabeçalhos kmsg, pois são uma das poucas estruturas em KHEAP_DATA_BUFFERS que contêm ponteiros do kernel. A primitiva de "kfree arbitrário" que obtive com essa abordagem é a mesma primitiva usada no exploit multicast_bytecopy; no entanto, o array IOSurfaceClient agora é protegido por PAC e clientes forjados precisam ter um ponteiro válido de volta para o IOSurfaceRootUserClient que os criou, tornando isso um alvo de leitura/escrita do kernel não mais desejável.

Compilando e instalando

A Apple não facilitou a compilação e instalação de extensões DriverKit personalizadas, especialmente sem uma conta paga de Apple Developer, mas é possível.

Antes de começar, recomendo:

  • Desativar o SIP
  • Definir os boot-args amfi_get_out_of_my_way=1 cs_enforcement_disable=1
  • Executar systemextensionsctl developer on
  • Reiniciar o computador

Depois disso, você deve conseguir selecionar sua equipe de desenvolvedor nas configurações do projeto Xcode e o projeto será compilado com sucesso. Você pode então executar o HIDDriverLoader, usar "Install Dext" para instalar a extensão DriverKit e "Trigger Exploit" para, você adivinhou, acionar o exploit.

Se isso falhar, você também pode tentar compilar sem assinatura usando:

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

E então assinar 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

Substituindo self-sign-cert pelo nome de um certificado autoassinado no seu chaveiro.

Obrigado por ler :)

Baixar ferramenta