Мы обнаружили уязвимости в php скриптах на вашем аккаунте

Через какую дыру взломали сайт?

Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Смотреть фото Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Смотреть картинку Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Картинка про Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Фото Мы обнаружили уязвимости в php скриптах на вашем аккаунтеЕсли сайт взломан, мало удалить с него вирус и загруженный PHP Shell. Нужно еще найти причину, по которой произошел взлом, иначе через день-два на сайте снова будет под бодрую музыку развеваться красивый турецкий иностранный флаг. Чаще всего причина — украденный пароль от FTP, устаревшая версия CMS или плагина к ней, но как найти, что именно было использовано для проникновения?

Имея некоторый опыт в этой сфере (в среднем наша техподдержка занимается поиском причины взлома сайта раз в неделю), мы систематизировали накопившуюся информацию.

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

Зачем взламывают сайты

Алгоритм поиска причины взлома

Как искать следы взлома

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

Иными словами, необходимо искать все необычное/непонятное и заглядывать внутрь всех подозрительных файлов. PHP Shell может выглядеть, например, так:

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

Если ничего не помогает, можно просто поискать все файлы, содержащие закодированное в base64 содержимое, например, так:

Определение времени взлома

Когда файлы найдены, определить время взлома очень просто — достаточно посмотреть время изменения самого раннего файла.

Если подозрительных файлов не найдено, но сайт заражен вирусом, посмотрите дату изменения файлов index.php, index.html или тех, в которых обнаружите вирус. Скорее всего в этом случае сайт взломали, украв пароль от FTP.

Поиск журналов взлома сайта

Теперь самое главное — чтобы эти журналы были в наличии!

Если на сайте только произведен дефейс или добавлен вирус ко всем файлам index.html, скорее всего, сайт взломали через кражу пароля FTP (или, гораздо реже, SSH). Посмотрите журналы подключения по FTP и SSH во время взлома — присутствие в нем неизвестных IP-адресов или большого количества разных IP-адресов, осуществивших успешное подключение к сайту, означает, что пароль украден.

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

Если же на сайте присутствуют PHP Shell или вредоносные скрипты (например, для рассылки спама), скорее всего, сайт взломали через уязвимость в CMS или каком-либо её плагине. В этом случае потребуется проанализировать логи веб-сервера.

Если удалось определить IP-адрес

Определив IP-адрес взломщика, мы производим поиск этого IP-адреса по журналу веб-сервера и видим все действия, которые он совершал. Где-то близко к моменту обращения к PHP Shell будет успешное использование уязвимости сайта.

Если определить IP-адрес не удалось

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

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

Если известно время взлома сайта (а мы его уже знаем), необходимо поискать в журнале веб-сервера все запросы POST, находящиеся близко ко времени взлома. Здесь нет конкретных советов — выглядеть они могут совершенно по-разному, но выглядеть они будут в любом случае необычно. Например, это могут быть запросы, содержащие ‘../../../../’ или длинные запросы, содержащие имена файлов или запросы SQL.

Если ничего не удалось найти

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

И ее расширений! Чаще всего взломы производятся не через ядро системы управления сайтом, а через какой-нибудь устаревший плагин, автор которого давно забросил его разработку.

Ну и, разумеется, смените все пароли, которые имеют какое-либо отношение к сайту.

Источник

Потенциальная уязвимость php-скриптов

Функции fopen, file, include и require могут открывать файлы с других сайтов по протоколам http и ftp. Эта возможность несёт в себе потенциальную уязвимость в php-скриптах, позволяющую использовать сайт как прокси.
Предупреждаю ничего нового в этом материале не будет. Несмотря на впечатляющие возможности для злоумышленника, данная уязвимость — просто комбинация общеизвестных свойств php.

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

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

Уязвимость: Url fopen wrapper

Для увеличения функциональности и упрощения кодирования, разработчики php сделали такую особенность в функциях fopen, file, include и прочих. Если имя файла начинается с http://, сервер выполнит HTTP-запрос, скачает страницу, и запишет в переменную как из обычного файла. Аналогично работают префиксы «ftp://», «php://» (последний предназначен для чтения и записи в stdin, stdout и stderr). Нужно это было для того, чтобы разработчики сайтов не мучались с библиотеками http-запросов и не писали их вручную. Данная опция отключается в настройках php, параметр allow_url_fopen.

