Introdução
Neste blog, analisaremos a vulnerabilidade CVE-2024-23265, uma falha de corrupção de memória no kernel do iOS. Este exercício de aprendizado ajudará a entender o processo de diferenciação IPSW e identificar onde o patch provavelmente corrigiu a vulnerabilidade.
CVE-2024-23265 é uma falha de corrupção de memória no kernel do iOS, corrigida na versão 17.4. A correção foi feita melhorando os mecanismos de bloqueio para prevenir condições de corrida que poderiam levar à terminação inesperada do sistema ou a escritas não autorizadas na memória do kernel.
- Versão Vulnerável: iOS 17.3.1 (Build 21D61)
- Versão Corrigida: iOS 17.4 (Build 21E219)
Vamos explorar como o problema subjacente foi abordado no nível do kernel e como analisar tais correções usando técnicas práticas de diferenciação.
Extração e Diferenciação dos KEXTs do Kernelcache com IPSW
Continuando da série IPSW Walkthrough publicada no blog 8ksec, usaremos a ferramenta IPSW tool.
Para começar, baixe os arquivos IPSW relevantes diretamente via terminal usando os seguintes comandos:
ipsw download ipsw --device iPhone15,4 --build 21D61 ipsw download ipsw --device iPhone15,4 --build 21E219 Eles correspondem às versões vulneráveis (iOS 17.3.1) e corrigidas (iOS 17.4) associadas à CVE-2024-23265.
Agora, para extrair o kernelcache dos arquivos IPSW baixados, use os seguintes comandos:
ipsw extract --kernel iPhone15,4_17.3.1_21D61_Restore.ipsw ipsw extract --kernel iPhone15,4_17.4_21E219_Restore.ipsw Ao extrair, podemos inspecionar as versões do kernel para confirmar diferenças entre os builds:
ipsw kernel version 21D61__iPhone15,4/kernelcache.release.iPhone15,4 --json | jq . { "kernel": { "darwin": "23.3.0", "date": "2023-12-20T17:32:11Z", "xnu": "10002.82.4~3", "type": "RELEASE", "arch": "ARM64", "cpu": "T8120" }, "llvm": {} } ipsw kernel version 21E219__iPhone15,4/kernelcache.release.iPhone15,4 --json | jq . { "kernel": { "darwin": "23.4.0", "date": "2024-02-16T20:44:20Z", "xnu": "10063.102.14~10", "type": "RELEASE", "arch": "ARM64", "cpu": "T8120" }, "llvm": {} } Também podemos extrair todos os KEXTs de ambos os kernelcaches para inspeção detalhada usando:
ipsw kernel extract kernelcache.release.iPhone15,4 --all --output KEXTs_21D61
ipsw kernel extract kernelcache.release.iPhone15,4 --all --output KEXTs_21E219
No entanto, para o propósito de fazer a comparação (diffing), extrair todos os KEXTs é desnecessário. Uma rápida comparação entre as duas versões do kernelcache pode ser feita diretamente com:
Ao contrário de ferramentas como o plugin BinExport para Ghidra e a extensão BinDiffHelper, que permitem uma análise completa da cadeia BinDiff, bem como a reconstrução de assembly baseada em IA é uma opção emergente, este exemplo adota uma abordagem mais rápida e leve: exportar código descompilado do Ghidra e compará-lo usando ferramentas como Meld.
Use o seguinte script do Ghidra para exportar todas as funções descompiladas:
#@category Export
#@keybinding
#@menupath
#@toolbar
from ghidra.app.decompiler import DecompInterface
from ghidra.util.task import ConsoleTaskMonitor
import os
# Altere para onde deseja que a saída seja escrita
output_file = ""
decomp = DecompInterface()
decomp.openProgram(currentProgram)
fm = currentProgram.getFunctionManager()
functions = fm.getFunctions(True)
with open(output_file, "w") as out:
out.write("// Funções descompiladas de: "{}nn".format(currentProgram.getName()))
for function in functions:
results = decomp.decompileFunction(function, 60, ConsoleTaskMonitor())
if results and results.decompileCompleted():
sig = function.getName()
body = results.getDecompiledFunction().getC()
out.write("// Função: "{} em {}n".format(sig, function.getEntryPoint()))
out.write(body)
out.write("nn")
else:
out.write("// Falha ao descompilar função: "{} em {}nn".format(function.getName(), function.getEntryPoint()))
Agora, para exportar o código descompilado diretamente via terminal, podemos usar Ghidra’s analyzeHeadless:
analyzeHeadless -process com.apple.driver.AppleDiskImages2 -scriptPath ~/ghidra_scripts -postScript DumpAllToSingleFile.py --noanalysis
Após gerar os arquivos de código para ambos os projetos, podemos começar a compará-los.
Identificando o Patch via Comparação Descompilada
Agora estamos prontos para a comparação:
meld version1.c version2.c
Depois de uma inspeção, mudanças significativas começam a aparecer por volta da linha ~8170:


Ao aplicar a simbolização, muitos símbolos internos permanecem não resolvidos. Esta função específica parece implementar verificações de limites de ponteiros e lógica semelhante à de uma tabela de páginas em AppleDiskImages2.kext, mas seu nome exato e papel não estão presentes nos símbolos disponíveis.
Diferenças-chave incluem um novo cláusula de guarda. No iOS 17.3.1:
if (lVar4 != 0) {
return lVar4;
}
No iOS 17.4:
if ((lVar4 != 0) && (lVar4 != -1)) {
return lVar4;
}
// ......
lVar5 = 0;
if (lVar4 != -1) {
lVar5 = lVar4;
}
Isto adiciona uma verificação explícita contra -1 (0xffffffffffffffff), prevenindo a volta de um ponteiro potencialmente envenenado ou inválido. Esta correção mitigou o retorno de ponteiros não validados, abordando padrões comuns de condições de corrida em estruturas apertadas de ponteiros ou mapas de alocação.
Outra mudança que podemos ver afeta a lógica de término do loop:
if (uVar8 == *(uint *)(lVar5 + 0xc)) {
lVar5 = 0;
}
Em vez de retornar diretamente lVar4, a lógica atualizada garante:
- Retorno nulo em falha (falha limpa)
- Apenas resultados válidos e não-
-1são propagados
Isto reforça a verificação de limites e validação da integridade de memória.
Condições Provavelmente Vulneráveis no iOS 17.3.1
Até este ponto, as condições provavelmente vulneráveis em 17.3.1 podem ser:
*(long *)(uVar3 + (uVar1 & 0xfffffff8))pode retornar -1:- Não validado
- Retornado diretamente ao chamador
- Caso a estrutura de dados subjacente seja modificada concorrentemente:
- Um valor
-1estalado pode ser retornado - Race com desalocação leva a UAF ou exposição de ponteiro inválido
- Índice controlado pelo usuário (
param_1[1]) impulsiona o loop
Isto aponta para uma possível condição de corrida, permitindo que condições controladas pelo usuário bypassem verificações de segurança e retornem referências de memória maliciosas ou corrompidas. Se esta função for invocada via um caminho IOUserClient::externalMethod ou equivalente, um ator malicioso pode:
- Race atualizações à estrutura de dados subjacente
- Obrigar o retorno de
-1 - Iniciar corrupção da memória do kernel ou acesso não autorizado a ponteiros
Esses achados se alinham estreitamente com o aviso CVE-2024-23265:
"Melhorias de bloqueio prevenem escritas não autorizadas na memória do kernel ou terminação inesperada do sistema."
O retorno de -1 como um ponteiro pode disparar ambos os modos de falha.
Rastreando o Impacto
Analisando a função vulnerável no iOS 17.3.1 dentro do Ghidra, inspecionamos as referências ao seu uso:
Uma referência notável leva a __ZN20AppleDiskImageDevice15boostAnyRequestEh:
undefined8 __ZN20AppleDiskImageDevice15boostAnyRequestEh(long param_1, uint param_2)
A função AppleDiskImageDevice::boostAnyRequest(uint8_t level):
- Chama diretamente a função vulnerável 9 vezes
- Itera sobre
param_1 + 0xf8usandoparam_1[1]como índice do loop (provavelmente influenciado pelo usuário) - Aplique lógica envolvendo bandeiras QoS e buffers de memória residentes no kernel
- Likely manipulando algum tipo de fila de I/O ou lista de buffers em trânsito
- Não tem verificação de limites para valores desreferenciados (antes da correção)
Outros referências:
void FUN_fffffff028a13b98(long param_1, long param_2)
Esta função: - Também chama a função vulnerável 3 vezes - Preenche uma estrutura em param_2 + 0x780 - Sugere coleta de metadados ou enumeração de extensões de uma estrutura de suporte - Parece ser um caminhar estruturado das mapeações da imagem do disco, escrevendo-as na memória do usuário.
Sua principal iteração é:
while ((local_48 != local_60 || local_40 != local_58) && (*(uint *)(param_2 + 0x780)
Rastreamento da Acessibilidade do Usuário
Para determinar se o usuário pode alcançar boostAnyRequest() ou FUN_fffffff028a13b98, verificamos a presença de referências externalMethod ou tabelas IOExternalMethodDispatch. Esses definem como IOUserClient expõe seletores de método ao usuário. Se qualquer uma dessas funções é referenciada nessas tabelas ou em declarações condicionais vinculadas a índices de seletores, isso confirma a acessibilidade da syscall via IOConnectCallMethod.
Sem sucesso nas referências estáticas, procuramos por evidências em string:

A string IOReturn AppleDiskImageDevice::boostAnyRequest(uint8_t) confirma que boostAnyRequest é um método formalmente declarado com um símbolo visível, não anônimo ou in-line. Embora isso não prove que está exposto via IOExternalMethodDispatch, seu nome e estrutura são consistentes com métodos que são despachados através de externalMethod em subclasses de IOUserClient.
Consultando boostAnyRequest, encontramos a seguinte função:

Esta função chama boostAnyRequest(...) se FUN_fffffff028a0c98c(...) == 0.
Ela opera em um campo de objeto a partir de param_1 + 0xf8, provavelmente referenciando o AppleDiskImages2UserClient ou uma estrutura relacionada.
Em seguida, ela chama DIDeviceRequestPool::GetRequest(…) que interessantemente se encaixa no nome visto ao procurar por externalMethod na árvore de símbolos: e.g. DIDeviceCreatorUserClient::externalMethod(…):

Isto nos diz que a chamada para boostAnyRequest não é independente. Em vez disso, ela é:
- Likely uma subrotina em um usuário ou em sua classe auxiliar
- Chamado como parte de um caminho de solicitação, provavelmente a partir do
externalMethodde umaDIDevice*UserClientsubclass - Implica que chamando algum seletor levará à
GetRequest(...), o qual por sua vez pode chamarboostAnyRequest(...)se uma condição for atendida
Há também 4 referências cruzadas para __ZN20AppleDiskImageDevice10GetRequestEmhPFbPvES0_:
- FUN_fffffff028a0f990
- AppleDiskImageDevice::doEjectMediaEv
- FUN_fffffff028a10444
- FUN_fffffff028a107e4
Todos chamam:
AppleDiskImageDevice::GetRequest(...)
→ o qual chama condicionalmente:
AppleDiskImageDevice::boostAnyRequest(…)
→ o qual contém a chamada vulnerável para:
FUN_fffffff028a156c8
Essas funções não aparecem em tabelas de despacho padrão, o que significa que a KEXT provavelmente usa um despacho manual dentro da função externalMethod(...), como um switch na seleção ou uma matriz personalizada de ponteiros para funções. Isso torna a análise estática mais difícil e força a verificação em tempo real para encontrar seleções válidas.
O despacho provavelmente é feito por meio de um switch personalizado (param_2) ou tabela indireta, com cada valor do seletor mapeando para uma dessas funções.
A seguir, tentamos identificar o mapeamento dos seletores varrendo a binária em busca de referências escalares aos endereços das funções vulneráveis, mas não encontramos correspondências, confirmando que o despacho é dinâmico e não tabular.

Conclusão
Com a comparação do AppleDiskImages2 KEXT entre iOS 17.3.1 e 17.4, identificamos uma vulnerabilidade de corrupção de memória no kernel (CVE-2024-23265), causada por falta de verificação do valor sentinela -1 em um loop que desreferencia ponteiros do kernel.
A função vulnerável é chamada repetidamente na AppleDiskImageDevice::boostAnyRequest(uint8_t), provavelmente exposta via um método user client. A Apple corrigiu isso adicionando bloqueio e guardas -1, indicando uma condição de corrida clássica onde uma tabela de ponteiros ou lista de extensões pode ser modificada durante a iteração, levando a uma desreferência inválida.
Para explorar essa vulnerabilidade, primeiro identifique o IOService apropriado via ioreg, depois use fuzzing ou força bruta para valores de seletor com IOConnectCallMethod para disparar o caminho do código vulnerável (Referência), após isso, forneça entrada elaborada para manipular o estado interno do loop ou referências à memória, então induza uma condição de corrida na estrutura subjacente usando concorrência e finalmente observe o comportamento do kernel em busca de evidências de corrupção de memória, desreferência inválida de ponteiro ou instabilidade no sistema (Consulte Análise de Kernel Panic da 8ksec).
Sobre o Autor — Sergio Lopez (s3rg0x)
Sergio Lopez (LinkedIn) é um Red Teamer e Pesquisador de Segurança, com experiência em segurança da IA, análise de vulnerabilidades do kernel, shellcoding, design de quadros de comando C2 e desenvolvimento de malware evasivo.
Ele apresentou em Black Hat e várias outras conferências de segurança líderes, e possui múltiplas certificações da indústria incluindo OSEP, OSWE, GREM, CRTO e eCPPT.
Sergio é apaixonado por avançar a comunidade de segurança e está sempre ansioso para contribuir com pesquisas e compartilhamento de conhecimento.

