Содержание статьи
Идея метода для обхода фильтров кода на PHP возникла у меня еще давно, но подсказка подвернулась недавно. Примерно год назад, читая статью про очередные уязвимости в образовательной платформе Moodle, я наткнулся на статью, в которой исследователям пришлось придумать, как выполнить код без квадратных скобок и текста, используя только математические формулы. У метода, предложенного авторами оригинальной статьи, были ограничения, поэтому я немного изменил подход и доработал его.
Давай сначала разберем оригинальный метод, а дальше я покажу свою доработку.
www
Оригинальный метод опубликован его авторами в их репозитории на GitHub и относится к уязвимости CVE-2024-43425.
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Исходный метод
Сразу оговорюсь, что этот метод был создан специально для эксплуатации Moodle, имеет ряд недостатков и ограничений и не рассчитан на более гибкое применение где‑то еще. Моя доработка, которую я покажу чуть позже, тоже неидеальна, но значительно расширяет возможности.
Давай формализуем задачу: нужно добиться RCE, используя только символы, которые встречаются в формулах. Это автоматически запрещает квадратные скобки и подобные хаки, которые обычно применяют в таких случаях.
Кажется, будто без спецсимволов получить выполнение кода невозможно, но для интерпретируемых языков это не совсем так. В PHP есть возможность задать имя функции в виде выражения, причем любого. Понять, как это работает, можно на простом примере:
php > print(base64_encode("printf"));cHJpbnRm
php > base64_decode("cHJpbnRm")("hello");hello
Как видно из примера, имя функции printf
кодируется в Base64, а если раскодировать его обратно и вызвать строку с аргументом, только что раскодированная строка будет интерпретирована как функция, которая тут же будет выполнена. Для вызова функции можно использовать любой способ получения строки, а интерпретатор PHP сам найдет и выполнит нужную функцию.
Здесь важно, что это должна быть именно функция, то есть eval
или print
таким способом вызвать не получится, потому что это специальные языковые конструкции, а не функции. Такие конструкции PHP обрабатывает перед собственно выполнением кода. Если попробовать провернуть такой трюк с eval
, то получим ошибку:
php > print(base64_encode("eval"));ZXZhbA==php > base64_decode("ZXZhbA==")("phpinfo();");PHP Warning: Uncaught Error: Call to undefined function eval() in php shell code:1Stack trace:#0 {main} thrown in php shell code on line 1php >
Почитать про другие конструкции языка можно в статье Махафузура Рахамана, а сейчас нам нужно просто усвоить, что вызывать таким способом допустимо только функции.
Плавно перейдем ко второй задаче — избавлению от заметных и очевидно нематематических функций вроде base64_decode
. По условию задачи использовать можно только математические функции, а, как известно, при математических вычислениях обычно получаются числа.
Второй трюк заключается в том, чтобы после операций с числами получить строку. Для этого в PHP используется оператор .
, который может быть как десятичной точкой в числе, так и оператором конкатенации. Чтобы извлечь из числа строку, нужно сначала получить любую другую строку. В оригинальной статье авторы используют acos(
, который даст NAN
, так как это выражение невозможно вычислить. Давай попробуем:
php > print(gettype(acos(2)));double
Получился double
, а нужно string
. Самый простой способ — конкатенация. При конкатенации чего угодно даже с пустой строкой получится строка, а мы сложим два числа‑строки NAN
:
php > print(gettype(acos(2)));double
php > print(gettype(acos(2) . acos(2)));string
php > print(acos(2) . acos(2));NANNAN
При конкатенации двух acos(
получается строка NANNAN
. Дальше можно собрать желаемую строку, используя операцию XOR:
(acos(2) . 1) ^ (0 . 0 . 0) ^ (1 . 1 . 1)
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее