Atacul asupra Node.js prin manipularea prototipurilor de obiecte JavaScript

Cercetătorii de la Centrul Helmholtz pentru Securitatea Informației (CISPA) și de la Institutul Regal de Tehnologie (Suedia) au analizat aplicabilitatea tehnicii de poluare a prototipului JavaScript pentru a crea atacuri asupra platformei Node.js și a aplicațiilor populare bazate pe aceasta, ducând la executarea codului.

Metoda de poluare a prototipurilor folosește o caracteristică a limbajului JavaScript care vă permite să adăugați noi proprietăți la prototipul rădăcină al oricărui obiect. Aplicațiile pot conține blocuri de cod (gadget) a căror funcționare este afectată de o proprietate substituită; de exemplu, codul poate conține un construct precum „const cmd = options.cmd || „/bin/sh”’, a cărui logică va fi schimbată dacă atacatorul reușește să înlocuiască proprietatea „cmd” în prototipul rădăcină.

Un atac de succes necesită ca aplicația să poată folosi date externe pentru a crea o nouă proprietate în prototipul rădăcină al obiectului, iar execuția respectivă întâlnește un gadget care depinde de proprietatea modificată. Schimbarea prototipului se realizează prin procesarea proprietăților serviciului „__proto__” și „constructor” în Node.js. Proprietatea „__proto__” returnează prototipul clasei obiectului, iar proprietatea „constructor” returnează funcția folosită pentru a crea obiectul.

Dacă codul aplicației conține atribuirea „obj[a][b] = valoare” și valorile sunt setate din date externe, un atacator poate seta „a” la valoarea „__proto__” și poate realiza instalarea propriei proprietăți. cu numele „b” și valoarea „valoare” în prototipul rădăcină al obiectului (obj.__proto__.b = valoare;), iar proprietatea setată în prototip va fi vizibilă în toate obiectele. În mod similar, dacă codul conține expresii precum „obj[a][b][c] = valoare”, setând „a” la valoarea „constructor” și „b” la „prototip” în toate obiectele existente, puteți definiți o nouă proprietate cu numele „c” și valoarea „valoare”.

Exemplu de schimbare a prototipului: const o1 = {}; const o2 = obiect nou(); o1.__proto__.x = 42; // creează proprietatea „x” în prototipul rădăcină console.log (o2.x); // accesează proprietatea „x” de la alt obiect // ieșirea va fi 42, deoarece prototipul rădăcină a fost schimbat prin obiectul o1, care este folosit și în obiectul o2

Exemplu de cod vulnerabil: function entryPoint (arg1, arg2, arg3){ const obj = {}; const p = obj[arg1]; p[arg2] = arg3; întoarcere p; }

Dacă argumentele funcției entryPoint sunt formate din datele de intrare, atunci un atacator poate transmite valoarea „__proto__” la arg1 și poate crea o proprietate cu orice nume în prototipul rădăcină. Dacă transmiteți arg2 valoarea „toString” și arg3 valoarea 1, puteți defini proprietatea „toString” (Object.prototype.toString=1) și blocați aplicația în timpul apelului la toString().

Exemple de situații care ar putea duce la execuția codului atacatorului includ crearea proprietăților „principal”, „shell”, „exports”, „contextExtensions” și „env”. De exemplu, un atacator poate crea o proprietate „principală” în prototipul rădăcină al unui obiect, scriind în el calea către scriptul său (Object.prototype.main = „./../../pwned.js”) și această proprietate va fi apelată în momentul execuției în codul constructului require ("my-package"), dacă pachetul inclus nu definește în mod explicit proprietatea "principală" în package.json (dacă proprietatea nu este definită, se va obţine din prototipul rădăcină). Proprietățile „shell”, „exports” și „env” pot fi înlocuite în mod similar: let rootProto = Object.prototype; rootProto["exports"] = {".":"./changelog.js"}; rootProto["1"] = "/path/to/npm/scripts/"; // declanșare apel require("./target.js"); Object.prototype.main = "/path/to/npm/scripts/changelog.js"; Object.prototype.shell = „nod”; Object.prototype.env = {}; Object.prototype.env.NODE_OPTIONS = "—inspect-brk=0.0.0.0:1337"; // declanșare apel require("bytes");

Cercetătorii au analizat 10 de pachete NPM cu cel mai mare număr de dependențe și au descoperit că 1958 dintre ele nu au o proprietate principală în package.json, 4420 folosesc căi relative în instrucțiunile lor require, iar 355 folosesc direct API-ul de substituție de comandă.

Un exemplu de lucru este un exploit pentru atacarea backend-ului Parse Server care suprascrie proprietatea evalFunctions. Pentru a simplifica identificarea unor astfel de vulnerabilități, a fost dezvoltat un set de instrumente care combină metode de analiză statică și dinamică. În timpul testării Node.js, au fost identificate 11 gadget-uri care pot fi folosite pentru a organiza atacuri care duc la executarea codului atacatorului. În plus față de Parse Server, au fost identificate și două vulnerabilități exploatabile în NPM CLI.

Sursa: opennet.ru

Adauga un comentariu