Райтап писали: tungue, Dana Scully
Сегодня мы делаем разбор level 04 с сайта websec.fr .
Сайт предоставляет таски по веб-пентесту, и на эти таски написано не очень много райтапов (в основном, они все на китайском\корейском\японском языках).
Я когда-то начинала разбирать, но потом дело, к сожалению, сошло на нет.
Перед разбором сначала расскажем о том, что такое php injection и за счет чего мы можем эксплуатировать данную атаку.
Инъекции очень часто используют недостатки написанного кода, и здесь как раз это и происходит.
Согласно OWASP, уязвимость возникает, когда введенные пользователем данные не обрабатываются должным образом перед передачей в PHP-функцию unserialize(). В теории, сериализация — это преобразование объектов в битовую последовательность, из которой можно потом восстановить почти такой же объект с помощью десериализации. Поскольку PHP допускает сериализацию объектов, мы можем передавать специальные сериализованные строки уязвимому вызову unserialize(), что приводит к внедрению произвольных объектов PHP в область приложения.
Для успешного использования уязвимости должны быть выполнены два условия:
Приложение должно иметь класс, реализующий магический метод PHP (такой как __wakeup или __destruct), который можно использовать для выполнения вредоносных атак или запуска «POP chain».
Все классы, используемые во время атаки, должны быть объявлены при вызове уязвимого unserialize(), в противном случае для таких классов должна поддерживаться автозагрузка объектов.
Как правило, любая уязвимость внедрения кода состоит из неправильной проверки ввода и динамической и небезопасной оценки ввода пользователя.
Пользовательский ввод включает в себя любые данные, обработанные приложением и обработанные или введенные пользователями приложения. Он охватывает поля формы прямого ввода и загрузку файлов, а также другие источники данных, такие как параметры строки запроса и файлы cookie.
Вообще php инъекции бывают разные, но у нас будет пример эксплуатации уязвимости, связанной с магическим методом __destruct и сериализацией куки.
Задача таска заключается в том, чтобы с помощью инъекции и десериализации достать флаг из базы данных.
Таск встречает нас начальной страницей с областью для ввода.

И реакция будет только на ввод 1. Тогда мы увидим:

В таске еще представлены 2 файла с исходным кодом, давайте разберем.

После анализа первого файла можно сделать вывод, что веб-сайт проверяет, указано ли значение leet_hax0r в файле cookie, и, если да, он будет десериализован, в ином случае он установит файл cookie для пользователя.
serialize в PHP сериализирует, а deserialize — десериализирует. Поскольку на выходе мы получаем не строку, а просто набор байтов, то мы его на всякий случай оборачиваем в base64.
В итоге в $cookie получается сериализированная версия ассоциативного массива. А потом мы кладём $cookie в куку leet_hax0r на 30 дней, откуда мы её можем восстановить. Здесь мы проверяем $_REQUEST на наличие поля id. А ещё проверяется с помощью is_numeric что переданный нами id — это число или числовая строка. У нас id это "0" , напомню, так что проверка проходит успешно.
Ну, а потом внутри if мы склеиваем наш id и query . Для нашего нуля в итоге в $sql->query будет лежать 'SELECT username FROM users WHERE id=0' . Логичный и понятный запрос.
На самом деле, код выше далек от идеала, потому что там, например, не проверяется являются ли сравниваемые айпишники одинаковым типом. Это, конечно, вряд ли можно использовать как вектор атаки, но тем не менее.

Далее объект класса SQL в переменной $SQL умер, а значит... срабатывает функция __destruct , которая выполняет запрос из $sql->query . В результате мы получаем юзернейм для пользователя с id=0 .
Очевидно, что если мы запихнем в id какой-то запрос, например id=select username from users , то он не пройдёт проверку is_numeric и ни на что не повлияет.
Когда мы поняли логику работы скрипта и безнадёжность взлома через id , у нас остаётся только одно... Куки.
Для выполнения инъекции использовали Burp Suite.
Вектор атаки:
Мы изменим значение $query на запрос, который хотим получить из базы данных, эксплуатируя unserialize.
То есть, когда функция unserialize активирована, она извлекает из нее все значения, а затем присваивает их текущей переменной. Внедрение путем создания объекта, затем хеширование его в base64 и назначение его в файл Cookie.
Полезная нагрузка, которую мы использовали, выглядит так:
O:3:"SQL":1:{s:5:"query";s:49:"SELECT password as username FROM users WHERE id=1";}
Примечание:
O - объект или класс
s - строка.
Как мы определили формат полезной нагрузки?
Изначально мы декодировали перехваченный куки, чтобы понять, в каком формате писать запрос для инъекции. Саму инъекцию производим через Repeater. В разделе Proxy выбираем запрос с методом GET. В куки после leet_hax0r=, вставляем наш код, выделяем его и выбираем Convert selection -> Base64-encode. После этого запрос приобретает нужную нам кодировку, после чего его можно отправлять.
Это выглядит так:

Ответ после выполнения запроса:

Ура, флаг найден :)