CR/LF в HTTP-запросах

Комбинация символов carriage return и line feed в HTTP-запросе разделяет заголовки. Подробно об этом можно почитать в статье Антона Калмыкова « Генерация HTTP-запросов ». Эту комбинацию символов можно передать в GET-запросе в виде «%0D%0A».

Untrusted input

На многих сайтах страницы генерируются скриптом-шаблонизатором. В скрипт перенаправляются все запросы сайта. Из REQUEST_URI берётся имя файла, который надо открыть. Файл считывается, к нему добавляется шаблон с навигацией, шапкой и т.п., и результат выдаётся клиенту.

Нерадивый или неопытный программист запросто может написать открытие файла без проверки данных:

От запроса отбрасывается первый символ — слэш — и открывается файл. Злоумышленник легко может вписать в качестве пути к файлу на сервере строку http://example.com: http://n00b.programmer.com/http://example.com Другой вариант — все адреса на сайте имеют вид http://n00b.programmer.com/index.php?f=news В таком случае злоумышленник будет пробовать открыть адрес типа http://n00b.programmer.com/index.php?f=http://example.com Очень важно не доверять входящим данным и фильтровать при помощи регулярных выражений входящие запросы.

Эксплойт

Поскольку в приведённом примере адрес никак не проверяется, в запрос можно вставить строку с HTTP-запросом. Если злоумышленник откроет путь

то скрипт выполнит HTTP-запрос:

Последние три строки скрипт добавляет автоматически, но два rn перед ними означают конец запроса. Таким образом, незащищённый скрипт можно использовать как прокси-сервер. Зная несколько «дырявых» сайтов, злоумышленник может выстроить из них цепочку, чтобы его было сложнее найти.

Умное использование эксплойта

Если у провайдера, предоставляющего бесплатный демо-доступ, дырявый сайт, можно написать скрипт для домашнего сервера, который бы формировал запросы к такому прокси-серверу и экономил немного денег. Это дело, безусловно, подсудное и наказуемое, но по большому счёту баловство. Более прибыльное использование чужой машины как прокси — рассылка коммерческого спама. Пример из статьи, написанной Ульфом Харнхаммаром :

(должно быть одной строкой) модуль PHP соединится с сервером mail.example.com по 25 порту и отправит следующий запрос:

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

Меры защиты от эксплойта

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

Проверка журнала запросов

Для начала полезно ознакомиться со списком уникальных адресов, запрашиваемых с сайта. Это поможет узнать, были ли случаи атак и использования дырки. Обычно спамеры сразу проверяют возможность соединения с нужным им почтовым релеем по 25 порту. Поэтому искать следует строки «:25» и «%3A25».

Настройка php

Изменение кода

Возможна такая сложная ситуация: вы клиент, а нерадивый админ хостинг-провайдера вписал все установки php в php_admin_value, и поменять их нельзя. Придётся модифицировать код скриптов. Самый простой способ — искать функции fopen, file и include, открывающие файлы из имён переменных. И вырезать функцией str_replace префиксы http:// и ftp://. Впрочем, иногда скрипту, всё-таки, необходимо открывать адреса, которые приходят от пользователя. Например, скрипт-порнолизатор, который вставляет в текст матерки или заменяет текст на ломаный русский язык («трасса для настайащих аццоф, фсем ффтыкать»). Наверное, больше всего от неряшливого программирования пострадали именно эти сайты. В данном случае вполне можно ограничиться вырезанием «rn» из полученной строки. В таком случае злоумышленник не сможет добавить свой собственный заголовок к запросу, который отправляете вы.

Прекращение работы при оффенсивном запросе

При этом предполагается, что на сайте не будут отправляться методом GET формы с многострочным пользовательским вводом. Иначе они будут остановлены этим правилом.

Если вы при помощи mod_rewrite поддерживаете адресацию, удобную для чтения, то скорее всего, двоеточие и CRLF не используются. Поэтому другие строки RewriteRule не будут подходить под сканирующий запрос, и строку, прекращающую обработку запроса, лучше поместить в конце списка правил. Тогда обычные запросы будут переписываться и перенаправляться до этой строки (используйте флаг [L]), что уменьшит время их обаботки. В зависимости от разных условий оно может варьироваться.

