Aanval op Node.js door manipulatie van prototypen van JavaScript-objecten

Onderzoekers van het Helmholtz Center for Information Security (CISPA) en het Royal Institute of Technology (Zweden) analyseerden de toepasbaarheid van de JavaScript-prototypevervuilingstechniek om aanvallen op het Node.js-platform en populaire daarop gebaseerde applicaties te creëren, wat leidde tot code-uitvoering.

De prototype-vervuilingsmethode maakt gebruik van een functie van de JavaScript-taal waarmee u nieuwe eigenschappen kunt toevoegen aan het hoofdprototype van elk object. Applicaties kunnen codeblokken (gadgets) bevatten waarvan de werking wordt beïnvloed door een vervangende eigenschap; de code kan bijvoorbeeld een constructie bevatten als 'const cmd = options.cmd || "/bin/sh"', waarvan de logica zal worden gewijzigd als de aanvaller erin slaagt de eigenschap “cmd” in het root-prototype te vervangen.

Voor een succesvolle aanval is vereist dat de toepassing externe gegevens kan gebruiken om een ​​nieuwe eigenschap in het root-prototype van het object te creëren, en dat bij de uitvoering een gadget wordt aangetroffen die afhankelijk is van de gewijzigde eigenschap. Het wijzigen van het prototype wordt bereikt door de service-eigenschappen “__proto__” en “constructor” in Node.js te verwerken. De eigenschap "__proto__" retourneert het prototype van de klasse van het object, en de eigenschap "constructor" retourneert de functie die is gebruikt om het object te maken.

Als de applicatiecode de toewijzing “obj[a][b] = waarde” bevat en de waarden zijn ingesteld op basis van externe gegevens, kan een aanvaller “a” instellen op de waarde “__proto__” en de installatie van zijn eigen eigenschap bewerkstelligen met de naam “b” en de waarde “value” in het root-prototype van het object (obj.__proto__.b = waarde;), en de eigenschap die in het prototype is ingesteld, zal in alle objecten zichtbaar zijn. Op dezelfde manier kunt u, als de code uitdrukkingen bevat als “obj[a][b][c] = waarde”, door “a” in te stellen op de waarde “constructor” en “b” op “prototype” in alle bestaande objecten definieer een nieuwe eigenschap met de naam "c" en de waarde "waarde".

Voorbeeld van het wijzigen van het prototype: const o1 = {}; const o2 = nieuw object(); o1.__proto__.x = 42; // maak eigenschap “x” in het root-prototype console.log (o2.x); // toegang tot eigenschap “x” vanuit een ander object // de uitvoer zal 42 zijn, omdat het root-prototype is gewijzigd via object o1, dat ook wordt gebruikt in object o2

Voorbeeld van kwetsbare code: function entryPoint (arg1, arg2, arg3){ const obj = {}; const p = obj[arg1]; p[arg2] = arg3; retour p; }

Als de argumenten van de entryPoint-functie worden gevormd uit invoergegevens, kan een aanvaller de waarde “__proto__” doorgeven aan arg1 en een eigenschap maken met een willekeurige naam in het root-prototype. Als u arg2 de waarde "toString" en arg3 de waarde 1 doorgeeft, kunt u de eigenschap "toString" (Object.prototype.toString=1) definiëren en de toepassing laten crashen tijdens de aanroep van toString().

Voorbeelden van situaties die kunnen leiden tot het uitvoeren van code door een aanvaller zijn het aanmaken van de eigenschappen "main", "shell", "exports", "contextExtensions" en "env". Een aanvaller kan bijvoorbeeld een ‘main’-eigenschap maken in het root-prototype van een object, en daarin het pad naar zijn script schrijven (Object.prototype.main = ‘./../../pwned.js’) en deze eigenschap zal worden aangeroepen op het moment van uitvoering in de code van de constructie require("my-package"), als het opgenomen pakket de eigenschap "main" in package.json niet expliciet definieert (als de eigenschap niet is gedefinieerd, het wordt verkregen uit het root-prototype). De eigenschappen “shell”, “exports” en “env” kunnen op dezelfde manier worden vervangen: laat rootProto = Object.prototype; rootProto["exports"] = {".:.:/changelog.js"}; rootProto["1"] = "/pad/naar/npm/scripts/"; // trigger-oproep vereisen("./target.js"); Object.prototype.main = "/pad/naar/npm/scripts/changelog.js"; Object.prototype.shell = "knooppunt"; Object.prototype.env = {}; Object.prototype.env.NODE_OPTIONS = "—inspect-brk=0.0.0.0:1337"; // trigger-aanroep require("bytes");

De onderzoekers analyseerden 10 NPM-pakketten met het grootste aantal afhankelijkheden en ontdekten dat 1958 daarvan geen hoofdeigenschap hebben in package.json, 4420 relatieve paden gebruiken in hun require-instructies en 355 rechtstreeks de opdrachtvervangings-API gebruiken.

Een werkend voorbeeld is een exploit voor het aanvallen van de Parse Server-backend die de eigenschap evalFunctions overschrijft. Om de identificatie van dergelijke kwetsbaarheden te vereenvoudigen, is een toolkit ontwikkeld die statische en dynamische analysemethoden combineert. Tijdens het testen van Node.js zijn elf gadgets geïdentificeerd die kunnen worden gebruikt om aanvallen te organiseren die leiden tot de uitvoering van de code van de aanvaller. Naast Parse Server zijn er ook twee exploiteerbare kwetsbaarheden geïdentificeerd in de NPM CLI.

Bron: opennet.ru

Voeg een reactie