February 22

Dirty JavaScript — Prototype Pollution

Сегодня я расскажу про такую уязвимость, как загрязнение прототипа. Мы ознакомимся с практическим примером применения этой уязвимости в лабораторной, а также рассмотрим некоторые утилиты для её поиска.

Перед тем, как мы приступим к поиску уязвимостей загрязнения прототипа, очень важно разобраться, как работает данная уязвимость и причём тут объекты JavaScript.

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

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

Теория

Для начала зайдём в консоль нашего браузера, в которой можно исполнить любой JS, и познакомимся с тем, что такое объект.

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

Объект в JS представляет собой комбинацию ключа и значения. Это очень похоже на словари в Python или какой-нибудь JSON.

Инициализируем переменную user:

var user = {username:"wr3dmast3r", password:"qwerty"}

Поскольку мы просто сохранили значения этой переменной в ключ-значениях, мы можем обратиться к ним с помощью конструкции <object>.<property>, например:

user.username

Таким образом мы получаем доступ к конкретному свойству объекта.

Создадим еще одну переменную и добавим к ней свойство isAdmin:

var admin = {username:"admin",password:"admin",isAdmin:true}

Как можно заметить, теперь можно обратиться к полю isAdmin и убедиться, что пользователь является администратором:

Создадим ещё одного пользователя:

var user = {username:"cherepawwka",password:"qweqwe"}

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

Предположим, что есть приложение, которое на самом деле проверяет какой пользователь имеет права администратора проверяя конкретное значение isAdmin.

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

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

Попробую показать на примере.

Обратимся к объекту user:

Мы видим, что в данном объекте существует только два параметра, и пользователь не является администратором

У администратора в данном случае существует дополнительный параметр:

proto

Загрязним прототип объекта user с помощью __proto__:

user.__proto__.isAdmin = true

Как мы видим, в ответе фигурирует true, но если мы снова посмотрим на объект user, то заметим, что ничего не изменилось:

Но если мы обратимся к свойству isAdmin данного объекта напрямую, то увидим наше переданное ранее значение:

И если раньше мы получали при таком запросе неопределенное значение, то теперь мы видим, что параметр имеет значение true. Это происходит потому, что мы добавили значение true свойства isAdmin в прототип, а когда вы добавляете свойство в прототип объекта JS, для другого объекта такого же класса, у которого это свойство не определено пользователем, JS будет пытаться найти его в свойстве __proto__.

Если мы подробнее посмотрим на объект user, то увидим, откуда берется параметр isAdmin для этого пользователя:

На скриншоте вы можете увидеть, что у нас есть дополнительное свойство, которое мы добавили внутрь прототипа.

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

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

Именно поэтому данная уязвимость называется "загрязнение прототипа", потому что мы модифицируем или искажаем значение предопределенных прототипов.

Практика

Перейдем к практике и рассмотрим уязвимое приложение. На главной странице в лабораторной существуют несколько разделов — контакты, главная страница и продукты.

Можно заметить, что каждая из страниц открывается под уникальным идентификаторов в параметре page:

Первое, на что стоит обратить внимание при поиске уязвимости, — это JS-скрипты, упомянутые в коде страницы.

Если мы проверим xss 0.3.3, то обнаружим, что существует известная уязвимость, которая приводит к DoS, что нам не подходит:

Для jquery 3.5.1 есть потенциальный эксплойт в репозитории @blackfan. В нем можно найти много полезной информации о Prototype Pollution. Если вдруг вы когда-нибудь столкнетесь с этой уязвимостью, данный репозиторий— первое, что необходимо посмотреть:

Если посмотреть на уязвимости jQuery.query версии 2.2.3, то можно найти упоминание уязвимости, связанной с Prototype Pollution:

Имеющиеся компоненты не предоставляют возможности для эксплуатации уязвимости, поэтому стоит осмотреться ещё раз и обратить внимание на упущенные моменты.

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

Скрипт начинается с создания страниц объекта, а в переменной pages находятся 4 страницы с какой-то HTML-разметкой. Дальше в коде можно увидеть создание переменной pl, которая берет из GET-запроса значение page. Затем идет проверка существования этой страницы, и, если она существуют, то выдается соответствующее HTML-содержимое, иначе нас перенаправляет на первую страницу из списка ("?page=1").

Если вы раньше изучали DOM XSS, то одна из вещей, на которую стоит обратить внимание, — это использование innerHTML, которое потенциально может привести к уязвимости.

Посмотрим на код внимательнее.

1. Если мы вводим цифру 4, то нам выдается статический HTML-код, который представляет собой верхнюю панель на странице.

2. Вторым параметром является pages[pl], где pl — это GET-параметр, то есть тот ввод, который мы контролируем.

Но даже если бы мы смогли что-то ввести в данный JS, добавленный фильтр помешал бы эксплуатации XSS.

Вернемся к коду на странице:

Мы можем попробовать добавить новую страницу или изменить уже существующую, чтобы разместить свою полезную нагрузку.

Exploit

Попробуем загрязнить прототип с помощью полезной нагрузки и вызовем страницу:

__proto__[5]=payload&page=5

Добавим полезную нагрузку для исполнения уязвимости:

__proto__[5]=<img+src%3dx+onerror%3dalert(document.domain)>&page=5

Полезная нагрузка обрезается из-за фильтрации, последним шагом остается обойти её:

На этом этапе стоит вспомнить о ещё одном полезном свойстве прототипов — гаджетах, которые можно найти в разных библиотеках.

Передаю ещё раз привет моему кумиру @blackfan, и обращаюсь к его репозиторию

В описанном способе должно соблюдаться условие (должна быть установлена определенная настройка):

options.whiteList = options.whiteList || DEFAULT.whiteList;

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

Модернизируем полезную нагрузку:

__proto__[5]=<img+src=x+onerror%3Dalert(document.domain)>&__proto__[whiteList][img][0]=onerror&page=5

Сталкиваемся с тем, что она не отрабатывает:

Методом проб и ошибок удалось найти рабочий тэг и обработчик событий:

__proto__[5]=<style+onload%3Dalert(document.domain)>&__proto__[whiteList][style][0]=onload&page=5

И таким образом удается обойти валидацию и исполнить скрипт:

Инструменты

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

DOM Invader

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

Подробнее:

PortSwigger: DOM Invader

HackTricks: DOM Invader

Prototype Pollution Scanner

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

Github: pphack

github BlackFan

И не забываем про отличный репозиторий, с которым я настоятельно рекомендую ознакомиться при эксплуатации Prototype Pollution. На Payloads all the Things вы этого не найдете :)