Удаленное удаление. Как захватить контроль над WordPress, заставив его стереть файл
Содержание статьи
Баг был обнаружен еще 20 ноября 2017 года исследователем Славко Михайльоски (Slavco Mihajloski) из RIPS Tech, но вплоть до версии 4.9.7, которая вышла 5 июня 2018 года, проблема оставалась незапатченной. То есть на протяжении семи месяцев она представляла серьезную угрозу для безопасности на огромном количестве сайтов по всему миру и на многих из них продолжает представлять.
Стенд
Чтобы разобрать уязвимость, нам сначала понадобится уязвимый WordPress. Первым делом ставим контейнер с базой данных.
$ docker run -d --rm -e MYSQL_USER="wpdel" -e MYSQL_PASSWORD="4hicmMRyUq" -e MYSQL_DATABASE="wpdel" --name=mysql --hostname=mysql mysql/mysql-server
Теперь контейнер, на который поставим веб-сервер. На нем будет располагаться WordPress.
$ docker run -it --rm -p80:80 --name=wpdel --hostname=wpdel --link=mysql debian /bin/bash
Установим все нужные пакеты и расширения PHP.
$ apt-get update && apt-get install -y apache2 php php7.0-mysqli php-gd nano wget
Уязвима версия CMS под номером 4.9.6, ее и скачаем.
$ cd /tmp && wget https://wordpress.org/wordpress-4.9.6.tar.gz
$ tar xzf wordpress-4.9.6.tar.gz
$ rm -rf /var/www/html/* && mv wordpress/* /var/www/html/
$ chown -R www-data:www-data /var/www/html/
Запускаем сервис apache2.
$ service apache2 start
Теперь устанавливаем WordPress, используем наш MySQL-сервер в качестве БД.

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

Анализ уязвимости
В WordPress, как и в любой уважающей себя CMS, можно загружать произвольные файлы и встраивать их в публикуемые посты. Самый очевидный способ применения — это добавление фотографий. Если файл загруженной картинки больше установленных в настройках размеров, то для него создаются миниатюры (thumbnails). Самую мелкую из них можно увидеть при нажатии на кнопку редактирования картинки (в закладке медиа в разделе Thumbnail Settings).

Заглянем в файл post.php, где находится кусок кода, ответственный за редактирование любой записи в системе. Да, аттачи тоже считаются записями.
/wordpress/wp-admin/post.php
33: if ( $post_id )
34: $post = get_post( $post_id );
...
62: switch($action) {
В переменной $action находится действие из запроса, которое нужно выполнить. Оператор switch перенаправляет выполнение скрипта в нужную часть кода. Вот, к примеру, запрос на редактирование конкретной записи.
GET /wp-admin/post.php?post=25&action=edit HTTP/1.1
Host: wpdel.visualhack
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7,bg;q=0.6,uk;q=0.5,hu;q=0.4
При его обработке исполняется следующий кусок кода:
062: switch($action) {
...
098: case 'edit':
099: $editing = true;
...
122: if ( ! empty( $_GET['get-post-lock'] ) ) {
123: check_admin_referer( 'lock-post_' . $post_id );
124: wp_set_post_lock( $post_id );
125: wp_redirect( get_edit_post_link( $post_id, 'url' ) );
126: exit();
127: }
128:
129: $post_type = $post->post_type;
130: if ( 'post' == $post_type ) {
131: $parent_file = "edit.php";
132: $submenu_file = "edit.php";
133: $post_new_file = "post-new.php";
134: } elseif ( 'attachment' == $post_type ) {
135: $parent_file = 'upload.php';
136: $submenu_file = 'upload.php';
137: $post_new_file = 'media-new.php';
138: } else {
139: if ( isset( $post_type_object ) && $post_type_object->show_in_menu && $post_type_object->show_in_menu !== true )
140: $parent_file = $post_type_object->show_in_menu;
141: else
142: $parent_file = "edit.php?post_type=$post_type";
143: $submenu_file = "edit.php?post_type=$post_type";
144: $post_new_file = "post-new.php?post_type=$post_type";
145: }
146:
...
174: include( ABSPATH . 'wp-admin/edit-form-advanced.php' );
175:
176: break;
Погуляв немного по этой ветке кода, можно обнаружить довольно любопытный экшен.
178: case 'editattachment':
179: check_admin_referer('update-post_' . $post_id);
180:
181: // Don’t let these be changed
182: unset($_POST['guid']);
183: $_POST['post_type'] = 'attachment';
184:
185: // Update the thumbnail filename
186: $newmeta = wp_get_attachment_metadata( $post_id, true );
187: $newmeta['thumb'] = $_POST['thumb'];
188:
189: wp_update_attachment_metadata( $post_id, $newmeta );
Особенно интересен раздел Update the thumbnail filename. Переменная $newmeta содержит метаданные записи с указанным ID. Ключ thumb содержит путь до миниатюры, и его значение можно изменить с помощью параметра thumb в POST-запросе. Эти данные уходят в функцию wp_update_attachment_metadata, которая расположилась чуть ниже в этом же файле.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»




Подпишись на наc в Telegram!
Только важные новости и лучшие статьи
Подписаться