Источник

5 главных уязвимостей PHP безопасности

Учитывая тот факт, что взлом некоторых сайтов может принести немалую выгоду, а для того, чтобы его осуществить, преступнику даже не нужно выходить из дома, можно сделать вывод, что взлом сайта – очень заманчивое занятие для преступника. В дополнение к этому, злоумышленники в интернете очень редко несут наказания за свои деяния.Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Смотреть фото Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Смотреть картинку Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Картинка про Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Фото Мы обнаружили уязвимости в php скриптах на вашем аккаунтеНо для того, чтобы взломать ресурс, хакер должен найти уязвимости в его работе. Пяти главным уязвимостям php и будет посвящена данная статья.

SQL Injection

Очень популярным методом взлома сайта среди хакеров является вставка SQL фрагмента вместо URL. В таком случае злоумышленник получает доступ к именам ваших таблиц. Чтобы избежать такого взлома, следует очень внимательно отслеживать все данные, которые присылают вам пользователи. Для борьбы с такими взломами обычно применяют PDO.

Cross site scripting

Так называемый XSS тоже является очень популярным способом взлома. Злоумышленники внедряют опасный код, обычно это JavaScript, в код вашего скрипта PHP. Это становится возможным в случае, когда отображены входные данные пользователей, которые были посланы вам из различных форм, блогов, форумов, чатов и т.д. Если в этих данных содержится вредоносный код, то ваш сайт может быть взломан.

Откровения в исходном коде

Еще одна уязвимость сайтов php – когда злоумышленники, в случае сбоя настроек Apache, могут видеть содержимое и названия ваших файлов. В нормальной ситуации PHP выполняется на стороне сервера, но в случае сбоя код может стать видим обычным пользователям. Этого нельзя допускать, так как прочитав его, злоумышленники могут увидеть доступные файлы или базы данных с важной информацией, такой как, например, учетные данные.

Чтобы не допустить такого взлома, следует более внимательно отнестись к структуре каталогов вашего ресурса. Ни в коем случае не допускайте хранения важных файлов в каталогах, которые доступны всем пользователям, чтобы не допустить возможности, что их увидит кто-то, кроме вас.

Удаленный запуск файлов

Если ваш сайт содержит удаленные файлы, это тоже может стать причиной взлома, если эти файлы не доверенные. Если сайт, на котором хранятся ваши удаленные файлы, был взломан, то их код может быть заменен на вредоносный, который поможет хакерам во взломе вашего сайта. Поэтому нужно очень внимательно относиться к безопасности всех ресурсов, где располагаются ваши файлы.
Если же все-таки взлом стороннего ресурса произошел, то есть довольно простой способ устранить опасность. Для этого вам нужно в php.ini проверить состояние следующих флажков:

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

Угон сессий

Угон случается, когда злоумышленник украл чужой идентификатор сессии и воспользовался им. Этот идентификатор играет роль ключа в защищенном соединении между клиентом и веб-сервером. В PHP этот идентификатор хранится у пользователя. Если ключ был украден, но во время сеанса пользователем не было совершено никаких важных действий, то ничего особо страшного не произойдет. Но если во время сессии пользователь осуществляет, скажем, аутентификацию, это может привести к неприятным последствиям, ведь хакер может завладеть данными для входа в аккаунт. Это особенно важно, если пользователь, совершающий вход, является администратором сайта.

ID сессии очень часто крадут с помощью описанного выше XSS скрипта. Поэтому защита от взлома с помощью XSS убьет сразу двух зайцев, и защитит вас еще и от угона сессий. Хорошим способом защиты именно от угона идентификаторов сессии является их регулярная смена. И чем чаще, тем лучше. Для того, чтобы сменить идентификатор в php вам нужно воспользоваться session_regenerate_id.

В случае, если вы используете PHP 5.2 или выше, то для защиты от угона идентификатора вы можете воспользоваться таким функциями, как:
session.cookie.httpolny и session_set_cookie_parms.

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

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

Источник

