Skip to content

More WordPress XMLRPC Brute Force Attacks

If I never see this file again, it might be too soon.

We’re seeing an uptick in requests to xmlrpc.php, the API endpoint for WordPress. The wp.getUsersBlogs method is being hit across a large number of domains; I managed to grab a series of request payloads:

There aren’t any abnormal HTTP headers in these requests, just the appropriate Content-Length, and Connection: Close. wp.getUsersBlogs is used to retrieve a list of blogs for the provided user (http://codex.wordpress.org/XML-RPC_WordPress_API/Users). Below is a sample response:

Note the application-layer 403 response in the response body. The HTTP daemon, however, will respond with a 200 OK, which means that log analyzers and simple WAF rules would be useless in identifying a malicious behavior pattern, such as a distributed series of requests to the XML-RPC endpoint, each with a different credential set. By analyzing the response body, a distributed series of requests could be used to effectively brute force a user’s credentials, without triggering traditional brute force rules that rely on HTTP response codes (for example, wp-login.php returns a 200 on failed login, but 301 when presented with correct credentials).

Administrators wanting to disable this potential attack vector can disable the wp.getUsersBlogs method, similar to how pingbacks were being disabled after the well-known XMLRPC DDoS earlier this year (http://wptavern.com/how-to-prevent-wordpress-from-participating-in-pingback-denial-of-service-attacks):

The will result in a separate API failure (event when presented with proper credentials):

I’m very unfamiliar with how common plugins like Jetpack play with this part of the XML-RPC endpoint, so I doubt that recommending this action be taken on a broad scale would be wise, but for the paranoid, it’s a good way to lock down the core application.

Update [7/24/2014]: Sent this over to the ISC team (https://isc.sans.edu/diary/+WordPress+brute+force+attack+via+wp.getUsersBlogs/18427), and it got picked up by Trustwave (http://cryptobells.spiderlabs.com/2014/07/honeypot-alert-wordpress-xml-rpc-brute-force-scanning.html) and Threatpost (http://threatpost.com/wordpress-sites-seeing-increased-malware-brute-force-attacks-this-week/107378 – does that screenshot look familiar? 😉 ). We noticed far fewer occurrences of this in the days that followed, but that doesn’t negate the threat. Login Security Solution advertises defense against XML-RPC authentication, but with such a massive horizontal scale, that still wouldn’t have made a difference.

Update [8/11/2014]: WordPress 3.9.2 was released a few days ago that included an XML-RPC vulnerability fix, but this did not patch what we’ve noted here- the vulnerability patched was completely unrelated (BreakSec has a great write-up on the fix).

Published inBlog

23 Comments

  1. Mike Mike

    My site is being bombarded by “POST /xmlrpc.php HTTP/1.1” every few minutes. Login Security Solution plugin won’t detect these attacks in my case.

    • poprocks poprocks

      As noted above, Login Security Solution is not effective against a large number of differing IPs. Have you tried using ngrep, or writing the contents of the POST payload to a file, to see what the actual content of the requests is? Have you considered disabling the getUsersBlog API method? How about disabling XML-RPC entirely if you’re not using a remote publishing client or Multisite?

  2. Mike Mike

    Hi, thanks for your help.

    To make a clear picture, I ran a script from your second screensot against my side. Login Security Solution plugin has detected it as a XML-RPC attack. The server response is 200.

    Speaking about those endless “POST /xmlrpc.php HTTP/1.1″from elsewhere, the server response is 406. It seems their script, probably, is poorly written.
    Your advice about writing the contents of the POST payload to a file to see the actual content of the requests is the excellent idea.

    I keep googling that technique, can’t find it yet.

    Could you please provide me with you input?

    Thanks again.

    M.

  3. poprocks poprocks

    There are a few tools you can use to grab POST payloads. I’ve developed (well, still developing- it’s almost done!) a cloud proxy WAF that analyzes all HTTP traffic; this can be used to analyze HTTP traffic from our custom web interface. You can check out the project at https://freewaf.com/. We used ngrep (http://ngrep.sourceforge.net/) to capture packet payloads for this post. Syadmins of the North also had a cryptobells post detailing this, giving an example of a quick function to log data posted to xmlrpc.php; see their post at http://www.saotn.org/huge-increase-wordpress-xmlrpc-php-post-requests for more info on that.

  4. Mike Mike

    Thanks, Robert.

    I just tested Syadmins of the North’s script.

    It’s easy to run and read a log file.
    I can read my curl command in the log file while testing it, but those foreign:

    “POST /xmlrpc.php HTTP/1.1” 406 226

    go undetected.

    That logger and Login Security Solution can’t catch it. Hopefully, its payload is totally corrupted and won’t bring any harm.

    One more thing, when I deny those IP’s I get:

    “POST /xmlrpc.php HTTP/1.1” 403 18200

    See the total size of data: 18200, it was 226 before denying it.

    God knows what those script kiddies are doing 🙂 but their servers seem very powerful.

    They are on Ecatel Ltd. in the Netherlands and googling their activities makes them look like a cuckoo’s nest 🙂

    Thanks again for your support.

    M.

  5. Mike Mike

    Robert, I am still here. 🙁

    Never mind my 18200 bytes mention.

    It’s just my host’s state of the art 403 page.

    M.

  6. Mike Mike

    Robert, please never mind that 18200 bytes mention.

    It’s my host’s state of the art 403 page.

    M.

  7. poprocks poprocks

    Yes, you’re right about the response size 🙂

    As for the 406, it’s a code returned by the host- not something clients are sending. 406 is Not Acceptable, which relates to what Accept headers the client presents (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html). 226 is the response length. If the server is returning an error (with a status >= 400), it follows that application-level logging will not pick up the payload. Nothing you can do about that; since it’s not hitting your application, it doesn’t present a security risk. 🙂

  8. r109 r109

    I am currently seeing a massive flood of wp.getUsersBlogs hits on my server that I’m monitoring.

    • poprocks poprocks

      Would you care to share some logs, packet captures, or other analytics?

      • r109 r109

        Hi poprocks,

        I am not keen on ModSecurity, but I have these lines below attempting to curb the brute forcers… I wish I knew how to write the bf_counter and block the ip in the firewall but again I’m new to ModSec…
        ——————-
        SecRule REQUEST_URI “xmlrpc.php” “deny,status:411,id:5000227,chain,msg:’xmlrpc DoS attempt'”
        SecRule HTTP_User-Agent “WinHttp.WinHttpRequest.5”
        ——————-

        while this is going on, I’m watching,
        sudo tail -f /etc/httpd/logs/modsec_audit.log

        below is an example of what I’m capturing in the modsec_audit.log. As you can see, they are using the xmlrpc api method wp.getUsersBlogs. If you reference the method in the WordPress Codex, the two values “test” and “sydney” represent username and password.

        reference here: http://codex.wordpress.org/XML-RPC_wp

        ——————-
        –48d9096c-A–
        [23/Aug/2014:14:27:18 –0400] Go6GSUWntUYAAEPaTlIAAAEG [attackers-ip] 57504 [target-server-ip] 80
        –48d9096c-B–
        POST /xmlrpc.php HTTP/1.1
        Content-Length: 215
        Host: [attackers-target-domain.com]
        X-Forwarded-For: [attackers-ip]
        X-Varnish: 1715115807

        –48d9096c-C–

        wp.getUsersBlogs

        test
        sydney

        –48d9096c-F–
        HTTP/1.1 404 Not Found
        X-Powered-By: PHP/5.3.22
        Expires: Wed, 11 Jan 1984 05:00:00 GMT
        Cache-Control: no-cache, must-revalidate, max-age=0
        Pragma: no-cache
        Vary: Accept-Encoding
        Connection: close
        Content-Type: text/html; charset=UTF-8

        –48d9096c-H–
        Message: Access denied with code 411 (phase 2). Operator EQ matched 0 at REQUEST_HEADERS. [file “/usr/local/apache/conf/modsec2.user.conf”] [line “877”] [id “5000228”] [msg “xmlrpc DoS attempt”]
        Action: Intercepted (phase 2)
        Stopwatch: 1408818438112841 626398 (1127* 1964 -)
        Producer: ModSecurity for Apache/2.5.13 (http://www.modsecurity.org/).
        Server: Apache/2.0.63 (Unix) mod_ssl/2.0.63 OpenSSL/0.9.8e-fips-rhel5

        –48d9096c-Z–
        ——————-

        I was thinking a good way to stop this is to create a ModSecurity rule that blocks the ip after x failed attempts, however, I am new to ModSec so I can only seek assistance.

        poprocks, I hope this helps. I am willing to provide more log information and testing.

      • poprocks poprocks

        This all looks pretty standard, exactly what I first reported several weeks ago 🙂

        See the following pastebin for a rule that denies based on the sort of activity. An IP will be banned for fifteen minutes after 3 failed attempts: http://pastebin.com/BKPDxGLe

  9. r109 r109

    There is a new brute force exploit for xmlrpc.php. Attackers are using XMLRPC API method wp.getUsersBlogs to brute force logins with dictionaries.

  10. Well, recently Brute force Attacks has immensely increased, becoming a dangerous factor for all WordPress users, but it is a thing, which is fight-able, I mean, by using security methods, we can move brute force attacks out of the window. Although, it can be difficult for newbies, who just got started with WordPress, but he/she can learn by reading posts online and then can implement security.
    In my view, implementing only three tricks works very well, Changing Login Slug, A content Delivery network (CDN) and a Security Plugin, which bans IP address after a few Login attempts.

Leave a Reply

Your email address will not be published. Required fields are marked *