Cercetătorii de la Centrul Helmholtz pentru Securitate Informațională (CISPA) și de la Institutul Tehnologic Regal (Suedia) au analizat aplicabilitatea tehnicii de poluare a prototipurilor JavaScript pentru a crea atacuri asupra platformei Node.js și aplicațiilor populare bazate pe aceasta, care duc la executarea de cod.
Metoda de poluare a prototipurilor folosește o caracteristică a limbajului JavaScript care permite adăugarea de noi proprietăți în prototipul de bază al oricărui obiect. În aplicații pot exista blocuri de cod (widget-uri) al căror comportament este afectat de proprietățile inserate, de exemplu, în cod se poate găsi o construcție de genul 'const cmd = options.cmd || "/bin/sh"', logica acesteia fiind modificată dacă atacatorul reușește să insereze proprietatea "cmd" în prototipul de bază.
Pentru ca un atac să fie efectuat cu succes, este necesar ca datele externe din aplicație să fie utilizate pentru a crea o nouă proprietate în prototipul de bază al obiectului și ca în timpul execuției să fie întâlnit un widget care depinde de proprietatea modificată. Modificarea prototipului se face prin manipularea în Node.js a proprietăților speciale "__proto__" și "constructor". Proprietatea "__proto__" returnează prototipul clasei obiectului, iar proprietatea "constructor" returnează funcția folosită pentru a crea obiectul.
Dacă în codul aplicației apare o atribuție "obj[a][b] = value" și valorile sunt setate din date externe, atacatorul poate seta "a" la "__proto__" și poate obține setarea unei proprietăți cu numele "b" și valoarea "value" în prototipul de bază al obiectului (obj.__proto__.b = value;), astfel încât proprietatea setată în prototip va fi vizibilă în toate obiectele. Similar, dacă în cod se întâlnesc expresii de forma "obj[a][b][c] = value", setând "a" la "constructor" și "b" la "prototype", în toate obiectele existente se poate defini o nouă proprietate cu numele "c" și valoarea "value".
Exemplu de modificare a prototipului: const o1 = {}; const o2 = new Object(); o1.__proto__.x = 42; // creăm o proprietate "x" în prototipul de bază console.log(o2.x); // accesăm proprietatea "x" dintr-un alt obiect // la ieșire vom obține 42, deoarece prin obiectul o1 a fost modificat prototipul de bază utilizat și în obiectul o2.
Exemplu de cod vulnerabil: function entryPoint(arg1, arg2, arg3) { const obj = {}; const p = obj[arg1]; p[arg2] = arg3; return p; }
Dacă argumentele funcției entryPoint sunt generate din datele de intrare, atunci un atacator poate transmite în arg1 valoarea „__proto__” și poate crea în prototipul rădăcină o proprietate cu orice nume. Dacă se transmite în arg2 valoarea „toString” și în arg3 - 1, se poate defini proprietatea „toString” (Object.prototype.toString=1) și se poate provoca o cădere a aplicației în timpul apelului funcției toString().
Ca exemplu de situații care pot duce la execuția codului atacatorului, se citează crearea proprietăților „main”, „shell”, „exports”, „contextExtensions” și „env”. De exemplu, atacatorul poate crea în prototipul rădăcină al obiectului proprietatea „main”, scriind calea către scriptul său (Object.prototype.main = „./../..//pwned.js”) și această proprietate va fi apelată în momentul executării codului din construcția require(„my-package”), dacă pachetul importat nu definește în mod explicit în package.json proprietatea „main” (dacă proprietatea nu este definită, aceasta va fi obținută din prototipul rădăcină). În mod similar, pot fi înlocuite proprietățile „shell”, „exports” și „env”: let rootProto = Object.prototype; rootProto[„exports”] = {„.”: „./changelog.js”}; rootProto[„1”] = „/path/to/npm/scripts/”; // trigger call require („./target.js”); Object.prototype.main = „/path/to/npm/scripts/changelog.js”; Object.prototype.shell = „node”; Object.prototype.env = {}; Object.prototype.env.NODE_OPTIONS = „—inspect-brk=0.0.0.0:1337”; // trigger call require („bytes”);
Cercetătorii au analizat 10.000 de pachete NPM cu cele mai multe dependențe și au descoperit că 1958 dintre acestea nu au proprietatea main în package.json, 4420 folosesc căi relative în expresia require, iar 355 folosesc direct API-ul pentru substituirea comenzilor.
Ca exemplu funcțional se poate menționa un exploit pentru atacul asupra backendului Parse Server, care redefineste proprietatea evalFunctions. Pentru a facilita identificarea unor astfel de vulnerabilități, a fost dezvoltat un instrumentar care combină metode de analiză statică și dinamică. În timpul testării Node.js, au fost descoperite 11 gadgeturi care pot fi folosite pentru a organiza atacuri ce duc la execuția codului atacatorului. Pe lângă Parse Server, au fost identificate și două vulnerabilități exploatabile în NPM CLI.
Sursa: opennet.ro
