វាយប្រហារលើ Node.js តាមរយៈការរៀបចំគំរូវត្ថុ JavaScript

Исследователи Центра Гельмгольца по информационной безопасности (CISPA) и Королевского технологического института (Швеция) проанализировали применимость техники засорения прототипа объектов JavaScript («prototype pollution») для создания атак на платформу Node.js и популярные приложения на её основе, приводящих к выполнению кода.

Метод засорения прототипа использует особенность языка JavaScript, позволяющую добавить новые свойства в корневой прототип любого объекта. В приложениях могут встречаться блоки кода (гаджеты), на работу которых влияет подставленное свойство, например, в коде может быть конструкция вида ‘const cmd = options.cmd || «/bin/sh»‘, логика работы которой будет изменена, если атакующий сумеет подставить свойство «cmd» в корневой прототип.

Для успешного совершения атаки требуется, чтобы в приложении поступающие извне данные могли использоваться для создания нового свойства в корневом прототипе объекта, а также чтобы в ходе выполнения встречался гаджет, зависящий от изменённого свойства. Изменение прототипа осуществляется благодаря обработке в Node.js служебных свойств «__proto__» и «constructor». Свойство «__proto__» возвращает прототип класса объекта, а свойство «constructor» возвращает функцию, используемую для создания объекта.

Если в коде приложения встречается присвоение «obj[a][b] = value» и значения выставляются из внешних данных, атакующий может выставить «a» в значение «__proto__» и добиться установки своего свойства с именем «b» и значением «value» в корневом прототипе объекта (obj.__proto__.b = value;), при этом выставленное в прототипе свойство будет видно во всех объектах. Аналогично если в коде встречаются выражения вида «obj[a][b][c] = value», выставив «a» в значение «constructor», а «b» в «prototype» во всех существующих объектах можно определить новое свойство с именем «c» и значением «value».

Пример изменения прототипа: const o1 = {}; const o2 = new Object () ; o1.__proto__.x = 42; // создаём в корневом прототипе свойство «x» console.log (o2.x); // обращаемся к свойству «x» из другого объекта // на выходе получим 42, так как через объект o1 был изменён корневой прототип, используемый в том числе и в объекте o2

Пример уязвимого кода: function entryPoint (arg1, arg2, arg3){ const obj = {}; const p = obj[arg1]; p[arg2] = arg3; return p; }

Если аргументы функции entryPoint формируются из входных данных, то атакующий может передать в arg1 значение «__proto__» и создать в корневом прототипе свойство с любым именем. Если передать в arg2 значение «toString», а в arg3 — 1, можно определить свойство «toString» (Object.prototype.toString=1) и добиться краха приложения во время вызова функции toString().

В качестве примера ситуаций, которые могут привести к выполнению кода атакующего, приводится создание свойств «main», «shell», «exports», «contextExtensions» и «env». Например, атакующий может создать в корневом прототипе объекта свойство «main», записав в него путь к своему скрипту (Object.prototype.main = «./../../pwned.js») и данное свойство будет вызвано в момент выполнения в коде конструкции require(«my-package»), если подключаемый пакет явно не определяет в package.json свойство «main» (если свойство не определено, оно будет получено из корневого прототипа). Аналогично могут быть подставлены свойства «shell», «exports» и «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»);

Исследователи проанализировали 10 тысяч NPM-пакетов, имеющих наибольшее число зависимостей, и выявили, что 1958 из них не имеют свойства main в package.json, 4420 используют относительные пути в выражении require, а 355 напрямую используют API для подстановки команд.

В качестве работающего примера можно привести эксплоит для атаки на бэкенд Parse Server, переопределяющий свойство evalFunctions. Для упрощения выявления подобных уязвимостей разработан инструментарий, комбинирующий методы статического и динамического анализа. В ходе тестирования Node.js было выявлено 11 гаджетов, которые можно использовать для организации атак, приводящих к выполнению кода атакующего. Помимо Parse Server, две эксплуатируемые уязвимости также были выявлены в NPM CLI.

ប្រភព: opennet.ru

បន្ថែមមតិយោបល់