Image d’illustration générée par IA
isolated-vm : une faille critique peut transformer un sandbox JavaScript en RCE sur l’hôte
Une faille critique dans isolated-vm permet du code non fiable de prendre le contrôle de l'hôte Node.js via une corruption mémoire, avec risque d'exécution à distance.
Texte généré par intelligence artificielle, publié sans relecture humaine. Transparence IA
Une corruption mémoire brise la séparation entre le guest et l’hôte
Une vulnérabilité critique dans la bibliothèque Node.js isolated-vm permet à du code JavaScript non fiable de provoquer le crash du processus hôte et, dans certaines conditions, de prendre le contrôle de son flux d’exécution.
Le problème concerne le code natif C++ qui relie les objets JavaScript de V8 aux structures mémoire utilisées par isolated-vm. La faille combine type confusion, corruption mémoire et condition de concurrence time-of-check/time-of-use (TOCTOU).
L’impact est particulièrement sérieux, car isolated-vm est précisément conçue pour exécuter du code potentiellement hostile dans des isolates V8. Ces environnements isolent le tas, l’état d’exécution et le garbage collector, fournissant une frontière de sécurité sans nécessiter systématiquement de conteneurs ou de machines virtuelles.
La vulnérabilité compromet cette frontière. Le code guest peut parvenir à influencer le processus hôte, avec un risque potentiel d’exécution de code à distance sur l’hôte. Aucun élément ne fait état d’une exploitation de la vulnérabilité dans le cadre de campagnes réelles.
La vulnérabilité a été découverte le 21 août 2026. Aucun identifiant CVE associé au problème n’est connu.
La faille provient d’une double lecture de transferList
Le point d’entrée est le mécanisme suivant :
ivm.ExternalCopy(value, { transferList })
ExternalCopy permet de sérialiser des données dans un isolate, puis de les reconstruire dans un autre. Pour transférer efficacement de grands ArrayBuffer, le buffer source peut être « détaché » et l’espace mémoire correspondant transmis à la destination.
Lors de la construction de ExternalCopySerialized, isolated-vm analyse toutefois transferList à deux reprises. Lors du premier parcours, elle vérifie que chaque élément est un ArrayBuffer. Lors du second, elle récupère de nouveau les éléments et les convertit sans renouveler la vérification du type.
Cette différence est déterminante. La lecture des éléments s’effectue via Get(), qui ne se contente pas de récupérer une valeur existante : elle peut exécuter des accessor JavaScript, notamment des index getter.
Un getter contrôlé par le guest peut donc renvoyer un véritable ArrayBuffer lors de la validation, puis une valeur totalement différente lors de la seconde lecture. Cette nouvelle valeur est ensuite transmise à As<ArrayBuffer>(), qui se comporte dans ce chemin comme un cast non vérifié plutôt que comme une conversion sûre.
Le contrôle porte ainsi sur une valeur, tandis que l’utilisation ultérieure peut en concerner une autre.
Comment la valeur contrôlée atteint le code C++
La chaîne vulnérable traverse plusieurs composants internes :
src/external_copy/serializer.cc, oùtransferListest parcourue deux fois ;src/isolate/generic/array.h:42, dont l’itérateur lit les éléments viaarray->Get(context, index);src/external_copy/external_copy.cc:397, oùExternalCopyArrayBuffer::Transferutilise le handle considéré, à tort, comme unArrayBuffer.
Lors du second parcours, le handle peut donc contenir une valeur qui ne représente aucun buffer. Le code tente malgré tout des opérations réservées à un objet valide, notamment :
- vérifier si le buffer peut être détaché ;
- obtenir son backing store ;
- détacher le buffer ;
- gérer le
shared_ptr<BackingStore>associé à la mémoire.
Un petit entier tel que 0x41414141 peut être interprété par V8 comme un SMI, c’est-à-dire un entier directement représenté dans une valeur tagged. Le code natif le traite pourtant comme un objet et tente de le déréférencer.
Il en résulte une adresse calculée à partir de données contrôlées par l’attaquant. En outre, lors de la destruction du shared_ptr, le chemin peut effectuer un appel indirect via un pointeur vtable extrait de cette mémoire. Cette situation fournit une primitive potentielle de control-flow hijacking, c’est-à-dire de détournement du flux d’exécution du processus.
La classification technique inclut CWE-843, accès à une ressource via un type incompatible, et CWE-704, interprétation ou conversion incorrecte d’un type.
Le guest peut atteindre l’API indirectement
Le constructeur ExternalCopy est exposé côté hôte, mais l’attaque n’exige pas nécessairement que l’hôte appelle directement l’API avec des données déjà manipulées.
La classe peut être transférée au guest via l’option externalCopy du mécanisme transferable. Il suffit que le code non fiable dispose d’un seul objet ivm.Reference. À partir de cette référence, il peut récupérer le constructeur comme suit :
const ExternalCopy =
ref.getSync('anyKey', { externalCopy: true }).constructor;
Le guest peut alors préparer un transferList contenant un getter au comportement variable. Lors de la première lecture, le getter renvoie un ArrayBuffer valide et passe le contrôle initial. Lors de la seconde, il peut renvoyer, par exemple, 0x41414141.
Les embedder qui exécutent du JavaScript non fiable dans un isolate et partagent avec ce code au moins un objet ivm.Reference sont donc exposés.
Un scénario plus direct existe également : le code hôte peut être vulnérable s’il transmet à ExternalCopy un tableau transferList contrôlable par l’utilisateur. Dans ce cas, le guest n’a pas besoin de disposer d’une référence transférée.
Le PoC démontre un crash contrôlable
La preuve de concept a été vérifiée avec :
isolated-vm7.0.0 installée depuis npm ;- Node.js 26.5.0 ;
- macOS sur architecture arm64.
Le problème est reproductible sur toutes les plateformes, même si les adresses et les détails du fault peuvent varier.
L’exécution du PoC avec :
node poc.js
entraîne l’arrêt du processus hôte avec :
- le signal SIGSEGV ;
- le code de sortie 139 ;
- une erreur dans
v8::ArrayBuffer::IsDetachable; - l’adresse de fault
0x4141414100000047.
Cette dernière valeur est significative, car elle provient de l’entier contrôlé par le guest, interprété comme un SMI. Il ne s’agit donc pas simplement d’un déréférencement nul ou d’un crash aléatoire.
Le crash constitue l’impact minimal démontré. Dans des scénarios plus complexes, la même corruption pourrait permettre des lectures ou écritures vers des adresses choisies par l’attaquant, ainsi que le contrôle du flux du processus hôte. Il en résulte un risque d’évasion du sandbox et d’exécution de code sur l’hôte.
Versions corrigées et mesures à prendre pour les administrateurs
Les correctifs sont disponibles dans :
- isolated-vm 6.2.0 ;
- isolated-vm 7.0.1.
Les utilisateurs de la branche 6 doivent effectuer une mise à jour vers la version 6.2.0 au minimum ; ceux de la branche 7 doivent passer au moins à la version 7.0.1. Il convient également de vérifier les dépendances transitives qui incluent isolated-vm, notamment dans les services exécutant des scripts fournis par des utilisateurs, des plugins ou des contenus distants.
Le correctif empêche l’exécution de JavaScript pendant l’opération de copie. Le contenu de transferList ne peut ainsi plus changer entre la phase de validation et celle du cast.
Les mesures prioritaires sont les suivantes :
- mettre à jour isolated-vm vers la version corrigée de la branche utilisée ;
- recenser les embedder qui exécutent du JavaScript non fiable ;
- identifier les cas où le guest reçoit des objets
ivm.Reference; - contrôler les utilisations d’
ExternalCopyavec untransferListinfluençable par les entrées utilisateur ; - réduire, lorsque cela est possible, le partage d’objets
ivm.Referenceavec du code non fiable.
En matière de surveillance, les crashs avec SIGSEGV, le code 139 et des adresses de fault anormales ou pouvant être reliées à des valeurs contrôlées doivent faire l’objet d’une analyse immédiate. Il s’agit d’indicateurs techniques compatibles avec le PoC, mais qui ne suffisent pas à eux seuls à démontrer une compromission.
Aucun indicateur de compromission fondé sur le réseau, les fichiers ou les processus n’est connu. On ignore également si la vulnérabilité a été ajoutée au catalogue KEV de la CISA, et aucune information ne fait état d’une échéance de remédiation associée.
Sources
Cet article est une réécriture originale fondée sur les sources ci-dessous.
