author页面地址为 http://yoursite/?author=1 ID是自增的
请求这个地址会 301 到一个url,这个url里包含了做者的用户名。虽然不算漏洞,仍是给了爆破者很大的便利。
301指向的url : .../author/你的后台登陆用户名php
将下面的代码添加到当前主题的 functions.php 文件:
html
function my_author_link() { return home_url('/' ); } add_filter('author_link','my_author_link');
在 .htaccess 文件里加入
nginx
redirect 301 /(这里还有path的请自行添加)/author/你的后台登陆用户名/ http://yoursite/
上面这段代码意思为:
将 http://yoursite/.../author/你的后台登陆用户名/
跳转至
http://yoursite/
当咱们输入http://yoursite/?author=1 时会先跳转至http://yoursite/.../author/你的后台登陆用户名/,因此将跳转后的连接作301重定向便可。
此方法也能够在你的web服务器配置文件中实现git
xmlrpc 是 WordPress 中进行远程调用的接口,而使用 xmlrpc 调用接口进行帐号爆破在很早以前就被提出并加以利用。xmlrpc登陆接口默认没有验证码,最大错误尝试次数限制等。利用xmlrpc.php提供的接口尝试猜解用户的密码,能够绕过wordpress对暴力破解的限制。github
攻击的方式直接POST如下数据到xmlrpc.php
web
<?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接口的更多信息能够参考官方的指南 。若是密码正确,返回为:json
<?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>
错误返回:api
<?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 来提升爆破效率,使得成千上万次的账号密码组合尝试能在一次请求完成,极大的压缩请求次数,在必定程度上可以躲避日志的检测。安全
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>
从结果中能够看到在同一次请求里面处理了两种账号密码组合,并以集中形式将结果返回,经过该种方式能够极大地提升账号爆破效率。
这里举例一个nginx的
location =/xmlrpc.php {
deny all;
}
在wordpress的配置文件 wp-config.php 中添加以下代码
add_filter('xmlrpc_enabled', '__return_false');
后台默认地址是 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 功能差很少
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);
两种方法,二选一
这里用nginx来举例子,其余服务器的设置请自行研究
location ~* /wp/v2/(.*) {
deny all;
}
location ~* /wp-json/wp/v2/users(\/)?(.*)? {
deny all;
}
WPS Bidouille 中有个功能,能够直接在后台禁用掉restAPI的用户端点,因此,你只须要将用户端点在后台禁用便可。也可直接禁用REST API。
此外,该插件还支持禁用做者页面和做者连接,省掉了本文第一节的代码操做。其余功能详见这里
参考文章