Decrypting Zipped Base 64 Encoded PHP Malware

30 November -0001

Recently during an incident response I encountered a common piece of PHP used to provide an attacker with a user friendly interface. This PHP was effectively obscured by base 64 encoding the PHP, then zipping it. Thus the PHP would be invoked using:

eval(gzinflate(base64_decode('FJ3AD44...')));

Of course the gibberish in the middle of the code statement is base 64 encoded, gzipped content. Normally one would reveal the content by changing 'eval' to 'print_r' like so:

print_r(gzinflate(base64_decode('FJ3AD44...')));

and viewing the PHP page, which would then decode, inflate and display the PHP. However, some malware is encoded and zipped multiple times, so doing this does nothing more than reveal another garbled string like:

eval(gzinflate(base64_decode('zaBqmllMECf...')));

Depending on how many times the attacker has encoded and zipped the file it may become impossible, or at least infeasible, to decode and inflate the original PHP. In order to properly decode and inflate the malware we have to write a custom PHP function. In this case using the following code:

$string = "eval(gzinflate(base64_decode('zaBqmllMECf...')));";
function decodeIt($text) {
	$text = substr($text, 30);  //take off the eval(gzinflate(base64_decode('
	$text = substr($text,0,strlen($text)-5); //take off the ending ')));
	$text = gzinflate(base64_decode($text));
	return $text;
}
while (substr($string,0,4)=='eval') {
	$string = decodeIt($string);
}
$string = htmlspecialchars($string);
print("decoded:\n<pre>");
print_r($string);

Using this code, PHP will look at the decoded, inflated scripts and if they begin with 'eval' then they will be run recursively through the decodeIt() function until they are completely unwrapped. This method is often necessary, especially when malware is encoded and zipped dozens of times.