Две уязвимости в PHP, взлом Pornhub и вознаграждение 22.000 долларов

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

Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Смотреть фото Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Смотреть картинку Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Картинка про Мы обнаружили уязвимости в php скриптах на вашем аккаунте. Фото Мы обнаружили уязвимости в php скриптах на вашем аккаунте

Автор: Руслан Хабалов

Предисловие

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

Благодарности

Этот проект был реализован силами Дарио Вейбера (Dario Weißer, @haxonaut), cutz и Руслана Хабалова (@evonide). Выражаем особую благодарность cutz за помощь в соавторстве данной статьи.

Введение

Наше внимание привлекла программа по поиску дыр от сервиса Pornhub, опубликованная в Hackerone, и относительно высокое вознаграждение. Основной задачей было углубиться внутрь системы настолько, насколько это возможно и найти уязвимости, позволяющие удаленно выполнить код. В процессе поиска брешей в PHP, на котором работает Pornhub, были задействованы максимально возможные средства.

Обнаружение уязвимости

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

Во всех случаях при получении данных из POST-запроса параметр «cookie» был несериализованным и впоследствии отражался через заголовки Set-Cookie. Пример запроса:
POST /album_upload/create HTTP/1.1
.
tags=xyz&title=xyz. &cookie=a:1:

Response Header:
Set-Cookie: 0=1337; expires

Сей факт впоследствии подтверждался посредством отсылки специально сформированного массива, содержащего объект:
tags=xyz&title=xyz. &cookie=a:1:>
Ответ на отосланный запрос:
0=exception ‘Exception’ in /path/to/a/file.php:1337
Stack trace:
#0 /path/to/a/file.php(1337): unserialize(‘a:1: #1

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

Стандартные техники эксплуатации подобных уязвимостей требуют использования так называемого Property-Oriented-Programming (POP), который связан со злоупотреблением уже существующих классов со специальными «магическими методами» для того, чтобы выполнить вредоносный код. В нашем случае было довольно сложно получить информацию о любых фреймворках и объектах в целом, используемых в Pornhub. Было безуспешно протестировано множество классов из самых распространенных фреймворков.

Описание уязвимости

Главный десерализатор в PHP 5.6 относительно сложен и содержит более 1200 строк кода. Кроме того, многие внутренние PHP-классы имеют собственные методы, предназначенные для десериализации. Из-за поддержки различных сущностей, включая объекты, массивы, целые числа, строки и даже ссылки, неудивительно, что в PHP столько уязвимостей, связанных с нарушением целостности памяти. К сожалению, информация о подобных брешах для новых версий (PHP 5.6 или PHP 7) отсутствовала, поскольку ранее к десериализации уже было приковано особое внимание (см. phpcodz). Таким образом, процесс аудита был похож на выжимание уже хорошо выжатого лимона. После столь большого внимания к данной области и патчей, исправляющих потенциальные уязвимости, все должно быть безопасно, не правда ли?

Стресс-тест функции unserialize

Чтобы выяснить, настолько ли все безопасно и безоблачно, Дарио написал специальный фаззер (средство, позволяющее автоматически тестировать код) для обработки сериализованных строк, передаваемых в функцию unserialize. Запуск фаззера в PHP 7 тут же привел к недокументированному поведению. Однако этот трюк не удалось воспроизвести в Pornhum, и мы решили, что там используется PHP 5.

При запуске фаззера на платформе PHP 5 мы получили логи размером более 1 ТБ, но без какого-либо успеха. Последующее тестирование заставило нас вновь задуматься о ранее выявленном недокументированном поведении. Необходимо было ответить на несколько вопросов: относилась ли эта проблема к сфере безопасности? Можно ли было эксплуатировать эту брешь только локально или удаленно тоже? Для дополнительного усложнения фаззер начал генерировать непечатаемые блобы данных размером более 200 Кб.

Анализ недокументированного поведения

Большая часть времени требовалась для анализа потенциальных проблем. В конце концов, нам удалось получить концепцию рабочей уязвимости, связанной с нарушением целостности памяти – бреши типа use-after-free! При последующем исследовании выяснилось, что причины кроются в алгоритме сбора мусора, компоненте в PHP, который не имеет никакого отношения к функции unserialize. Однако взаимодействие обоих компонентов возникало после того, как функция unserialize завершала свою работу, что не очень подходит для удаленной эксплуатации. Последующий анализ помог глубже понять проблему и найти другие похожие уязвимости, которые были уже более подходящими для удаленной эксплуатации.

