
CVE-2018-4241: débordement de tas du noyau XNU dû à une mauvaise vérification des limites dans MPTCP pour iOS 11 - 11.3.1publié par Ian Beer
@i41nbeer
mptcp_usr_connectx est le gestionnaire de l'appel système connectx pour la famille de sockets AP_MULTIPATH.
La logique de cette fonction ne parvient pas à traiter correctement les sockaddrs source et destination qui ne sont pas 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);
}
En regardant dans la structure que vous débordez, vous remarquez que vous pouvez atteindre les deux champs ici :
if (mpte->mpte_itfinfo_size > MPTE_ITFINFO_SIZE) _FREE(mpte->mpte_itfinfo, M_TEMP);
mpte_itfinfo_size se trouve juste avant mpte_itfinfo.
Quand la structure est initialisée, le pointeur mpte_itfinfo pointe vers un petit tableau inline. Si plus de sous-flux sont ajoutés qu'il n'y en a dans le tableau, ils sont alors placés dans un buffer du tas, et mpte_itfinfo pointera vers celui-ci.
Si vous aviez un autre bug (par exemple la divulgation du tas noyau de async_wake), vous pourriez écraser le champ mpte_itfinfo avec n'importe quel objet de zone valide et il serait libéré (en fait, vous pourriez aussi l'écraser avec un offset dans cet objet pour encore plus de fun !)
Cependant, nous n'avons pas ça.
Une autre approche consiste à écraser partiellement le pointeur. Si nous l'écrasons partiellement avec des octets NULL, nous pouvons le faire pointer vers une valeur alignée sur 256 octets, 65k, 16 Mo ou 4 Go.
Dans cet exploit, je choisis un écrasement NULL de 3 octets, ce qui provoquera un kfree de l'adresse de mpte_itfinfo arrondie à la limite des 16 Mo suivante.
Le flux d'exploitation est le suivant :
Mise en garde : Pour connecter les sockets mptcp, vous avez besoin de l'entitlement com.apple.developer.networking.multipath qui nécessite un certificat développeur Apple, que n'importe qui peut acheter auprès d'Apple.
Fiabilité : C'est un outil de recherche en sécurité et il est looooin d'être parfait. Cependant, il devrait fonctionner la plupart du temps, et quand il fonctionne, il devrait bien nettoyer pour ne pas planter plus tard.
Pour améliorer la probabilité qu'il fonctionne :
Appareils supportés : Cela devrait fonctionner sur iOS 11.0 à 11.3.1 inclus. J'ai testé sur : iPod Touch 6g, iPhone 6s, iPhone SE, iPhone 7, iPhone 8
API : #include "sploit.h" et appelez go() pour exécuter l'exploit. S'il a fonctionné, vous pouvez utiliser les fonctions de kmem.h pour lire et écrire la mémoire du noyau
Notes: @elvanderb a donné une conférence éclair sur le bug à rump.beer à Paris le 31 mai : https://www.rump.beer/2018/slides/ios_48h.pdf @jaakerblom a publié un exploit fonctionnel sur github le 1er juin : https://github.com/potmdehex/multipath_kfree La technique de John est similaire à la mienne mais il fait un débordement de deux octets plutôt que trois, et remplace par des objets différents. du bon boulot !