Atak na Node.js poprzez manipulację prototypami obiektów JavaScript

Naukowcy z Centrum Bezpieczeństwa Informacji Helmholtza (CISPA) i Królewskiego Instytutu Technologii (Szwecja) przeanalizowali możliwość zastosowania techniki zanieczyszczania prototypów JavaScript do tworzenia ataków na platformę Node.js i oparte na niej popularne aplikacje, prowadzących do wykonania kodu.

Metoda zanieczyszczania prototypów wykorzystuje funkcję języka JavaScript, która pozwala na dodanie nowych właściwości do prototypu głównego dowolnego obiektu. Aplikacje mogą zawierać bloki kodu (gadżety), na których działanie ma wpływ podstawiona właściwość, na przykład kod może zawierać konstrukcję taką jak „const cmd = options.cmd || „/bin/sh”', którego logika zostanie zmieniona, jeśli atakującemu uda się zastąpić właściwość „cmd” w prototypie głównym.

Skuteczny atak wymaga, aby aplikacja mogła użyć danych zewnętrznych do utworzenia nowej właściwości w prototypie głównym obiektu, a wykonanie napotkało gadżet zależny od zmodyfikowanej właściwości. Zmiana prototypu odbywa się poprzez przetworzenie właściwości usługi „__proto__” i „konstruktor” w Node.js. Właściwość „__proto__” zwraca prototyp klasy obiektu, a właściwość „konstruktor” zwraca funkcję użytą do utworzenia obiektu.

Jeśli kod aplikacji zawiera przypisanie „obj[a][b] = wartość”, a wartości są ustawione na podstawie danych zewnętrznych, atakujący może ustawić „a” na wartość „__proto__” i osiągnąć instalację własnej właściwości z nazwą „b” i wartością „value” w prototypie głównym obiektu (obj.__proto__.b = wartość;), a właściwość ustawiona w prototypie będzie widoczna we wszystkich obiektach. Podobnie, jeśli kod zawiera wyrażenia takie jak „obj[a] [b] [c] = wartość”, ustawiając „a” na wartość „konstruktor”, a „b” na „prototyp” we wszystkich istniejących obiektach, możesz zdefiniuj nową właściwość o nazwie „c” i wartości „wartość”.

Przykład zmiany prototypu: const o1 = {}; const o2 = nowy obiekt(); o1.__proto__.x = 42; // utwórz właściwość „x” w prototypie głównym console.log (o2.x); // uzyskaj dostęp do właściwości „x” z innego obiektu // wynikiem będzie 42, ponieważ prototyp główny został zmieniony przez obiekt o1, który jest również używany w obiekcie o2

Przykład podatnego kodu: funkcja EntryPoint (arg1, arg2, arg3){ const obj = {}; const p = obj[arg1]; p[arg2] = arg3; zwróć p; }

Jeśli argumenty funkcji EntryPoint są utworzone z danych wejściowych, osoba atakująca może przekazać wartość „__proto__” do arg1 i utworzyć właściwość o dowolnej nazwie w prototypie głównym. Jeśli przekażesz arg2 wartość „toString” i arg3 wartość 1, możesz zdefiniować właściwość „toString” (Object.prototype.toString=1) i zawiesić aplikację podczas wywołania toString().

Przykłady sytuacji, które mogą prowadzić do wykonania kodu przez osobę atakującą, obejmują utworzenie właściwości „main”, „shell”, „exports”, „contextExtensions” i „env”. Na przykład atakujący może utworzyć właściwość „main” w prototypie głównym obiektu, zapisując w niej ścieżkę do swojego skryptu (Object.prototype.main = „./../../pwned.js”) i właściwość ta zostanie wywołana w momencie wykonania w kodzie konstrukcji require("my-package"), jeśli dołączony pakiet nie definiuje jawnie właściwości "main" w package.json (jeżeli właściwość nie jest zdefiniowana, zostanie on uzyskany z prototypu głównego). W podobny sposób można zastąpić właściwości „shell”, „exports” i „env”: niech rootProto = Object.prototype; rootProto["eksport"] = {".":"./changelog.js"}; rootProto["1"] = "/ścieżka/do/npm/skrypty/"; // wywołanie wywołania require("./target.js"); Object.prototype.main = "/ścieżka/do/npm/scripts/changelog.js"; Obiekt.prototype.shell = "węzeł"; Obiekt.prototyp.env = {}; Object.prototype.env.NODE_OPTIONS = "—inspect-brk=0.0.0.0:1337"; // wywołanie wywołania require("bajty");

Badacze przeanalizowali 10 1958 pakietów NPM z największą liczbą zależności i odkryli, że 4420 z nich nie ma właściwości main w package.json, 355 używa ścieżek względnych w swoich instrukcjach require, a XNUMX bezpośrednio korzysta z API podstawienia poleceń.

Działającym przykładem jest exploit umożliwiający atak na backend serwera Parse Server, który zastępuje właściwość evalFunctions. Aby uprościć identyfikację takich podatności, opracowano zestaw narzędzi łączący metody analizy statycznej i dynamicznej. Podczas testów Node.js zidentyfikowano 11 gadżetów, które można wykorzystać do zorganizowania ataków prowadzących do wykonania kodu atakującego. Oprócz Parse Server w interfejsie CLI NPM zidentyfikowano także dwie możliwe do wykorzystania luki.

Źródło: opennet.ru

Dodaj komentarz