Недавно я обнаружил уязвимость, которая немного менее распространена и довольно интересна тем, как она работает.

Привет, охотники, сегодня я решил поделиться с вами своим последним маленьким открытием и немного подробнее объяснить, как работает прототип загрязнения.

Что такое прототипное загрязнение?

Определение из PortSwigger: Загрязнение прототипа — это уязвимость JavaScript, которая позволяет злоумышленнику добавлять произвольные свойства к прототипам глобальных объектов, которые затем могут быть унаследованы объектами, определенными пользователем. Хотя прототипное загрязнение часто невозможно использовать как отдельную уязвимость, оно позволяет злоумышленнику контролировать свойства объектов, которые в противном случае были бы недоступны. Если приложение впоследствии будет обращаться с контролируемым злоумышленником свойством небезопасным образом, это потенциально может быть связано с другими уязвимостями.

Таким образом, мы понимаем, что загрязнение прототипа нельзя использовать как таковое, но должно быть связано. Прежде чем вдаваться в подробности, необходимо кратко вспомнить некоторые основные понятия JavaScript, чтобы понять, как работает эта уязвимость.

Почти все является объектом в JS

Объект — в JavaScript — это набор пар ключ-значение, значения которых могут быть разных типов (строка, число, логическое значение...). Минималистский синтаксис объекта выглядит следующим образом:

let myObject = {
 prop1: "A simple string",
 prop2: 100,
 prop3: true
}

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

myObject.prop1   // -> will return "A simple string"

Во втором используется обозначение скобок :

myObject['prop2']   // -> will return 100

Как мы увидим чуть позже, мы будем использовать один из этих двух синтаксисов для загрязнения прототипа. На мгновение запомните, что почти все в JS является объектом и что каждый объект связан с другим объектом — родительским — называемым прототипом, из которого он наследует методы и свойства.

Объектно-ориентированная парадигма

Javascript — это мультипарадигменный язык программирования, включающий функциональное, объектно-ориентированное, процедурное и прототипное программирование. В нашем случае нас будет интересовать объектно-ориентированная сторона, это шаблон программирования, который, как следует из названия, использует объекты и состоит из объектов классов, и прототипы.

Мы не собираемся развивать здесь эту обширную тему, а лишь попытаемся понять, как наследование и прототипирование работают в JS.

Как поясняется в PortSwigger, всякий раз, когда вы ссылаетесь на свойство объекта, механизм JavaScript сначала пытается получить к нему доступ непосредственно в самом объекте. Если у объекта нет соответствующего свойства, движок JavaScript вместо этого ищет его в прототипе объекта.
-› Под «прототипом объекта» здесь подразумевается родительский объект объект.

Когда вы создаете новый объект, JavaScript автоматически назначает его одному из своих встроенных прототипов в зависимости от типа вновь созданного объекта:

Тип моего объекта здесь — строка. Когда я пытаюсь получить доступ к свойству моего объекта с помощью точечной нотации, браузер предлагает мне, как мы видим на картинке выше, целую кучу методов/свойств, которые я никогда не создавал: toLowerCase, toUpperCase (…).
Это нормально, JavaScript автоматически присвоил моему объекту прототип String, поэтому myObject унаследовал все его свойства и методы.

Можно увидеть прототип объекта, используя следующий синтаксис (в большинстве браузеров):

Синтаксис для доступа к свойству такой же, как и для любого объекта, как показано ранее. Сам String прототип наследуется от другого прототипа, который сам наследуется от другого прототипа, можно двигаться вверх по цепочке, просто сцепив вот так :

myObject.__proto__.__proto__.__proto__

Важным моментом здесь является то, что этот синтаксис служит «геттером» и «сеттером» одновременно: он позволяет получить доступ к свойствам, а также изменять их.

А что, если злоумышленник может изменить свойство прототипа, перезаписав значение существующего свойства, используемого в приложении, другим значением? Он «загрязняет» прототип!
Но, как объяснялось ранее, это не делает его уязвимостью, которую можно использовать немедленно.

