
CVE-2018-4241: Desbordamiento de montón del kernel XNU debido a una comprobación de límites deficiente en MPTCP para iOS 11 - 11.3.1publicado por Ian Beer
@i41nbeer
mptcp_usr_connectx es el manejador de la syscall connectx para la familia de sockets AP_MULTIPATH.
La lógica de esta función falla al manejar correctamente sockaddrs de origen y destino que no son AF_INET o 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);
}
Mirando alrededor en la estructura que desbordas, notas que puedes alcanzar ambos campos aquí:
if (mpte->mpte_itfinfo_size > MPTE_ITFINFO_SIZE) _FREE(mpte->mpte_itfinfo, M_TEMP);
mpte_itfinfo_size está justo antes de mpte_itfinfo.
Cuando la estructura es inicializada, el puntero mpte_itfinfo apunta a un pequeño array inline. Si se añaden más subflujos de los que caben, se colocan en un buffer de heap, y mpte_itfinfo apuntará a ese buffer.
Si tuvieras otro bug (por ejemplo, el bug de divulgación de heap del kernel de async_wake) podrías sobrescribir el campo mpte_itfinfo con cualquier objeto de zona válido y sería liberado (de hecho, también podrías sobrescribirlo con un offset dentro de ese objeto para aún más diversión).
Sin embargo, no tenemos eso.
En su lugar, otro enfoque es sobrescribir parcialmente el puntero. Si lo sobrescribimos parcialmente con bytes NULL, podemos apuntarlo a un valor alineado a 256 bytes, 65k, 16MB o 4GB.
En este exploit elijo una sobrescritura de 3 bytes con NULL, lo que causará un kfree de la dirección mpte_itfinfo redondeada hacia abajo al siguiente límite de 16MB.
El flujo de explotación es el siguiente:
Advertencia: Para conectar sockets mptcp necesitas el entitlement com.apple.developer.networking.multipath, que requiere un certificado de desarrollador de Apple, que cualquiera puede comprar a Apple.
Fiabilidad: Esta es una herramienta de investigación de seguridad y está muuuuuy lejos de ser perfecta. Sin embargo, debería funcionar la mayoría de las veces, y cuando funciona, debería hacer un buen trabajo de limpieza para que no cause panic más tarde.
Para mejorar la probabilidad de que funcione:
Dispositivos compatibles: Debería funcionar en iOS 11.0 - 11.3.1 inclusive. He probado en: iPod Touch 6g, iPhone 6s, iPhone SE, iPhone 7, iPhone 8
API: #include "sploit.h" y llama a go() para ejecutar el exploit. Si funciona, puedes usar las funciones en kmem.h para leer y escribir memoria del kernel.
Notas: Varias personas han hecho bindiff públicamente de este bug desde el parche (o su 0day fue parcheado ;) lee su material para más detalles: @elvanderb dio una charla relámpago sobre el bug en rump.beer en París el 31 de mayo: https://www.rump.beer/2018/slides/ios_48h.pdf @jaakerblom publicó un exploit funcional en github el 1 de junio: https://github.com/potmdehex/multipath_kfree La técnica de John es similar a la mía pero él hace un desbordamiento de dos bytes en lugar de tres, y lo reemplaza con objetos diferentes. ¡buen material!