Prototype pollution

Prototype pollution — это уязвимость JavaScript, позволяющая атакующему добавлять произвольные свойства в глобальные прототипы объектов, которые затем могут наследоваться пользовательскими объектами.

prototype-pollution-infographic.svg

Хотя Prototype pollution часто само по себе неэксплуатируемо, оно позволяет атакующему управлять свойствами объектов, к которым в противном случае нет доступа. Если приложение впоследствии обрабатывает контролируемое атакующим свойство небезопасным образом, это потенциально можно скомбинировать с другими уязвимостями. В клиентском JavaScript это обычно приводит к DOM-based XSS, тогда как Prototype pollution на стороне сервера может даже привести к RCE.

Подробнее

Если вы не знакомы с тем, как работают прототипы и наследование в JavaScript, рекомендуем сначала ознакомиться со следующим обзором.

Как возникают уязвимости Prototype pollution?

Уязвимости Prototype pollution обычно возникают, когда функция JavaScript рекурсивно сливает объект, содержащий контролируемые пользователем свойства, с существующим объектом, не выполняя предварительную очистку ключей. Это может позволить атакующему внедрить свойство с ключом __proto__ вместе с произвольными вложенными свойствами.

Из-за особого значения __proto__ в контексте JavaScript операция слияния может присвоить вложенные свойства прототипа объекта вместо самого целевого объекта. В результате атакующий может «загрязнить» прототип свойствами с вредоносными значениями, которые затем могут быть использованы приложением опасным образом.

Можно «загрязнить» любой прототип, но чаще всего это происходит со встроенным объектом Object.prototype.

Успешная эксплуатация загрязнения прототипов требует следующих ключевых компонент:

Источники Prototype pollution

Источником загрязнения прототипов является любой контролируемый пользователем ввод, позволяющий добавлять произвольные свойства в прототипы. Наиболее распространённые источники:

  • URL — через строку запроса (query) или фрагмент (hash)

  • Web messages

Prototype pollution через URL

Рассмотрим следующий URL, содержащий сформированную атакующим строку запроса:

При разборе строки запроса на пары key:value парсер URL может трактовать __proto__ как произвольную строку. Но посмотрим, что произойдёт, если затем эти ключи и значения будут слиты в существующий объект как свойства.

Можно подумать, что свойство __proto__ вместе с вложенным evilProperty просто добавится к целевому объекту следующим образом:

Однако это не так. В какой-то момент операция рекурсивного слияния может выполнить присваивание значения evilProperty с помощью выражения, эквивалентного следующему:

Во время этого присваивания JavaScript-движок трактует __proto__ как геттер прототипа. В результате evilProperty присваивается возвращённому объекту-прототипу, а не самому целевому объекту. Предполагая, что целевой объект использует стандартный Object.prototype, все объекты в среде выполнения JavaScript теперь будут наследовать evilProperty, если только у них уже нет собственного свойства с соответствующим ключом.

На практике внедрение свойства с именем evilProperty вряд ли даст эффект. Однако атакующий может использовать ту же технику, чтобы «загрязнить» прототип свойствами, которые используются приложением или любыми подключёнными библиотеками.

Prototype pollution через JSON-ввод

Контролируемые пользователем объекты часто получаются из строки JSON с помощью метода JSON.parse(). Примечательно, что JSON.parse() также рассматривает любой ключ в JSON-объекте как произвольную строку, включая такие вещи, как __proto__. Это предоставляет ещё один потенциальный вектор загрязнения прототипов.

Предположим, атакующий внедряет следующий вредоносный JSON, например, через web message:

Если это преобразовать в объект JavaScript через метод JSON.parse(), в результирующем объекте действительно будет свойство с ключом __proto__:

Если объект, созданный через JSON.parse(), затем сливается с существующим объектом без надлежащей очистки ключей, это также приведёт к загрязнению прототипа при присваивании, как мы видели в предыдущем примере для URL.

Prototype Pollution sinks

Prototype Pollution sinks — это по сути функция JavaScript или элемент DOM, к которому вы получаете доступ через загрязнение прототипов и который позволяет выполнить произвольный JavaScript или системные команды. Некоторые клиентские приёмники мы подробно рассмотрели в разделе о DOM-based XSS.

Поскольку загрязнение прототипов позволяет управлять свойствами, которые иначе были бы недоступны, это потенциально даёт возможность достичь ряда дополнительных приёмников внутри целевого приложения. Разработчики, незнакомые с загрязнением прототипов, могут ошибочно полагать, что эти свойства не контролируются пользователем, поэтому фильтрация или очистка может быть минимальной.

Prototype Pollution gadgets

Gadgets предоставляет средство превращения уязвимости загрязнения прототипов в реальный эксплойт. Это любое свойство, которое:

  • Используется приложением небезопасно, например передаётся в приёмник без должной фильтрации или очистки.

  • Контролируется атакующим через загрязнение прототипов. Иными словами, объект должен быть способен унаследовать вредоносную версию свойства, добавленную атакующим в прототип.

Свойство не может быть гаджетом, если оно определено непосредственно на самом объекте. В этом случае собственная версия свойства объекта имеет приоритет над любой вредоносной версией, которую вы можете добавить в прототип. Более надежные сайты также могут явно устанавливать прототип объекта в null, что гарантирует отсутствие наследования каких-либо свойств.

Пример гаджета загрязнения прототипов

Многие библиотеки JavaScript принимают объект, с помощью которого разработчики могут задавать различные параметры конфигурации. Код библиотеки проверяет, добавил ли разработчик явно определённые свойства в этот объект, и при наличии корректирует конфигурацию соответствующим образом. Если свойство, представляющее конкретную опцию, отсутствует, часто используется предопределённый вариант по умолчанию.

Упрощённый пример может выглядеть так:

Теперь представим, что код библиотеки использует этот transport_url, чтобы добавить на страницу ссылку на скрипт:

Если разработчики сайта не установили свойство transport_url на своём объекте config, это потенциальный гаджет. В случаях, когда атакующий может «загрязнить» глобальный Object.prototype своим transport_url, это свойство будет унаследовано объектом config и, следовательно, установит src для этого скрипта на домен по выбору злоумышленника.

Если прототип можно «загрязнить» через параметр запроса, например, атакующему достаточно побудить жертву перейти по специально сформированному URL, чтобы заставить её браузер импортировать вредоносный JavaScript-файл с домена, контролируемого атакующим:

Предоставив data: URL, атакующий также может напрямую внедрить полезную нагрузку XSS в строку запроса следующим образом:

Обратите внимание, что завершающий // в этом примере просто комментирует жёстко заданный суффикс /example.js.

Что дальше?

Теперь, когда вы знакомы с концепциями загрязнения прототипов, давайте посмотрим, как находить эти уязвимости в реальных приложениях:

Last updated