Ссылки на описания брешей:

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

Эксплуатация уязвимостей

Несмотря на то, что найденные бреши типа use-after-free выглядели привлекательно, методы эксплуатации оказались непростыми, и весь процесс подразделялся на несколько стадий. Поскольку главной целью было выполнение произвольного кода, необходимо было тем или иным образом скомпрометировать указатель инструкции процессора под названием RIP (на платформе x86_64). При решении данной задачи возникают следующие препятствия:

Первая проблема решается при помощи возвратно-ориентированного программирования (Return-oriented programming, ROP), где вы можете использовать уже существующие и выполняемые участки памяти из бинарного файла или библиотек. Вторая проблема решается поиском правильного адреса функции zend_eval_string. Обычно при запуске динамически линкованной программы загрузчик помещает процесс по адресу 0x400000, который является стандартным адресом загрузки на платформе x86_64. В случае если вы уже каким-то образом получили исполняемый файл интерпретатора PHP (например, нашли пакет, собранный на целевой машине), то можете локально поискать смещение к любой нужной функции. Мы обнаружили, что в Pornhub используется нестандартная версия php5-cgi, что осложняет определение версии PHP и получение любой другой информации о структуре памяти процесса интерпретатора.

Получение бинарного файла PHP и необходимых указателей

Уязвимостей типа use-after-free в PHP обычно эксплуатируются по схожим правилам. Как только вы смогли заполнить освобожденную память, которая впоследствии многократно используется как внутренняя PHP-переменная (так называемые zval’ы), вы можете генерировать вектора, позволяющие считывать информацию из любого участка памяти и выполнять код.

Подготовка к раскрытию памяти

Как было упомянуто ранее, нам необходимо было получить больше информации относительно бинарного файла PHP, используемого в Pornhub. Таким образом, первый шаг – использование уязвимости use-after-free для инжектирования zval’а, представляющего PHP-строку. Структура zval в PHP 5.6 выглядит следующим образом:

«Zend/zend.h»
[. ]
struct _zval_struct <
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
>;

Ввиду того, что поле zvalue_value определено как union, фальсификация и компрометирование типов значительно упрощается.

«Zend/zend.h»
[. ]
typedef union _zvalue_value <
long lval; /* long value */
double dval; /* double value */
struct <
char *val;
int len;
> str;
HashTable *ht; /* hash table value */
zend_object_value obj;
zend_ast *ast;
> zvalue_value;

Строковая PHP-переменная соответствует zval с типом 6. Следовательно, тип union рассматривается как структура, содержащая указатель на первый символ и длину поля. Таким образом, если мы смастерим строковой zval с произвольным указателем и длинной поля, то сможем получить нужную информацию в заголовке ответа при отражении функцией setcookie() инжектированного zval’а.

Поиск адреса загрузки интерпретатора PHP (image base)

Обычно все начинается с получения бинарного файла, который, как было сказано ранее, начинается с адреса 0x400000. К сожалению, на сервере Pornhub используются защиты наподобие PIE и ASLR, рандомизирующие адрес загрузки процесса и разделяемых библиотек. Подобная ситуация становится все более распространенной, поскольку большинство пакетов распространяются с позиционно независимым кодом. То есть следующая задача – найти корректный адрес загрузки бинарного файла.

Первая сложность – каким-либо образом получить хотя бы один корректный адрес, откуда можно начать копирование бинарного файла. И здесь полезно предварительно ознакомиться с тем, как устроена внутренняя память в PHP. В частности, при освобождении zval из памяти далее PHP перезаписывает первые восемь байт адресом на предыдущую освобожденную область данных. Таким образом, для того чтобы получить первый корректный адрес, нам нужно создать целочисленный zval, освободить созданный zval и затем использовать висячий указатель на этот zval для получения текущего значения.

