Vm2: Pas à l'abri de while(1){}

Créé le 2 janv. 2019  ·  19Commentaires  ·  Source: patriksimek/vm2

Le code suivant n'expirera pas.

const {VM} = require('vm2');
new VM({timeout:1}).run(`
        function main(){
        while(1){}
    }
    new Proxy({}, {
        getPrototypeOf(t){
            global.main();
        }
    })`);
bug confirmed help wanted

Tous les 19 commentaires

Il n'y a probablement pas de bonne solution à cela. NodeJS est un processus unique. La protection while(1){} nécessite plusieurs processus afin que le vérificateur ne soit pas bloqué par le while(1){}. Peut-être que si nous pouvions relancer le projet SpiderNode......

Ceci est dû au test de Decontextify.value pour le prototype de l'objet invité avec instanceOf . Le module Node.js vm est bien sûr à l'abri de cela. Le patch d'un pauvre pourrait être de backdoor Proxy afin que nous puissions détecter tous les objets proxy du bac à sable, mais une approche idéale serait de ne jamais faire confiance à aucun objet et fonction de celui-ci et de mettre contextify.js dans le bac à sable, utiliser des valeurs primitives pour communiquer et des poignées pour référencer des objets invités.

Juste pour que les gens voient, sans navigateur utilise un processus enfant et une transmission de message pour parler avec l'enfant exécutant le code non fiable. C'est un peu une tâche de configuration, mais une fois cela fait, vous serez immunisé contre les attaques while(1){} . La source est ici .

Le but de ce projet semble être de ne pas engendrer un tout
Processus du système d'exploitation. Sinon, il existe de nombreuses solutions disponibles qui sont beaucoup
plus sûr.

Sinon, il existe de nombreuses solutions disponibles qui sont beaucoup plus sécurisées.

Lesquels? Je ne suis pas sûr qu'il y ait une meilleure alternative à VM2 dans NodeJS, du moins pas que j'aie vu

Le mainteneur de cette bibliothèque a signalé cette alternative dans le numéro 80 :

https://github.com/laverdet/isolated-vm

Je ne l'ai pas encore essayé moi-même.

Je n'exécute que du code non fiable dans le processus enfant. Vous ne pouvez pas risquer de l'exécuter dans l'instance principale V8, surtout lorsque vous savez qu'il s'agit d'un seul thread.

Le problème de l'appel direct du moteur V8 est le manque de module d'importation. Dans mon cas, je dois importer le module de demande pour autoriser les opérations HTTP. Il doit également être asynchrone.

Je trouve que vm2 + child_process est une bonne combinaison.

Hey,

moi-même et @harelmo avons créé une bibliothèque au-dessus de vm2 qui est immunisée contre while(1) {} .

Semblable à ce que tian-ma a mentionné, mais nous utilisons des threads de travail node.js au lieu de processus enfants.

jetez un oeil ici : https://www.npmjs.com/package/isolated-runtime et dites-nous ce que vous en pensez :)

Une autre solution de contournement serait de simplement désactiver Proxy dans le bac à sable, n'est-ce pas ?

const vm = new VM({
    sandbox: {
        Proxy: undefined
    },
    timeout: 100
});

En utilisant Promise, il est également possible d'écrire du code qui n'expire jamais :

"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
    Promise.resolve().then(a=>{
        while(1){}
    });
}+')()';
try{
    console.log(new VM({timeout:10}).run(untrusted));
}catch(x){
    console.log(x);
}

Le problème ici est que .then mettra en file d'attente la fonction à appeler plus tard dans la file d'attente des microtâches.
Pour résoudre ce problème, nous devons écrire nos propres fonctions Promise.then et associées ou désactiver Promise.

Cela fonctionne aussi :

const {VM} = require('vm2');

const script = '(' + function() {
    (async function x(){
        await {then: y=>y()};
        while(1){}
    })();
}+')()';

new VM({timeout:10}).run(script);

Donc, passer outre Promise ne fonctionnera pas.
Aucune idée de comment résoudre ce problème.

Bien que cela reste ouvert, nous devrions ajouter une clarification au README pour avertir les utilisateurs potentiels. Cette vulnérabilité permet d'échapper au bac à sable et va ainsi à l'encontre de l'objectif de la bibliothèque.

Lisez-moi a été mis à jour https://github.com/patriksimek/vm2/commit/77f5265ab53b87864a312156ee62b1082787e9b0#diff -04c6e90faac2675aa89e2176d2eec7d8. Et je ne sais pas comment cela pourrait être utilisé pour échapper au bac à sable, vous ne pouvez échapper qu'à la période prévue dans laquelle le script doit s'exécuter.

Cela devrait peut-être être son propre problème, mais pourquoi NodeVM ne prend-il pas en charge le "timeout" ?

@crowder puisque dans NodeVM les setTimeout, setInterval et setImmediate existent, il serait facile de contourner le délai d'attente. Peut-être qu'il y a aussi d'autres raisons, mais je ne travaille pas avec NodeVM, seulement VM.

@XmiliaH n'implémenterait- il pas un délai d'attente pour empêcher au moins des boucles infinies involontaires?
La combinaison malveillante while(true) + setTimeout serait toujours un problème, mais toute simple boucle infinie involontaire effectuée par erreur pourrait être gérée en prenant en charge le délai d'attente.

Ma préoccupation est que NodeVM permet d'exiger des modules qui peuvent être des modules hôtes et que le délai d'expiration pendant qu'ils changent d'état global peut être mauvais. De plus, le NodeVM renvoie un module et les utilisateurs appelleraient très probablement les fonctions exposées par le module, mais le délai d'expiration ne s'appliquerait qu'au chargement du module et non aux appels de fonction.
Si vous souhaitez appeler une fonction avec un délai d'attente, vous pouvez simplement l'implémenter vous-même comme

function doWithTimeout(fn, timeout) {
    let ctx = CACHE.timeoutContext;
    let script = CACHE.timeoutScript;
    if (!ctx) {
        CACHE.timeoutContext = ctx = vm.createContext();
        CACHE.timeoutScript = script = new vm.Script('fn()', {
            filename: 'timeout_bridge.js',
            displayErrors: false
        });
    }
    ctx.fn = fn;
    try {
        return script.runInContext(ctx, {
            displayErrors: false,
            timeout
        });
    } finally {
        ctx.fn = null;
    }
}

Bon point @XmiliaH et merci pour l'exemple
Je vois que votre exemple utilise le package vm et non vm2
Ce serait bien de pouvoir utiliser vm2 et de toujours pouvoir l'arrêter

@szydan Le code que j'ai posté est utilisé par VM2 pour expirer la VM. Si le NodeVM avait également un délai d'attente, il utiliserait la même fonctionnalité.

Cette page vous a été utile?
0 / 5 - 0 notes