Рецепт успешного прототипа загрязнения

Здесь мы удовлетворимся отрывком из PortSwigger, простым, кратким и явным:

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

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

Прототип источника загрязнения — любые входные данные, которые позволяют отравлять объекты-прототипы с произвольными свойствами.

 – Поглотитель — Другими словами, функция JavaScript или элемент DOM, обеспечивающий выполнение произвольного кода.

- Гаджет, который можно использовать — это любое свойство, которое передается в приемник без надлежащей фильтрации или санитарная обработка.

Источник:PortSwigger

Реальный случай с моей находкой

Теперь давайте перейдем к реальному случаю с моей находкой. Поскольку программа частная, я добровольно изменю некоторые детали.

Проведя исследование на www.example.com, я обнаружил несколько уязвимостей (XSS, внедрение Iframe..), которые позволяют мне думать, что вводимые пользователем данные не всегда корректно очищались. После обхода различных векторов, которые могут привести — напрямую — к инъекциям кода HTML/JS, я подумал о том, чтобы проверить, можно ли выполнить прототип загрязнения через URL, если возможно, политику -довольно слабое отношение к фильтрам- наверняка позволит мне сделать что-нибудь интересное.

Я начал с чего-то простого, но меня заблокировал WAF:

?__proto__.zhero=zhero

Я предпринял несколько попыток, но безуспешно, особенно через конструктор, но брандмауэр систематически блокировал мои запросы. Тем не менее, стоит попробовать;

Если ввод не был рекурсивно очищен:

?__pro__proto__to__.zhero=zhero 

Через конструктор (будучи объектом, он также наследует прототип):

constructor[prototype][zhero]=zhero

Не забудьте протестировать в каждом случае два способа доступа к объекту (запись через точку и запись со скобками). Это может иметь особое значение после обхода WAF, в зависимости от того, как URL-адрес и его параметры обрабатываются.

Позже, изучая свою цель в веб-архиве, я обнаружил, что многие URL-адреса содержат символ #, за которым следует полезная информация. Это указывало на то, что это не просто якорь, и если приложение использовало этот символ, это обязательно должно быть учтено функцией, которая управляет URL-адресом и его параметрами. Итак, я пытался:

#__proto__[zhero]=zhero

Запрос проходит без проблем с WAF небольшой тест в консоли, чтобы увидеть, не был ли загрязнен прототип:

Обнадеживающий результат: прототип был загрязнен. После некоторого исследования кода JS ответственной функцией является эта:

Как видите, абсолютно никаких фильтров нет. Входные данные могли быть пропущены через фильтры перед передачей в функцию, но здесь это не так.

Как объяснялось ранее, этого недостаточно для успеха рецепта. Теперь необходимо внедрить полезную нагрузку в свойство, опасно используемое приложением. Глядя на исходный код, пара ключ-значение, ранее введенная через URL, используется как «атрибут-значение» в теге стиля:

Увидев это, не осталось никаких загадок в том, как использовать эксплойт, просто используйте обработчик событий в качестве атрибута и некоторый код JS в качестве значения:

#__proto__[onload]=alert(%22XSS by zhero_%22)

Нам удалось привязать наш прототип загрязнения к XSS. Конечно, можно внедрить любой более сложный JS-код. Встроенная функция оповещения использовалась — как обычно — только для примера.

Все приложение было уязвимо, поэтому эту полезную нагрузку можно было использовать в любой части www.example.com.

Спасибо, что читаете меня, если у вас есть какие-либо вопросы, не стесняйтесь, дайте мне знать. Удачной охоты 🏹

Мой аккаунт в Твиттере: https://twitter.com/blank_cold

Взгляните на мою предыдущую статью под названием:Ошибка бизнес-логики, враг сканеров

https://medium.com/bugbountywriteup/business-logic-flaw-the-enemy-of-scanners-45e96304f55f