Поскольку в php-cgi используется множество процессов, ответвленных от главного процесса, схема памяти не изменяется между различными запросами при условии, что мы отсылаем данные одного и того же размера. Мы можем отсылать запрос за запросом и каждый раз копировать различные порции памяти посредством изменения начального адреса поддельной строки zval. Однако получения адреса освобожденной кучи самого по себе не достаточно, чтобы получить какие-либо зацепки за месторасположение области выполнения бинарного файла. Подобное происходит потому, что вокруг освобожденной области отсутствует достаточное количество полезной информации.

Для получения интересующих нас адресов существует сложная техника, где используется множественные освобождения и размещения PHP-структур во время процесса десериализации (см. презентацию ROP in PHP applications, слайд 67). Из-за специфики нашего случая и желания упростить задачу настолько, насколько возможно, мы решили пойти другим путем.

При помощи сериализованной строки наподобие «i:0;a:0:<>i:0;a:0:<>[…]i:0;a:0:<>» как части конечной несериализованной полезной нагрузки мы можем воспользоваться функцией unserialize для создания множества пустых массивов и впоследствии освободить созданные объекты после завершения работы unserialize. При инициализации массива PHP последовательно выделяет память для zval и hashtable. Одна стандартная запись в hashtable для пустых массивов – символ uninitialized_bucket. В итоге мы смогли получить фрагмент памяти, который выглядит примерно так:

0x7ffff7fc2fe0: 0x0000000000000000 0x0000000000eae040
[. ]
0x7ffff7fc3010: 0x00007ffff7fc2b40 0x0000000000000000
0x7ffff7fc3020: 0x0000000100000000 0x0000000000000000
0x7ffff7fc3030: # : 0x0000000000000000

0xeae040 – адрес символа uninitialized_bucket ивтожевремяпрямойуказательнаBSS-сегмент. Вы можете видеть, что подобное происходит несколько раз по соседству с последней освобожденной областью. Как было сказано ранее, многие пустые массивы были освобождены. Таким образом, пользуясь тем, что некоторые записи hashtable остаются неизменными внутри кучи, мы смогли получить этот специальный символ. И, наконец, нам удалось выполнить постраничное обратное сканирование, начиная с адреса символа uninitialized_bucket, для нахождения заголовка ELF.

Получение интересных сегментов бинарного файла PHP

На данный момент ситуация осложняется тем, что мы научились получать лишь 1 КБ данных в один запрос (из-за ограничений размера заголовка на сервере Pornhub). Бинарный файл PHP может занимать до 30 МБ. Если выполнять один запрос в секунду, то время получения полного файла может занимать до 8 часов 20 минут. Поскольку мы боялись, что процесс эксплуатации уязвимости может быть прерван в любой момент, необходимо было действовать как можно быстрее и незаметнее. Нам потребовалось реализовать эвристические алгоритмы для заблаговременной фильтрации интересных секций. Тем не менее, мы могли разложить любую структуру, на которую была ссылка внутри ELF-строки и таблицы символов. Существуют другие техники наподобие ret2dlresolve, позволяющие пропустить весь процесс получения, но в нашем случае данные методы были не совсем подходящими, поскольку требовали создания дополнительных структур данных и знания относительно различных участков памяти.

Чтобы получить адрес функции zend_eval_string, вначале необходимо найти программные заголовки формата ELF, которые находятся по смещению 32. Затем нужно просканировать вперед до тех пор, пока не найдется программный заголовок с типом 2 (PT_DYNAMIC), для того, чтобы получить динамическую секцию ELF. Данная секция содержит ссылку на строку и таблицу символов (тип 5 и 6), которые вы можете полностью выгрузить, используя размер поля вместе с любой функцией, чей виртуальный адрес вы желаете получить. Кроме того, вы также можете использовать hashtable (DT_HASH) для более быстрого нахождения функций, но этот метод уже не столь интересен, поскольку вы можете работать с таблицами локально. В дополнении к функции zend_eval_string нам интересны символы и местонахождение POST-переменных (поскольку предполагается, что эти переменные далее будут использоваться в качестве ROP-стека).

Получение адреса POST-данных

Чтобы получить адрес передаваемых POST-данных, необходимо найти указатели при помощи следующей цепочки:

Подготовка полезной нагрузки для ROP

