VirtualBox Slirp NAT Packet Heap Exploitation

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

TL;DR

  • VirtualBox distribue un fork fortement modifié de Slirp dont les packet buffers (mbufs) résident dans un allocateur de zones personnalisé avec des métadonnées inline et des callbacks par pointeur de fonction (pfFini, pfDtor).
  • Un invité peut réécrire le m->m_len de confiance avec une longueur d’en-tête IP contrôlée par l’attaquant, ce qui annule tous les contrôles de limites ultérieurs et fournit à la fois des primitives d’infoleak et d’overwrite.
  • En abusant de paquets UDP avec checksum 0 et un ip_len surdimensionné, le guest peut exfiltrer les mbuf tails et les métadonnées des chunks voisins pour apprendre les adresses du heap et de la zone.
  • Fournir des options IP spécialement fabriquées force ip_stripoptions() à memcpy() trop de données en place, de sorte que l’attaquant peut écraser l’en-tête struct item du mbuf suivant et orienter son champ zone vers des données entièrement contrôlées.
  • La libération du mbuf corrompu déclenche zone->pfFini() avec des arguments fournis par l’attaquant ; le pointer vers memcpy@plt donne une primitive de copie/écriture arbitraire qui peut être dirigée vers des entrées GOT ou d’autres données de contrôle à l’intérieur du binaire VirtualBox non-PIE.

Packet allocator anatomy

VirtualBox alloue chaque trame Ethernet entrante depuis une zone par interface nommée zone_clust. Chaque chunk de données de 0x800 octets est précédé d’un en-tête inline:

struct item {
uint32_t magic;      // 0xdead0001
void    *zone;       // uma_zone_t pointer with callbacks
uint32_t ref_count;
LIST_ENTRY(item) list; // freelist / used list links
};

Quand un mbuf est libéré la pile d’appels m_freem -> ... -> slirp_uma_free() fait confiance à l’en-tête inline :

  1. uma_zfree_arg() recalculates item = (struct item *)mem - 1 et devrait valider item->zone, mais Assert() est compilé hors des builds release.
  2. slirp_uma_free() lit zone = item->zone et exécute sans condition zone->pfFini(zone->pData, data_ptr, zone->size) suivi de zone->pfDtor(...).

Donc, tout write-what-where dans l’en-tête du mbuf se traduit par un appel indirect contrôlé pendant free().

Infoleak via m->m_len override

Au début de ip_input() VirtualBox a ajouté:

if (m->m_len != RT_N2H_U16(ip->ip_len))
m->m_len = RT_N2H_U16(ip->ip_len);

Parce que l’affectation a lieu avant la vérification de l’en-tête IP, un invité peut annoncer n’importe quelle longueur jusqu’à 0xffff. Le reste de la pile (ICMP, UDP, gestionnaires de fragmentation, etc.) suppose que m->m_len est fiable et l’utilise pour décider combien d’octets copier depuis le mbuf.

Utilisez des paquets UDP avec checksum 0 (signifiant “no checksum”). Le fast-path NAT transmet m->m_len octets sans vérifier l’intégrité du payload, donc gonfler ip_len fait que Slirp lit au-delà du véritable tampon et renvoie des résidus du heap vers l’invité ou vers un helper externe coopérant au-delà du NAT. Parce que la taille du chunk est de 2048 octets, le leak peut inclure :

  • Le struct item inline du mbuf suivant, révélant l’ordre de la freelist et le pointeur réel zone.
  • Des cookies du heap tels que les champs magic, aidant à fabriquer des en-têtes plausibles lors de corruptions ultérieures.

Overwriting neighbouring chunk headers with IP options

La même longueur falsifiée peut être transformée en primitive d’overwrite en forçant le paquet à passer par ip_stripoptions() (déclenché lorsque l’en-tête IP contient des options et que le payload est UDP/TCP). Le helper calcule une longueur de copie à partir de m->m_len puis appelle memcpy() pour décaler l’en-tête transport par-dessus les options supprimées :

  1. Fournir un ip_len long de sorte que la longueur de déplacement calculée dépasse le mbuf courant.
  2. Inclure un petit nombre d’options IP pour que Slirp entre dans le chemin de stripping.
  3. Quand memcpy() s’exécute, il lit depuis le mbuf suivant et écrit par-dessus le payload du mbuf courant et l’en-tête inline, corrompant magic, zone, ref_count, etc.

Parce que l’allocator garde les paquets de la même interface contigus sur la freelist, cet overflow atteint de manière déterministe le chunk suivant après un grooming modéré du heap.

Forging uma_zone_t to hijack pfFini

Une fois que le struct item adjacent est corruptible, l’exploit procède comme suit :

  1. Utiliser les adresses heap leakées pour construire une fausse structure uma_zone à l’intérieur d’un mbuf entièrement contrôlé par l’invité. Remplir :
    • pfFini avec l’entrée PLT de memcpy().
    • pData avec le pointeur de destination souhaité (par ex. entrée GOT, slot de vtable, tableau de pointeurs de fonction).
    • size avec le nombre d’octets à copier.
    • Optionnel : pfDtor comme appel de second stade (par ex. pour invoquer le pointeur de fonction nouvellement écrit).
  2. Écraser le champ zone du mbuf cible avec le pointeur vers la fausse structure ; ajuster les pointeurs list pour que la tenue de la freelist reste suffisamment cohérente afin d’éviter des plantages.
  3. Libérer le mbuf. slirp_uma_free() exécute maintenant memcpy(dest=pData, src=item_data, n=size) alors que le mbuf contient encore des données contrôlées par l’invité, aboutissant à une écriture arbitraire.

Puisque le binaire VirtualBox sous Linux n’est pas PIE, les adresses PLT de memcpy et system sont fixes et peuvent être utilisées directement. L’invité peut aussi stocker des chaînes comme /bin/sh dans un autre mbuf qui reste référencé lorsque l’appel détourné s’exécute.

Heap grooming via fragmentation

La zone par interface de Slirp contient 3072 chunks et est initialement découpée comme un tableau contigu dont la freelist est parcourue des adresses hautes vers le bas. Une adjacency déterministe peut être obtenue en :

  • Inonder le NAT avec de nombreux fragments IP_MF de taille constante afin que le code de réassemblage alloue des séquences d’mbuf prévisibles.
  • Recycler des chunks spécifiques en envoyant des fragments qui expirent, forçant des frees retournant dans la freelist en ordre LIFO.
  • Utiliser la connaissance du parcours de la freelist pour placer le mbuf victime futur juste après le mbuf qui portera l’overflow des options IP.

Ce grooming garantit que l’overflow atteint le struct item ciblé et que la fausse uma_zone reste dans les limites de la primitive de leak.

From arbitrary write to host code execution

Avec la primitive memcpy-on-free :

  1. Copier une chaîne /bin/sh contrôlée par l’attaquant et le buffer de commande dans un mbuf stable.
  2. Utiliser la primitive pour écraser une entrée GOT ou un callsite indirect (par ex. un pointeur de fonction dans l’état du device NAT) avec l’entrée PLT de system().
  3. Déclencher l’appel écrasé. Parce que VirtualBox exécute le device NAT à l’intérieur du processus hôte, le payload s’exécute avec les privilèges de l’utilisateur lançant VirtualBox, permettant une fuite du guest vers l’hôte.

Les payloads alternatifs incluent planter une mini ROP chain dans la mémoire heap et copier son adresse dans un callback fréquemment invoqué, ou repointer pfFini/pfDtor eux-mêmes vers des gadgets chaînés pour des écritures répétées.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks