- Внедрение локального shellcode в среде Windows
- Введение
- Подключение к LPC порту
- Создание разделяемой секции
- Методика
- Создание эксплойта
- Проблемы с использованием LPC портов
- Примеры эксплойтов
- Заключение
- Реверсинг малвари для начинающих. Внедрение shellcode и шифрование malware-кода
- Содержание статьи
- Четыре предыдущих урока
- WARNING
- Data Encoding, или шифрование как искусство
- Теоретическая часть
- Специализированный инструментарий
- Внедрение shellcode, или как получить удаленный доступ к командному шеллу
- Теоретическая часть
- Обнаружение шелл-кода на взломанной машине
- WARNING
- Анализ семпла malware01
- Xakep #263. Кредитки в опасности
- Анализ семпла malware02
- Заключение
Внедрение локального shellcode в среде Windows
В этой статье описана новая методика создания 100% надежных локальных эксплойтов для операционных систем семейства Windows. Методика основана на недостатках архитектуры операционных систем Windows, позволяющих низко привилегированному процессу внедрять данные в почти любой другой процесс, даже если он запущен с более высокими правами.
В этой статье описана новая методика создания 100% надежных локальных эксплойтов для операционных систем семейства Windows. Методика основана на недостатках архитектуры операционных систем Windows, позволяющих низко привилегированному процессу внедрять данные в почти любой другой процесс, даже если он запущен с более высокими правами. Мы знаем, что реализация локальной атаки более проста, чем удаленной, однако и в этом процессе также есть некоторые трудности. После краткого введения и описания методики будет показано несколько примеров, понимание которых позволит читателю использовать описанные приемы при создании своих собственных эксплойтов.
Введение
Для обхода этих ограничений можно использовать различные адреса возврата и/или техники. После прочтения этой статьи вы сможете перестать волноваться обо всем этом, так как написать 100% надежный эксплойт, работающий на любой версии Windows, при любом пакете обновлений, языке, обходящий защиту от переполнения буфера будет очень просто. Происходить это будет вследствие того, что ваш код не будет выполняться из стека или кучи и не будет использовать фиксированный адрес возврата.
Предлагаемая методика использует Windows LPC (Local/Lightweight Procedure Call) – механизм межпроцессорного взаимодействия, используемый RPC для локальной связи. LPC позволяет процессам, используя LPC порты, взаимодействовать между собой посредством сообщений. LPC плохо документирован и не будет здесь детально описываться, однако более подробную информацию вы можете получить из источников, указанных в разделе “Ссылки”. LPC порты – это объекты Windows. Процессы могут создавать именованные LPC порты, к которым могут подключаться другие процессы, используя имя созданного LPC порта. Вы можете увидеть LPC порты процессов, воспользовавшись утилитой Process Explorer с сайта www.sysinternals.com. Для этого нужно выбрать процесс и посмотреть на строку Port в столбце Type (нижняя панель), в которой будет имя порта. Дополнительную информацию, например права доступа и т.д., можно получить, дважды кликнув на имени порта.
LPC активно используется внутренними механизмами операционной системы, такими как OLE/COM и т.д., что означает, что почти каждый процесс пользуется LPC портом. LPC порты могут быть защищены с помощью ACL, поэтому иногда, если клиентский процесс не имеет необходимых привилегий, соединение не может быть установлено.
Для применения методики мы будем использовать несколько функций API, которые будут описаны ниже.
Подключение к LPC порту
Для подключения к LPC порту используется функция Native API – NtConnectPort из Ntdll.dll.
ClientPortHandle: указатель на дескриптор порта, возвращаемый функцией.
ServerPortName: указатель на структуру UNICODE_STRING, содержащую имя порта, к которому нужно подключиться.
SecurityQos: указатель на структуру SECURITY_QUALITY_OF_SERVICE.
ClientSharedMemory: указатель на структуру LPCSECTIONINFO, содержащую информацию о разделяемой памяти.
ServerSharedMemory: указатель на структуру содержащую информацию о разделяемой памяти.
MaximumMessageLength: указатель на максимальный размер сообщения, возвращаемый функцией.
ConnectionInfo: указатель на массив, в котором содержится сообщение (отправленные на LPC сервер и полученные от LPC сервера данные).
ConnectionInfoLength: указатель на размер сообщения.
Другие функции для взаимодействия с LPC не будут рассматриваться, так как в описываемой методике они не используются. Если вы хотите узнать о них больше, воспользуйтесь информацией из раздела “Ссылки”.
Самый важный параметр, которой мы передаем в вышеописанную функцию, это имя LPC порта, находящееся в структуре UNICODE_STRING:
Поля структуры LPCSECTIONINFO:
Перед заполнением этой структуры нужно создать разделяемую секцию (см. [1] для большей информации о разделяемых секциях) и спроецировать ее на оба процесса (процесс из которого происходит подключение и процесс, к которому мы подключаемся).
Для структуры LPCSECTIONMAPINFO мы должны установить только размер структуры:
Структура SECURITY_QUALITY_OF_SERVICE может принимать любые значения, поэтому о ней мы можем не волноваться:
Для переменной ConnectionInfo мы можем использовать буфер со 100 нулевыми элементами; значение переменной ConnectionInfoLength должно быть равно размеру буфера.
Создание разделяемой секции
Для создания разделяемой секции используется следующая функция Native API из Ntdll.dll.
SectionHandle: указатель на идентификатор секции, возвращаемый функцией.
DesiredAccess: определяет нужные виды доступа: чтение, запись, исполнение и т.д.
ObjectAttributes: указатель на структуру OBJECT_ATTRIBUTES.
MaximumSize: указатель на размер создаваемой разделяемой секции.
PageAttributes: атрибуты страницы памяти: чтение, запись, исполнение и т.д.
SectionAttributes: атрибуты секции в зависимости от типа создаваемой секции.
FileHandle: дескриптор файла, для которого создается проекция в память.
Ниже перечисляются интересующие нас параметры.
Для параметра DesiredAccess мы должны указать нужные нам виды доступа к секции (нам нужен доступ на чтение, запись и исполнение). MaximumSize определяет нужный нам размер секции; переменная может принимать любое значение, но оно должно быть достаточное для хранения данных, которые будут помещены нами в секцию позже. В значение переменной PageAttributes мы также должны установить права на запись и чтение, а в SectionAttributes – тип секции “committed memory”.
Методика
Теперь, когда мы ознакомились с функциями для установления LPC соединения, рассмотрим методику атаки. Как я уже сказал, большинство процессов в Windows имеют LPC порты, к которым мы можем подключиться (если обладаем соответствующими правами). Как вы могли заметить, в одной из структур, передаваемых в функцию функции NtConnectPort, есть поле – разделяемая секция, которая проецируется на оба процесса, осуществляющих обмен данными, что является очень хорошей новостью. Почему? — спросите вы. Потому что все, что мы поместим в разделяемую секцию нашего процесса, будет спроецировано на другие процессы. Другими словами мы можем внедрить любые данные (и конечно shellcode) в любой процесс, какой мы захотим, даже если он запущен с большими правами, чем наш процесс! Что еще более удивительно, адрес, на который будет спроецирована разделяемая секция, будет возвращен функцией! Если вы все же не понимаете что в этом хорошего, перед продолжением чтения вам нужно ознакомиться с принципами создания эксплойтов. В большинстве случаев при атаке на уязвимое приложение с использование LPC, мы будем иметь возможность поместить shellcode в атакуемый процесс, а также будем знать, где это код расположен, поэтому все, что нужно будет сделать, это заставить процесс сделать переход на этот адрес.
Например, если вы хотите внедрить код в процесс smss.exe, вам нужно создать разделяемую секцию, подключиться к LPC порту \DbgSsApiPort, затем поместить код в разделяемую секцию, что незамедлительно приведет к проецированию этого кода на адресное пространство smss.exe. Если вы захотите внедрить код в процесс services.exe, вам нужно будет сделать то же самое, только подключаться к LPC порту \RPC Control\DNSResolver.
Данная методика обладает следующими преимуществами:
- Независимость от локализации Windows.
- Независимость от установленных пакетов обновлений.
- Независимость от версии Windows.
- Нет ограничений на размер shellcode.
- Нет ограничения на использования нулевых байтов, не нужно кодировать код.
- Нет ограничений связанных с кодировками символов.
- Обход некоторых защит от переполнения буфера.
- Быстрая разработка эксплойта.
Однако есть и некоторые недостатки:
- У большинства процессов в Windows есть LPC порт, но все же не у всех.
- Методика может не сработать, если используется переполнение буфера ASCII строкой, так как адрес разделяемой секция сервера в процессе сервера иногда принимает значение 0x00XX0000. Нужно заметить, что это маловероятно, потому что в большинстве случаев (если не во всех) переполнение буфера в Windows происходит Unicode-строкой; кроме того, проблема может быть решена переподключением к LPC порту до тех пор, пока не будет возвращен “хороший” адрес.
Создание эксплойта
Эксплойт, работающий по описанной методике, должен выполнить следующие действия:
- Создать разделяемую секцию, которая будет проецироваться при LPC соединении.
- Подключаться к LPC порту уязвимого процесса. После успешного подключения будут получены два указателя на разделяемую секцию, один для клиентского процесса, другой для серверного процесса.
- Скопировать shellcode в разделяемую секцию, спроецированную на клиентский процесс, в результате чего этот shellcode сразу же будет спроецирован на атакуемый процесс.
- Используя уязвимость, заставить атакуемый процесс сделать переход на разделяемую секцию, где расположен shellcode.
Давайте рассмотрим простой эксплойт для фиктивной уязвимости в сервисе XYZ, в котором функция VulnerableFunction() получает и обрабатывает Unicode-строку без проверки размера буфера-приемника. В этом примере используется переполнение буфера, но описываемая методика не ограничивается этим типом уязвимостей и может применяться для уязвимостей любого типа, в чем вы сможете убедиться, посмотрев на эксплойты доступные с этой статьей (см. Примеры эксплойтов).
Следующий код создает разделяемую секцию, размером 0x10000 байт с полными правами доступа (чтение, запись, исполнение и т.д.) и с атрибутами страницы – запись и чтение:
Далее идет подключение к LPC порту с именем LPCPortName:
После удачного соединения указатели на начало проецируемой разделяемой секции в клиентском и серверном процессах возвращаются в sectionInfo.ClientBaseAddress и sectionInfo.ServerBaseAddress соответственно.
Следующих код копирует shellcode в разделяемую секцию, спроецированную на адресное пространство клиентского приложения:
Следующий код, используя уязвимость, заставляет атакуемый процесс сделать переход на разделяемую секцию:
Проблемы с использованием LPC портов
При работе с LPC портами могут возникнуть некоторые проблемы:
- Некоторые LPC порты имеют динамические имена (например, порты, используемые OLE/COM). Это означает, что имя порта, при создании его процессом, всегда новое.
- Некоторые LPC порты имеют строгие ACL и не позволяют нам подключиться, если у нас нет соответствующих прав.
- Для успешного подключения к некоторым портам нужно передать определенные данные через параметр ConnectionInfo.
Первую проблему можно решить двумя способами. Первый, достаточно трудоемкий способ состоит в дизассемблировании приложения и изучении алгоритма определения имен LPC портов. Второй способ заключается в перехвате некоторой функции для получения имени порта. При работе с Automation (OLE/COM) перед подключением к порту, клиентский процесс определяет имя LPC порта сервера некоторым магическим образом, реализованным внутри COM/OLE. Дизассемблирование всего этого весьма непростое занятие, но мы можем, перехватив вызов функции NtConnectPort, определить имя LPC порта, когда функция попытается подключиться к порту. Этот прием можно увидеть в одном из эксплойтов, прилагающихся к этой статье (см. Примеры эксплойтов).
Вторая проблема выглядит неразрешимой. Ой, я сказал “неразрешимой”? Извините, ведь такого слова нет в словаре хакера. На данный момент эта проблема кажется неразрешимой, но LPC настолько не изучен, и мне приходилось наблюдать некоторые сверхъестественные вещи, касающиеся LPC, поэтому я не уверен на 100%. Можно подключиться к LPC порту не напрямую, обойдя ограничения, но при этом будут проблемы с созданием разделяемой секции. Я углублюсь в эту тему, когда появится свободное время.
Третья проблема может быть легко решена дизассемблированием и установлением того, как происходит подключение к проблематичному порту. Попробуйте поставить точку останова на функцию NtConnectPort, посмотрите на значения параметров, передаваемых в функцию, и используйте их в эксплойте.
Примеры эксплойтов
Чтобы увидеть описанную методику в действии, взгляните на прилагающиеся эксплойты.
- SSExploit2 (MS05-012 — COM Structured Storage Vulnerability — CAN-2005-0047)
- TapiExploit (MS05-040 — Telephony Service Vulnerability – CAN-2005-0058)
Заключение
Как вы могли убедиться, используя описанную методику, очень просто за 5-10 минут создать почти 100% надежный (я говорю “почти”, потому что не все уязвимости просты в использовании, а иногда слишком сложны для создания надежного эксплойта) эксплойт, использующий простое переполнение стека, независимый от локализации системы и пакета обновлений. По крайнее мере мне потребовалось именно столько времени для создания локального TAPI эксплойта (MS05-040).
Реверсинг малвари для начинающих. Внедрение shellcode и шифрование malware-кода
Содержание статьи
Ну что, ты готов попробовать свои силы и ринуться в бой? Тогда смело приступаем к делу!
Четыре предыдущих урока
Перед прочтением этой статьи рекомендуем освежить в памяти предыдущие материалы: нулевой, первый, второй, третий.
WARNING
Вся информация предоставлена исключительно в ознакомительных целях. Ни редакция, ни автор не несут ответственности за любой возможный вред, причиненный материалами данной статьи. Если ты что-то делаешь — будь уверен и понимай, что ты делаешь!
В нашем сегодняшнем выпуске мы разберем две интересные фичи современного поколения malware: обфускацию кода вредоноса при помощи шифрования и внедрение шелл-кода (англ. shellcode) после инфицирования — чтобы, например, выполнить какой-нибудь эксплоит или получить командную оболочку ОС.
Data Encoding, или шифрование как искусство
Теоретическая часть
Современные антивирусные движки и эвристические анализаторы с высокой долей вероятности позволяют обнаруживать еще неизвестные или малоизученные вирусы. Для того чтобы «расколоть» вредонос, антивирусное ПО сначала прогоняет по сигнатурной базе, затем, если не удается ничего обнаружить, выполняет код программы в собственной песочнице и, наконец, запускает поведенческий анализатор, рассматривая под лупой каждое действие вредоноса, будь то обращение к реестру, системным файлам, сторонним приложениям или проявление сетевой активности. Поэтому для скрытия присутствия вредоноса в системе применяются различные техники и методы, к примеру использование rootkits или bootkits, которые мы рассматривали в прошлой статье.
Другая задача состоит в том, чтобы максимально усложнить вирусному аналитику ручной анализ образца малвари, когда он уже попал в антивирусную лабораторию. Это, в свою очередь, позволит оттянуть время до того, как сигнатуру малвари добавят в антивирусные базы или будет выпущена тулза для лечения инфекции.
Один из вариантов — шифрование собственного кода малвари (англ. encoding), часто также называемое обфускацией. «Запутывать» код возможно на уровне алгоритма, заложенного во вредонос, исходника (source code) и/или ассемблерного текста. Для создания запутанного ассемблерного кода (того, который мы видим в IDA Pro) могут применяться и специализированные компиляторы, использующие неочевидные или недокументированные возможности. Отдельная песня — класс специальных программ, выполняющих обфускацию (обфускаторов). В качестве примера такого софта для программ на платформе .NET приведу вот эту софтину, для Java — эту. Будет полезна и старая добрая статья Криса Касперски «Обфускация и ее преодоление». Вот тут еще одна неплохая статья с теорией. Подробный рассказ про использование LLVM в качестве обфускатора. И обязательно прочитай статью «Учимся распознавать полиморфизм и обфускацию кода на примере известного вируса» в одном из прошлых выпусков нашего журнала.
Для тех же, кому лень разбираться в премудростях обфускаторов и писать свой запутанный код, есть готовые программы, которые работают по принципу «загрузил — выбрал опции — зашифровал». Найти их можно в кладовке легендарного VX Heaven. Однако помни, что все эти тулзы давно уже изучены современными антивирусами и внесены в сигнатурные базы, так что халявы не будет. 🙂
При шифровании кода малвари вирусописатель выбирает конкретный метод, который наилучшим образом отвечает его целям. Иногда могут использоваться простые шифры, к примеру логические побитовые операции — OR или XOR, реализующие битовые сдвиги в машинных инструкциях, или же сложные алгоритмы, к примеру Base64, изначально разработанный для кодирования сообщений электронной почты.
Часто используемый шифр, базирующийся на операции XOR, — это самый простой шифр, который весьма похож на всем известный шифр Цезаря. XOR, или исключающее OR, — логическая, или, как еще называют, битовая, операция, которая может быть использована для изменения оригинального порядка битов в ассемблерном коде. Операция XOR для шифрования использует значение статического байта. Каждый байт текста последовательно изменяется. Для тех, кто не понял, хорошим подспорьем будет вот этот пример, там наглядно показывают, как текст шифруется, причем правило сдвига букв можно выбрать самому.
Существует еще несколько простых алгоритмов шифрования, основанных на использовании рассмотренных инструкций:
- Алгоритмы ADD и SUB позволяют производить кодирование операциями ADD и SUB для отдельных байтов блока аналогично операции XOR. ADD и SUB — нереверсивные операции, поэтому их необходимо использовать совместно, то есть одну для шифрования, а другую для расшифровки.
- ROR- и ROL-инструкции позволяют перевернуть несколько битов в байте справа или слева. Точно так же, как ADD и SUB, они должны использоваться вместе, поскольку они необратимы, то есть выполняются только в одну сторону.
- ROT — это оригинальный шифр Цезаря. Обычно используется латинский алфавит (A–Z и a–z), начиная с любой буквы, или 94 печатных символа в стандартной кодировке символов ASCII.
- Многобайтовые (Multibyte Instead) преобразования заключаются в том, что заменяется не один байт, данный алгоритм позволяет использовать больше ключевых значений (к примеру, часто берутся цепочки 4 или 8 байт длиной). В этом случае ради удобства применяют операции XOR для каждого блока.
Чуть более подробно о приемах обфускации, построенных на использовании простых операций, можно почитать в статье Malware Monday: Obfuscation.
Специализированный инструментарий
- IDA Pro имеет очень полезный плагин под названием FindCrypt2, также идущий в пакете для разработчиков IDA Pro SDK. Плагин ищет в теле дизассемблированной программы специфические константы, связанные с криптографическими алгоритмами. И нужно сказать, хорошо справляется со своей задачей, так как большинство алгоритмов шифрования давно известны и изучены и используют сигнатурные элементы, по которым можно вычислить алгоритм.
- Krypto ANALyzer, он же просто KANAL, тоже весьма полезный инструмент, который использует те же принципы, что и плагин FindCrypt2 для IDA Pro. Однако Krypto ANALyzer предназначен для PE-анализатора, в нашем случае это неутомимый PEiD. Помимо констант и алгоритмов шифрования, Krypto ANALyzer распознает также таблицы Base64 и связанные с шифрованием функции из таблицы импорта.
- IDA Entropy Plugin — один из инструментов, который позволяет искать признаки энтропии, в нашем случае шифрования в PE-файлах. Он выручит, когда первые два со своей задачей не справились, но у нас есть подозрение, что криптография в коде точно используется. Достаточно просто загрузить данный плагин в IDA Pro, поместив ida-ent.plw в каталог IDA, где лежат все плагины.
Шифрование кода malware с целью обфускации во многом зависит от компетенции вирусописателя. Для шифрования могут использоваться в самом простом случае логические побитовые операции, например OR или XOR, реализующие битовые сдвиги в машинных инструкциях, или сложные алгоритмы, к примеру Base64, изначально разработанный для кодирования сообщений электронной почты.
Внедрение shellcode, или как получить удаленный доступ к командному шеллу
Теоретическая часть
Шелл-код (англ. shellcode) — это часть кода, встроенного в малварь и позволяющего после инфицирования целевой системы жертвы получить код командной оболочки, например /bin/bash в UNIX-подобных ОС, command.com в черноэкранной MS-DOS и cmd.exe в современных операционных системах Microsoft Windows. Очень часто шелл-код используется как полезная нагрузка эксплоита, но это выходит за рамки нашего материала. Зачем все это нужно? Как ты понимаешь, мало просто инфицировать систему, проэксплуатировать уязвимость или положить какую-нибудь системную службу. Все эти действия хакеров во многих случаях нацелены на получение админского доступа к зараженной машине. Так что малварь — это всего лишь способ попасть на машину и получить shell, то есть управление. А это уже прямой путь к сливу конфиденциальной информации, созданию ботнет-сетей, превращающих целевую систему в зомби, или просто выполнению иных деструктивных функций на взломанной машине.
Шелл-код обычно внедряется в память эксплуатируемой программы, после чего на него передается управление при помощи использования программных ошибок, таких как переполнение стека или переполнение буфера в куче, или использования атак форматной строки. Пока не забыл: хорошая статья на Хабре на эту тему. Управление шелл-коду передается перезаписью адреса возврата в стеке адресом внедренного шелл-кода, перезаписью адресов вызываемых функций или изменением обработчиков прерываний. Кстати, не припоминаешь ли чего из прошлого урока про инжекты в процессы? 🙂 Результатом всего этого и будет выполнение шелл-кода, который открывает командную строку для использования взломщиком.
Для тех, кто готов погружаться в дебри шелл-кода и пробовать свои силы в разработке, отличная статья из нашего журнала «Многоразрядные шелл-коды. Пишем Ring0-shellcode под Windows x64».
При эксплуатации удаленной уязвимости (то есть эксплоита) шелл-код может открывать на уязвимом компьютере заранее заданный порт TCP для дальнейшего удаленного доступа к командной оболочке. Такой код называется привязывающим к порту (англ. port binding shellcode). Если же шелл-код подключается к порту компьютера атакующего (с целью обхода брандмауэра или просачивания через NAT), то такой код называется обратной оболочкой (reverse shell shellcode).
Если ты хочешь побаловаться использованием эксплоитов, дорога тебе к Metasploit Framework, однако это выходит за рамки нашей статьи, поэтому я приведу лишь несколько ссылок на интересные статьи, которые ты можешь изучить самостоятельно.
Существуют два способа запуска шелл-кода в память на исполнение:
- Метод position-independent code (PIC, позиционно независимый код) — это код, который использует жесткую привязку бинарного кода (то есть кода, который выполнится в памяти) к определенному адресу или данным. Шелл-код — это по сути PIC. Почему жесткая привязка так важна? Шелл не может знать, в каком именно месте оперативной памяти будет располагаться, поскольку во время выполнения различных версий скомпрометированной программы или малвари они могут загрузить шелл-код в разные ячейки памяти.
- Метод Identifying Execution Location («Определяй свое место выполнения») заключается в том, что шелл-код должен разыменовать базовый указатель при доступе к данным в позиционно независимой структуре памяти. Добавление (ADD) или вычитание (Reduce) значений от базового указателя позволяет безопасно получить доступ к данным, которые входят в состав шелл-кода.
Обнаружение шелл-кода на взломанной машине
Хакеры, дорожащие своей свободой и репутацией, пишут шелл-коды, используя техники, скрывающие их атаку. Так, типичная система обнаружения вторжений (англ. IDS) обычно просматривает весь входящий сетевой трафик в поисках структуры, специфичной для шелл-кода. Если IDS находит такую структуру, то пакет, содержащий эту сигнатуру, уничтожается до того, как он еще достигнет своей цели. Однако слабая позиция IDS состоит в данном случае в том, что если трафик закодирован, то распознать его не удастся. Теперь ты улавливаешь, почему так ценно шифрование?
WARNING
Будь осторожен при скачивании и распаковке архивов с образцами malware на компьютер. Все исследования выполняй только в изолированной виртуальной среде! Не выполняй действий, в которых на 100% не уверен! И не забывай делать регулярные snapshot системы для быстрого отката в случае поломки.
Анализ семпла malware01
- IDA Pro (with plugins).
- OllyDbg.
- PEiD (with Krypto ANALyzer).
Ну что же, начнем! Первым делом открываем PEiD и грузим туда наш семпл. Все как и ожидалось, никаких сюрпризов. Однако мы помним, что имеем дело с криптографией в коде малвари, поэтому попробуем запустить плагин Krypto ANALyzer (должен быть предварительно подгружен в PEiD). И вот что мы видим: сигнатуры обнаружены.
Результат анализа файла в программе PEiD
Xakep #263. Кредитки в опасности
Идем дальше. Открываем IDA Pro и загружаем туда семпл, минуя множество окон и малоинформативных сведений, обращаем внимание на таблицу ресурсов. Да, и здесь тоже нет ничего бросающегося в глаза.
Отображение окна String в IDA Pro
Быть может, попытаем счастья в поиске сетевой активности? Хм. Запускаем Process Explorer, далее выделяем процесс, запущенный нашим вредоносом, переходим в «Свойства», жмем вкладку Strings и параллельно запускаем сетевую акулу Wireshark, в окне анализа пакетов можно обнаружить GET-запрос на веб-ресурс http://www.practicalmalwareanalysis.com. Данные на вкладке Strings утилиты Process Explorer и выдернутые из пакета Wireshark совпали.
Сравнение данных String в Process Explorer и Wireshark
Вернемся к дизассемблеру IDA Pro. На графической диаграмме замечаем любопытные строки: подпрограмма @0x00401300 загружает некий ресурс в двоичном виде и применяет операцию XOR для некоторого значения ‘;’ . На следующем скриншоте это весьма явно заметно.
Данные из IDA Pro
Ниже скриншот окна из IDA Pro, где желтым подсвечены инструкции шифрования с помощью операции XOR.
Текст кода с инструкциями шифрования с помощью операции XOR
И у нас возникает закономерный вопрос: какой ключ (шифр) используется для кодирования и что конкретно он кодирует? Вспоминая то, что мы нашли раньше, можно сделать вывод, что ключ ‘;’ призван декодировать строку, содержащую URL http://www.practicalmalwareanalysis.com.
Итак, мы определили, какой алгоритм шифрования используется и какие данные он шифрует. Пора подключать плагины, которые помогут нам в декодировании. Мы будем использовать инструменты поиска сигнатур FindCrypt2, Krypto ANALyzer и IDA Entropy Plugin, чтобы определить другие механизмы кодирования.
Окно с результатами парсинга семпла в Krypto ANALyzer
KANAL plugin обнаружил четыре адреса, использующих символы из строки ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/. Как думаешь, на что это похоже? Да-да, мой друг, ты правильно смекаешь, что этот набор символов представляет собой алфавит, из которого потом делаются перестановки. А что же с сетевым трафиком? Для его кодирования используется алгоритм Base64. Ниже представлен скриншот из отладчика OllyDbg, иллюстрирующий закодированные строки.
Закодированные строки Base64 в OllyDbg
А вот скриншот все из того же отладчика, содержащий инструкцию, которая отвечает за сам процесс кодирования.
Где-то в коде есть функция, отвечающая за декодирование информации по алгоритму Base64, и находится она по адресу 0x004010B1.
Функция, отвечающая за декодирование информации по алгоритму Base64
Посмотрим этот код на диаграмме из IDA Pro, желтым подсвечены ключевые характеристики кодирования, а именно: максимальная длина сообщения — 12 символов. В описании алгоритма максимальная длина в Base64 составляет 16 байт.
Просмотр кода в IDA Pro
А вот тут мы и подходим к главному вопросу: что же малварь делает после своего запуска? Код малвари сканирует имя компьютера, на котором он запустился, шифрует его и посылает на веб-ресурс (максимум 12 байт) http://www.practicalmalwareanalysis.com каждые 30 секунд, пока не получит строку со значением 0x6F в качестве первого символа в ответе. Так сказать, мини-реализация RAT, то есть управляющего сервера, посылающего команды, и зараженной машины, получающей и исполняющей эти команды.
Анализ семпла malware02
Наш семпл будет содержать шелл-код, который позволит после запуска на зараженной машине получить удаленное управление по TCP/IP и доступ к командной оболочке ОС. Ну, поехали! Грузим файл в IDA Pro и в строках кода видим, что некая функция в коде вызывается, чтобы создать новый процесс, а затем инжектит (внедряет) в него необходимый для получения удаленного доступа шелл-код.
Окно IDA Pro c инструкциями по созданию нового процесса
Переходим к OllyDbg, чтобы увидеть аргументы, передаваемые в функцию создания процесса GetProcessID (см. 0x4013DE). Мы можем установить точку останова на месте этой инструкции. На следующем скриншоте мы видим, что путь к запуску iexplore.exe передается в функцию, а она, в свою очередь, использует его для реализации CreateProcess, то есть создания процесса, в который внедряется шелл-код.
Окно OllyDbg c точкой останова на 0x4013DE
Это верное направление, продолжаем копать в эту сторону. Чтобы найти шелл-код, я бы сначала попробовал поискать вызов функции, ответственной за внедрение шелл-кода в удаленный процесс. Поиск WriteProcessMemory будет весьма хорошим направлением для этого.
Окно IDA Pro c функцией WriteProcessMemory
Как видим из скриншота, по адресу 0x00401230 вызывается функция с аргументом lpbuffer, который передается вместе с такими параметрами, как размер буфера и идентификатор процесса (PID). Нетрудно догадаться, что эта функция отвечает за открытие дескриптора удаленного процесса и в конечном счете позволяет записать в него какие-то данные. Чтобы разобраться с этим, мы просто должны проследить, кто вызвал эту функцию, и таким образом выяснить, где находится шелл-код.
Шелл-код, расположенный по адресу 0x407030
Идем дальше. Так-так-так, вот, кажется, мы и нашли шелл-код по адресу @0x407030. Смотрим на него (скриншот ниже), а затем нажимаем клавишу C, чтобы преобразовать данные в байт-код.
Содержимое шелл-кода после преобразования в байт-код в IDA Pro
Глядя на найденный шелл-код на следующем скриншоте из дизассемблера, мы видим, что автор малвари использует трюк с вызовом, как это было описано чуть ранее, чтобы получить адрес шелл-кода. Анализируя коды, мы можем предположить, что шелл-код, начиная с адреса 0x407048, декодируется с помощью операции XOR с 0xE7.
Шелл-код использует вызов инструкций и преобразования XOR
Для анализа шелл-кода мы можем извлечь его из процесса и запустить, используя sctest.
Для этого на функции WriteProcessMemory ставим первый брейк-пойнт. До того как этот код будет записан в удаленный процесс, мы меняем первый байт шелл-кода со значения 0x407030 на другое значение 0xCC (наша точка останова). Запускаем под отладчиком iexplore.exe, после его запуска сработает точка останова, и шелл-код не будет внедрен в процесс.
При анализе шелл-кода мы встретим функции, которые отвечают за ручное импортирование некой следующей функции. Ты можешь поставить брейк-пойнт на CALL-инструкции в шелл-коде, чтобы отслеживать, где (по какому адресу) в памяти он находится.
Просмотр импорта в OllyDbg
Теперь мы ставим брейк-пойнт @ при инициации удаленной связи с параметром SockAddr, который передается шелл-коду, то есть, грубо говоря, передается IP-адрес машины, которую нужно подцепить. Ниже на скриншоте показаны преобразования машинных данных в понятные человеку параметры (IP-адрес и TCP-порт).
Структура SockAddr в окне OllyDbg
Преобразование Hex to IP Address
В итоге мы видим, что reverse shell (cmd.exe) подключается на инфицированной машине по адресу 192.168.200.2:13330. Шелл-код создает процесс командной оболочки CreateProcessA после подключения к удаленному IP.
А вот так выглядит терминал Linux, с которого мы атаковали удаленную машину. В выводе терминала мы видим получение приглашения cmd.exe на инфицированной машине с адресом в сети 192.168.200.2:13330.
Вывод терминала Linux после эксплуатации малвари на машине жертвы
Заключение
Вот и прошли мы вместе с тобой, мой друг, тернистый путь по дебрям malware-кода. Ты молодец, если дочитал эту статью до конца, и очень крут, если теперь сможешь повторить все лабораторные работы, не подглядывая. Наш цикл статей по реверсингу для начинающих на этом заканчивается. Безусловно, мы рассмотрели только самые базовые и ключевые аспекты анализа малвари, и осталось очень много тем, о которых стоило бы рассказать. Можно продолжать эту тему до бесконечности и, наверное, написать даже целую книгу. Надеюсь, тебе было интересно, ты узнал хотя бы что-то новое для себя и теперь представляешь, что такое работа вирусного аналитика. Если есть вопросы, жалобы или предложения — смело пиши в комменты :).