
CVE-2018-4241: estouro de heap do kernel XNU devido a verificação de limites inadequada no MPTCP para iOS 11 - 11.3.1lançado por Ian Beer
@i41nbeer
mptcp_usr_connectx é o handler para a syscall connectx da família de sockets AP_MULTIPATH.
A lógica desta função falha ao lidar corretamente com sockaddrs de origem e destino que não são AF_INET ou AF_INET6:
// verify sa_len for AF_INET:
if (dst->sa_family == AF_INET &&
dst->sa_len != sizeof(mpte->__mpte_dst_v4)) {
mptcplog((LOG_ERR, "%s IPv4 dst len %u\n", __func__, dst->sa_len), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
error = EINVAL;
goto out;
}
// verify sa_len for AF_INET6:
if (dst->sa_family == AF_INET6 &&
dst->sa_len != sizeof(mpte->__mpte_dst_v6)) {
mptcplog((LOG_ERR, "%s IPv6 dst len %u\n", __func__, dst->sa_len), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
error = EINVAL;
goto out;
}
// code doesn't bail if sa_family was neither AF_INET nor AF_INET6
if (!(mpte->mpte_flags & MPTE_SVCTYPE_CHECKED)) {
if (mptcp_entitlement_check(mp_so) < 0) {
error = EPERM;
goto out;
}
mpte->mpte_flags |= MPTE_SVCTYPE_CHECKED;
}
// memcpy with sa_len up to 255:
if ((mp_so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0) {
memcpy(&mpte->mpte_dst, dst, dst->sa_len);
}
Olhando ao redor na estrutura que você transborda, percebe que pode atingir ambos os campos aqui:
if (mpte->mpte_itfinfo_size > MPTE_ITFINFO_SIZE) _FREE(mpte->mpte_itfinfo, M_TEMP);
mpte_itfinfo_size está logo antes de mpte_itfinfo.
Quando a estrutura é inicializada, o ponteiro mpte_itfinfo aponta para um pequeno array inline. Se mais subfluxos forem adicionados do que caberão ali, eles são colocados em um buffer de heap, e mpte_itfinfo apontará para esse buffer.
Se você tivesse outro bug (por exemplo, o bug de divulgação de heap do kernel do async_wake), poderia sobrescrever o campo mpte_itfinfo com qualquer objeto de zona válido e ele seria liberado (na verdade, você também poderia sobrescrevê-lo com um offset dentro desse objeto para ainda mais diversão!)
No entanto, não temos isso.
Em vez disso, outra abordagem é sobrescrever parcialmente o ponteiro. Se o sobrescrevermos parcialmente com bytes NULL, podemos apontá-lo para um valor alinhado a 256 bytes, 65k, 16MB ou 4GB.
Neste exploit, escolho uma sobrescrita de 3 bytes NULL, que causará um kfree do endereço mpte_itfinfo arredondado para baixo até o próximo limite de 16MB.
O fluxo de exploração é o seguinte:
Ressalva: Para conectar sockets mptcp, você precisa da permissão com.apple.developer.networking.multipath, que requer um certificado de desenvolvedor Apple, que qualquer um pode comprar da Apple.
Confiabilidade: Esta é uma ferramenta de pesquisa de segurança e está muuuuito longe de ser perfeita. No entanto, deve funcionar na maioria das vezes, e quando funciona, deve fazer um bom trabalho de limpeza para que não entre em pânico depois.
Para melhorar a probabilidade de funcionar:
Dispositivos suportados: Deve funcionar no iOS 11.0 - 11.3.1 inclusive. Testei em: iPod Touch 6g, iPhone 6s, iPhone SE, iPhone 7, iPhone 8
API: #include "sploit.h" e chame go() para executar o exploit. Se funcionou, você pode usar as funções em kmem.h para ler e escrever memória do kernel
Notas: Várias pessoas fizeram bindiff público deste bug a partir do patch (ou seu 0day foi corrigido ;) leia o material deles para mais detalhes: @elvanderb deu uma palestra relâmpago sobre o bug no rump.beer em Paris em 31 de maio: https://www.rump.beer/2018/slides/ios_48h.pdf @jaakerblom publicou um exploit funcional no github em 1º de junho: https://github.com/potmdehex/multipath_kfree A técnica de John é semelhante à minha, mas ele faz um overflow de dois bytes em vez de três, e substitui com objetos diferentes. material bom!