Forskere fra Helmholtz Center for Informationssikkerhed (CISPA) og det Kgl. Teknologiske Institut (Sverige) har analyseret anvendeligheden af teknikken prototype-forurening af objekter i JavaScript til at skabe angreb på Node.js-platformen og populære applikationer baseret på den, hvilket kan føre til kodeeksekvering.
Metoden til prototype-forurening udnytter et særligt træk ved JavaScript, som gør det muligt at tilføje nye egenskaber til rodprototypen for ethvert objekt. I applikationer kan der findes kodeblokke (gadgets), hvis funktion påvirkes af en indsat egenskab; for eksempel kan der være en konstruktion som ‘const cmd = options.cmd || "/bin/sh"’, hvis logik ændres, hvis angriberen formår at indsætte egenskaben «cmd» i rodprototypen.
For at et angreb skal lykkes, kræves det, at data udefra kan bruges til at oprette en ny egenskab i rodprototypen af et objekt, samt at der under udførelsen findes en gadget, der afhænger af den ændrede egenskab. Ændring af prototypen sker ved at behandle de interne egenskaber «__proto__» og «constructor» i Node.js. Egenskaben «__proto__» returnerer prototypen af objektklassen, mens «constructor» returnerer funktionen, der bruges til at oprette objektet.
Hvis der i applikationskoden findes en tildeling «obj[a][b] = value» og værdierne tildeles fra eksterne data, kan angriberen indstille «a» til værdien «__proto__» og opnå, at hans egenskab med navnet «b» og værdien «value» bliver indsat i rodprototypen af objektet (obj.__proto__.b = value;), hvor det indstillede prototypen-egenskab vil være synligt for alle objekter. Tilsvarende, hvis koden indeholder udtryk som «obj[a][b][c] = value», kan man ved at indstille «a» til værdien «constructor», og «b» til «prototype», definere en ny egenskab med navnet «c» og værdien «value» i alle eksisterende objekter.
Eksempel på ændring af prototypen: const o1 = {}; const o2 = new Object(); o1.__proto__.x = 42; // opretter egenskaben «x» i rodprototypen console.log(o2.x); // tilgår egenskaben «x» fra et andet objekt // outputtet vil være 42, da rodprototypen, som også anvendes af objektet o2, blev ændret via objektet o1.
Eksempel på sårbar kode: function entryPoint(arg1, arg2, arg3) { const obj = {}; const p = obj[arg1]; p[arg2] = arg3; return p; }
Hvis argumenterne i funktionen entryPoint dannes ud fra inputdata, kan en angriber overføre værdien «__proto__» til arg1 og skabe en egenskab med et vilkårligt navn i den globale prototype. Hvis man overfører værdien «toString» til arg2 og 1 til arg3, kan man definere egenskaben «toString» (Object.prototype.toString=1) og få applikationen til at gå ned under kaldet til funktionen toString().
Eksempler på situationer, der kan føre til, at angriberens kode udføres, inkluderer at oprette egenskaber som «main», «shell», «exports», «contextExtensions» og «env». For eksempel kan en angriber skabe egenskaben «main» i den globale prototype ved at skrive stien til deres script (Object.prototype.main = «.\/..\/..\/pwned.js») og denne egenskab vil blive kaldt, når koden i konstruktionen require(«my-package») udføres, hvis den tilsluttede pakke ikke eksplicit definerer egenskaben «main» i package.json (hvis egenskaben ikke er defineret, vil den blive hentet fra den globale prototype). Lignende kan egenskaberne «shell», «exports» og «env» indsættes: let rootProto = Object.prototype; rootProto[«exports»] = {«.»:».\/changelog.js»}; rootProto[«1»] = «\/path\/to\/npm\/scripts\/»; \/\/ trigger kald 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 kald require («bytes»);
Forskere har analyseret 10.000 NPM-pakker med det højeste antal afhængigheder og opdaget, at 1958 af dem ikke har egenskaben main i package.json, 4420 bruger relative stier i require-udtrykket, og 355 bruger direkte API'et til at indsætte kommandoer.
Et fungerende eksempel er et exploit til angreb på backend'en af Parse Server, der overskriver egenskaben evalFunctions. For at forenkle identificeringen af sådanne sårbarheder er der udviklet værktøjer, der kombinerer metoder til statisk og dynamisk analyse. I løbet af testningen af Node.js blev der identificeret 11 gadgets, der kan bruges til at organisere angreb, der fører til udførelse af angriberens kode. Udover Parse Server blev der også identificeret to udnyttelige sårbarheder i NPM CLI.
Kilde: opennet.ru
