\0x90\0x90\0x90

Security | 2008-02-03 12:20:04

The other day whilst browsing OC I came across a readable code of the PHP/Obfu.A IRC bot. I’m not sure whether the user contributed the actual readable code or had edited one of the obfuscated versions, but I decided to take a look at it.

It seems that PHP/Obfu.A was detected on the 30h of Jan 08 by two sources:

  • http://www.f-secure.com/weblog/archives/00001371.html
  • http://www.teamfurry.com/wordpress/2008/01/30/php-based-IRC-botnet-fast-flux-of-course/

Obfu.A is a RFI IRC bot that uses vulnerabilities in PHP code to execute remote PHP scripts. It uses the compromised site to load the remote script, join an IRC server (or in this case a number of IRC servers), and is then controllable by the bot-master. It is heavily obfuscated including the variables and server settings.

For more information on RFI exploits, see:

  • http://www.offensivecomputing.net/?q=node/624
  • http://en.wikipedia.org/wiki/Remote_File_Inclusion

I was about to test the usability of the code, but found that all settings, server details, passwords etc, were encrypted so as to further obfuscate the code. The script included a decrypting function using a cipher-key type of encryption, but had no function to encrypt. I set about analysing the decryption process and reversed it to create an encryption code so it would be usable in my tests.

Heres the original decrypt code:

function decrypt_settings($input)
 {
 $output = '';
 $input = base64_decode($input);
 for ($i = 0; $i < strlen($input); $i++) {
 $character = substr($input, $i, 1);
 $offset_character = substr(
 decode("M0AhIyFAJF4mKl4mQCMkIUAjIUAjISQjJSMkJSMkJWUzMkAzNEBoVGg0QHdlNTYz
NV4hQCMqXjdGSEdFJEAlQCNAIyRAIyFAIyQhQCNAISMkIyUj" .
 "JCVeJSZeJSYlXiYqU0RGI0AkIUZBVyRGQUFTREU="),
 ($i % strlen(decode("M0AhIyFAJF4mKl4mQCMkIUAjIUAjISQjJSMkJSMkJWUzMkAzNEBoVGg0QHdlNTYz
NV4hQCMqXjdGSEdFJEAlQCNAIyRAIyFAIyQ" .
 "hQCNAISMkIyUjJCVeJSZeJSYlXiYqU0RGI0AkIUZBVyRGQUFTREU="))) - 1,
 1
 );
 $character = chr(ord($character) - ord($offset_character));
 $output .= $character;
 }
 return $output;
 }
function decode($input)
 {
 $input = base64_decode(remove_spaces($input));
 return $input;
 }
function remove_spaces($input)
 {
 $input = str_replace(" ", "", $input);
 return $input;
 }

Those functions are called by something like decrypt_settings($settings[‘mo’]). In this case mo from the array had the value of cqtrig==

Let’s take a look at what its doing. First the function is called, and $input receives the value of an encrypted string. $input is then base64 decoded. Base64 is a type of encryption which PHP has functions for both encryption and decryption. We will work with the encrypted string cqtrig==.

The base64 decoded value is r«kŠ, which means nothing to us so far.

Next is the for loop, which basically says, increment $i whilst the value of $i is still less than the length of the $input string, which in this case is 4 characters, so run the loop 4 times.

$character gains the value of each letter, so on the first pass though the for loop, it will contain r, second «, etc etc

Then we find the $offset_character. At this point it is easiest for reading if we base64 decode the M0Ah…..
3@!#!@$^&*^&@#$!@#!@#!$#%#$%#$%e32@34@hTh4@we5635^!@#*^7FHGE$
@%@#@#$@#!@#$!@#@!#$#%#$%^%&^%&%^&*SDF#@$!FAW$FAASDE
is the resulted value.

Now the substr function will return a portion of the string which is defined by where the starting point is, and how many characters to take. substr ( string $string , int $start [, int $length ] )
3@!#!….. is our string.

The starting point is found by using another equation, $i % strlen(decode(“M0Ah……) -1. This piece of code finds the remainder value of $i divided by the string length value of 3@!#!…. So in essence it takes $i divided by 113 and the result is the remainder minus 1.

From tests that i did, the result of the equation is always one value less than $i. Then it takes one character after its pointer. So if $i is character number 26 in our encrypted string, substr will take the 26th character from the cipher text of 3@!#!….

For example on the first pass through the for loop, $i = 0, which means that substr’s result will be $offset_character = substr(3@!#!…. , -1, 1), so move the pointer one character from the end of the string, then let $offset_character equal that value. So therefore the first run through the for loop, $offset_character will equal E (from the end of 3@!#!….), the second run through $offset_character will equal 3, third @, etc etc.

So back to our example of r«kŠ,
Pass one through the for loop:
$character = r
$offset_character = E
Pass two through the for loop:
$character = «
$offset_character = 3
Pass three through the for loop:
$character = k
$offset_character = @
Pass four through the for loop:
$character = Š
$offset_character = !

Now after we have our $character and $offset_character vars filled, they are put through this: $character = chr(ord($character) – ord($offset_character))

Lets simplify it, ord($character) – ord($offset_character). ord is a built in PHP function which finds the ASCII value of a character. I found a decent ASCII table site here:

  • http://pages.videotron.com/cdao/ASCII.htm

So for r and E in our example, r = 114, E = 69, so 114 – 69 = 45. So now in our function $character = chr(45). chr is a built in PHP function which returns the character value of an ASCII value. In this case 45 = –

So at the end of the for loop for the first pass $character equals -, which is the appended to $output by $output .= $character;

After we run through the four passes of the four loop, we should get -x+i, which is our decrypted setting!
In PHP/Obfu.A -x+i is used as the channel mode. However this process can be applied to all settings.

Now we know it decrypts the settings, how about encrypt?

This is what we have so far, r – E = – or 114 – 69 = 45.
So to encrypt we have r as our unknown (x) so x – E = – or x – 69 = 45.
Using maths (:o!) we can find x by, x = – + E and then rearranging to get x = 45 + 69, therefore x = 114

Now the fun part, rearranging the PHP to encrypt. We will reuse the decryption function and just move some things, as most of it stays the same.

Seeing as were starting off with what we want to encrypt, we don’t need to base64 decode it, so we can remove $input = base64_decode($input). The for loop stays the same, as were using the same principles of the cipher text.
Although when it comes to $character = chr(ord($character) – ord($offset_character)), we now want to add the ASCII values to gain our encrypted value, so it becomes $character = chr(ord($character) + ord($offset_character));

Finally we want to base64 encode the entire string, not just the individual characters. This is because the first thing when decrypting, is the string is base64 decoded, so we want to base64 encode. To do this to the entire string it needs to be outside the for loop.

The final encrypt function should look like this:

function encrypt_settings($input)
 {
 $output = '';
for ($i = 0; $i < strlen($input); $i++) {
 $character = substr($input, $i, 1);
 $offset_character = substr(
 decode("M0AhIyFAJF4mKl4mQCMkIUAjIUAjISQjJSMkJSMkJWUzMkAzNEBoVGg0QHdlNTYz
NV4hQCMqXjdGSEdFJEAlQCNAIyRAIyFAIyQhQCNAISMkIyUj" .
 "JCVeJSZeJSYlXiYqU0RGI0AkIUZBVyRGQUFTREU="),
 ($i % strlen(decode("M0AhIyFAJF4mKl4mQCMkIUAjIUAjISQjJSMkJSMkJWUzMkAzNEBoVGg0QHdlNTYz
NV4hQCMqXjdGSEdFJEAlQCNAIyRAIyFAIyQ" .
 "hQCNAISMkIyUjJCVeJSZeJSYlXiYqU0RGI0AkIUZBVyRGQUFTREU="))) - 1,
 1
 );
 $character = chr(ord($character) + ord($offset_character));
 $output .= $character;
 }
 return base64_encode($output);
 }

You can call this function using something similar to this:

$encrypted_settings = encrypt_settings("yoursettings");
 echo($encrypted_settings);

So then you should have your encrypted string!

Hopefully this week ill be able to analyse the rest of the bot, but now most of the hard work is done by figuring out the encryption.

Hope this helps, have fun 🙂