Vedci z Helmholtzovho centra pre informačnú bezpečnosť (CISPA) a Kráľovského technologického inštitútu (Švédsko) analyzovali použiteľnosť techniky „zasypania prototypu“ objektov JavaScript („prototype pollution“) na vytváranie útokov na platformu Node.js a populárne aplikácie postavené na jej základoch, čo vedie k vykonaniu kódu.
Metóda zasypania prototypu využíva vlastnosť jazyka JavaScript, ktorá umožňuje pridať nové atribúty do koreňového prototypu akéhokoľvek objektu. V aplikáciách sa môžu objavovať bloky kódu (gadžety), ktorých fungovanie ovplyvňuje podstrčený atribút, napríklad v kóde môže byť konštrukcia 'const cmd = options.cmd || "/bin/sh"', logiku fungovania ktorej je možné zmeniť, ak sa útočníkovi podarí podstrčiť atribút "cmd" do koreňového prototypu.
Na úspešné vykonanie útoku je potrebné, aby dáta prichádzajúce z vonku mohli byť použité na vytvorenie nového atribútu v koreňovom prototypu objektu, a tiež aby sa počas vykonávania našiel gadžet, ktorý závisí od zmeneného atribútu. Zmena prototypu sa vykonáva prostredníctvom spracovania vo Node.js špeciálnych atribútov "__proto__" a "constructor". Atribút "__proto__" vráti prototyp triedy objektu, zatiaľ čo atribút "constructor" vráti funkciu, ktorá sa používa na vytvorenie objektu.
Ak sa v kóde aplikácie objaví priradenie "obj[a][b] = value" a hodnoty sú nastavené z vonkajších dát, môže útočník nastaviť "a" na hodnotu "__proto__" a dosiahnuť nastavenie svojho atribútu s názvom "b" a hodnotou "value" do koreňového prototypu objektu (obj.__proto__.b = value;), pričom nastavený atribút v prototypu bude viditeľný vo všetkých objektoch. Podobne, ak sa v kóde objavia výrazy ako "obj[a][b][c] = value", nastavením "a" na hodnotu "constructor" a "b" na "prototype" je možné v existujúcich objektoch definovať nový atribút s názvom "c" a hodnotou "value".
Príklad zmeny prototypu: const o1 = {}; const o2 = new Object(); o1.__proto__.x = 42; // vytvárame v koreňovom prototypu atribút "x" console.log(o2.x); // získavame prístup k atribútu "x" z iného objektu // na výstupe dostaneme 42, pretože prostredníctvom objektu o1 bol zmenený koreňový prototyp, ktorý sa používa aj v objekte o2.
Príklad zraniteľného kódu: function entryPoint (arg1, arg2, arg3){ const obj = {}; const p = obj[arg1]; p[arg2] = arg3; return p; }
Ak sú argumenty funkcie entryPoint zostavené z vstupných údajov, útočník môže do arg1 poslať hodnotu „__proto__“ a vytvoriť v koreňovom prototype vlastnosť s akýmkoľvek názvom. Ak sa do arg2 posunie hodnota „toString“ a do arg3 hodnota 1, je možné definovať vlastnosť „toString“ (Object.prototype.toString=1) a spôsobiť pád aplikácie počas volania funkcie toString().
Ako príklad situácií, ktoré môžu viesť k vykonaniu kódu útočníka, je uvedené vytváranie vlastností „main“, „shell“, „exports“, „contextExtensions“ a „env“. Napríklad, útočník môže vytvoriť v koreňovom prototype objektu vlastnosť „main“, do ktorej zapíše cestu k svojmu skriptu (Object.prototype.main = „.\/..\/..\/pwned.js“) a táto vlastnosť bude vyvolaná v okamihu vykonávania kódu v konštrukcii require(„my-package“), ak pripojený balík výslovne neurčuje v package.json vlastnosť „main“ (ak nie je vlastnosť definovaná, získava sa z koreňového prototype). Podobne môžu byť podstrčené vlastnosti „shell“, „exports“ a „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");
Výskumníci analyzovali 10 tisíc NPM balíkov s najväčším počtom závislostí a zistili, že 1958 z nich nemá v package.json vlastnosť main, 4420 používa relatívne cesty v výraze require a 355 priamo používa API na podstrčenie príkazov.
Ako funkčný príklad je možné uviesť exploit na útok na backend Parse Server, ktorý predefinováva vlastnosť evalFunctions. Pre jednoduchosť identifikácie podobných zraniteľností bol vyvinutý nástroj kombinujúci metódy statickej a dynamickej analýzy. Počas testovania Node.js bolo identifikovaných 11 gadgetov, ktoré je možné využiť na realizáciu útokov vedúcich k vykonaniu kódu útočníka. Okrem Parse Server boli tiež identifikované dve zneužiteľné zraniteľnosti v NPM CLI.
Zdroj: opennet.ru