Вторая часть связана с получением управления над процессом PHP и перехватом потока выполнения кода. Для решения этой задачи вначале необходимо разобраться, как модифицировать указатель инструкции.

Перехват указателя инструкции

Мы добавили в нашу полезную нагрузку фальшивый объект (вместо ранее использованной строки zval) с указателем на специально сформированную таблицу zend_object_handlers. По сути, данная таблица представляет собой массив указателей на функции, определение структуры которой можно найти здесь:

«Zend/zend_object_handlers.h»
[. ]
struct _zend_object_handlers <
zend_object_add_ref_t add_ref;
[. ]
>;

При создании подобной фальшивой таблицы zend_object_handlers, необходимо установить параметр add_ref. Функция, стоящая за данным указателем, управляет увеличением счетчика ссылок объекта. После создания поддельного объекта и передачи этого объекта в качестве параметра в функцию «setcookie» произойдет следующее:

#0 _zval_copy_ctor
#1 0x0000000000881d01 in parse_arg_object_to_string
[. ]
#5 0x00000000008845ca in zend_parse_parameters (num_args=2, type_spec=0xd24e46 «s|slssbb»)
#6 0x0000000000748ad5 in zif_setcookie
[. ]
#14 0x000000000093e492 in main

У setcookie должен быть хотя бы один обязательный параметр. Мы передаем наш объект в качестве второго (необязательного) параметра, который функция будет пытаться преобразовать в строку. Затем выполняется функция zval_copy_ctor:

«Zend/zend_variables.c»
[. ]
ZEND_API void _zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC)
<
[. ]
case IS_OBJECT:
<
TSRMLS_FETCH();
Z_OBJ_HT_P(zvalue)->add_ref(zvalue TSRMLS_CC);
[. ]
>

Здесь происходит вызов функции, на которую указывает add_ref, вместе с адресом нашего объекта, используемого в качестве параметра (более подробно см. PHP Internals Book – Copying zvals). Соответствующий ассемблерный код выглядит так:

: mov 0x8(%rdi),%rax
: callq *(%rax)

В коде, приведенном выше, RDI выступает в качестве первого аргумента в функции _zval_copy_ctor_func и в то же время является адресом на наш поддельный объект zval (или zvalue в исходном коде выше). Внутри определения _zvalue_value(см. выше) есть элемент obj с типом zend_object_value, который определен так:

«Zend/zend_types.h»
[. ]
typedef struct _zend_object_value <
zend_object_handle handle;
const zend_object_handlers *handlers;
> zend_object_value;

Следовательно, 0x8(%rdi) будет указывать на вторую запись внутри _zend_object_value, которая соответствует адресу первой записи внутри zend_object_handlers. Как было упомянуто ранее, данная запись представляет собой функцию, на которую указывает add_ref, что также объясняет, зачем мы напрямую управляем регистром RAX.

Чтобы обойти проблему, связанную с невыполняемой памятью, нам потребовалось собрать дополнительную информацию. В частности, нужно было собрать полезные гаджеты и подготовить подмену стека (stack pivoting) для ROP-цепи, поскольку к тому момент стек не был под нашим контролем.

Поиск ROP-гаджетов

На данный момент мы можем установить параметр add_ref (или, соответственно, регистр RAX) для контроля над указателем инструкции. Хотя сей факт закладывает хорошую основу, но не гарантирует запуска всех ROP-гаджетов, поскольку после выполнения первого гаджета процессор берет из текущего стека адрес следующей инструкции. Поскольку мы не контролируем стек в достаточной степени, необходима подмена стека на нашу ROP-цепь. Таким образом, следующий шаг – копирование RAX в RSP и последующее выполнение ROP-цепи оттуда. Используя локально скомпилированную версию PHP, мы поискали гаджеты, подходящие для подмены стека, и обнаружили, что функция hp_stream_bucket_split содержит следующий участок кода:

: push %rax # Больше пяти не собираться: роботы будут следить за улицами Сингапура, хакеры атаковали проект Jenkins, во Франции арестовали экологов, данные которых раскрыл ProtonMail, а россиян беспокоит идея «социальных рейтингов». Смотрите 31-й выпуск наших новостей.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *