В этой статье будет рассказано о нескольких методах, используемых для размещения вредоноса в PE-файле.
Автор: Ege Balci
В этой статье будет рассказано о нескольких методах, используемых для размещения вредоноса в PE-файле. Чтобы понять суть излагаемого материала читателю необходимо быть знакомым с ассемблером для архитектуры x86 и отладчиками хотя бы на среднем уровне и понимать формат PE файлов. Этот документ был опубликован 8 декабря 2016 года на сайте pentest.blog, а также подготовлен в PDF формате для офлайнового чтения.
2. Введение
В настоящее время практически все специалисты по безопасности, пентестеры и аналитики вредоносов имеют дело с бэкдорами на ежедневной основе. Помещение трояна в систему или в конкретную программу – наиболее популярный способ для поддержки постоянного доступа к целевой машине. В большинстве статей рассказывается о методах для имплантирования вредоносов в 32 битные PE файлы, но поскольку PE-формат представляет собой модифицированную версию формата Unix COFF (Common Object File Format; Общий формат объектных файлов), логика, заложенная в основу этих техник, применима и ко всем остальным типам исполняемых файлов. Кроме того, незаметность встроенного вредоноса очень важна и напрямую влияет на длительность нахождения в системе. Методы, которые будут описаны в данной статье, направлены на снижение процента детектирования настолько насколько возможно. Перед дальнейшим чтением рекомендую ознакомиться с первой частью, где рассказывалось об обходе техник детектирования, внутреннем устройстве антивирусов и фундаментальных постулатах антидетектирования.
3. Терминология
Учебное проникновение:
Существуют группы, состоящие из светлых хакеров (или белых шляп), которые атакуют цифровую инфраструктуру организации, как если бы это делал реальный злоумышленник, для того, чтобы протестировать устойчивость системы к внешним угрозам (по-другому это называется пентест). Например, компания Microsoft регулярно проводит подобные кибер-учения. Плюсы от подобных мероприятий лежат на поверхности – в процессе могут найтись бреши и проблемы, которые можно заранее устранить. Кроме того, здесь могут обнаружиться пути попадания конфиденциальной информации наружу, схемы эксплуатации и другие «недокументированные» возможности системы.
Рандомизация размещения адресного пространства:
ASLR представляет собой технику безопасности, направленную на защиту от атак, связанных с переполнение буфера. Чтобы не дать злоумышленнику корректно перейти на определенную функцию внутри памяти, ASLR в случайном порядке выстраивает позиции ключевые областей информации в адресном пространстве процесса. Сюда же включается базовый адрес исполняемого файла и позиции стека, кучи и библиотек.
Code Cave (Пещера в коде):
Code Cave представляет собой кусок кода, который записывается другой программой в память стороннего процесса. Этот код может быть выполнен посредством создания удаленного потока внутри целевого процесса. Code cave зачастую является ссылкой на секцию скриптовых функций кода, куда можно инжектировать любые инструкции. Например, если в памяти скрипта 5 байт, и 3 байта используются, в оставшиеся 2 байта скрипта можно добавить внешний код.
Контрольная сумма:
Контрольная сумма представляет собой небольшую порцию информации из блока цифровых данных для обнаружения ошибок, которые могут появиться во время передачи или хранения файла. Обычно при помощи контрольной суммы проверяется установочный файл после получения с сервера. Между нами говоря, контрольные суммы обычно используются для верификации целостности данных, но не учитывают подлинность информации.
4. Основные методы
Все примеры из этой статьи будут продемонстрированы на базе исполняемого файла SSH-клиента с именем putty. Есть несколько причин для выбора именно этого приложения в качестве подопытного образца. Putty написан на C++ и использует множество библиотек и API-функций. Во-вторых, внедрение вредоноса в ssh-клиент привлекает меньше внимания, поскольку программа уже выполняет tcp-соединение, и, таким образом, будет проще избежать мониторинга со стороны системы безопасности.
Код бэкдора будет взят из шелл-кода, используемого при обратном TCP-соединении и написанного Стивеном Фивером (Stephen Fever) под meterpreter. Главная цель – инжектировать шелл-код в целевой PE-файл без порчи функциональности приложения. Инжектированный шелл-код будет запускаться в отдельном потоке и будет постоянно пытаться подсоединиться к обработчику. Вторая задача – во время выполнения всех этих манипуляций необходимо оставаться незаметным настолько, насколько возможно.
Общий подход внедрения вредоноса в PE-файл состоит из 4 шагов:
В каждом шаге есть свои нюансы, которые напрямую влияют на устойчивость, долговременность и незаметность встроенного вредоноса.
Нахождение доступного пространства – первый шаг к реализации нашей задачи. Чрезвычайно важно выбрать правильное место внутри PE-файла для внедрения бэкдора. Оценка угрозы со стороны зараженного файла сильно зависит от того, как вы решите эту задачу. Здесь существует два подхода.
По сравнению со вторым подходом здесь больше вероятность обнаружения. Хотя, с другой стороны, при добавлении новой секции у нас нет ограничений по пространству, и мы можем внедрить бэкдор любого размера.
При помощи дизассемблера или редактора LordPE PE-файл можно расширить при помощи добавления заголовка новой секции. На рисунке ниже показана таблица секций исполняемого файла putty. При помощи PE-редактора добавлена новая секция «NewSec» размером 1000 байт.
Рисунок 2: Таблица секций
Во время создания новой секции важно установить флаги на чтение/запись/выполнение, чтобы запустить шелл-код, когда PE-образ проецируется (map) в память.
Рисунок 3: Выставление флагов на чтение/запись/выполнение
После добавления заголовка секции необходимо адаптировать размер файла, что делается в шестнадцатеричном редакторе при помощи добавления пустых байтов размером с новую секцию в конец файла.
Рисунок 4: Добавление пустых байтов
После добавления новой пустой секции необходимо запустить исполняемый файл и проверить, есть ли ошибки. Если все прошло гладко, новая секция готова к модификации в отладчике.
Рисунок 5: Новая секция .NewSec успешно добавлена в исполняемый файл
Решение проблемы доступного пространства при помощи добавления новой секции имеет некоторые недостатки. Практически все антивирусы опознают нестандартные секции, и если, к тому же, там есть полный набор флагов на чтение/запись/выполнение, то эта ситуация выглядит еще более подозрительно.
Даже если мы просто добавим новую секцию с полными правами без бэкдора, некоторые антивирусы уже помечают исполняемый файл как вредоносный.
Рисунок 5: Результаты проверки исполняемого файла с новой пустой секцией
Во втором методе, направленном на решение проблемы доступного пространства, используются code cave’ы из целевого исполняемого файла. Практически все скомпилированные бинарные файлы имеют code cave’ы, которые могут быть использованы для внедрения вредоноса. Code cave по сравнению с новой секцией привлекает намного меньше внимания, поскольку в этом случае применяются уже существующие обычные секции. Дополнительный и не менее важный плюс заключается в том, что после внедрения вредоноса размер PE-файла не меняется. Однако эта техника имеет свои недостатки.
Количество и размер сode cave’ов варьируется от файла к файлу, но в целом общий размер меньше, чем при добавлении новой секции. При использовании code cave’ов код бэкдора следует уменьшать настолько, насколько возможно. Второй недостаток – набор флагов. Поскольку выполнение будет перенаправляться в сode cave, у секции должны быть права на выполнение. В случае с некоторыми шелл-кодами (которые сами себя кодируют или подвергают обфускации) требуется права на запись для того, чтобы выполнять изменения внутри секции.
Использование нескольких code cave’ов позволяет преодолеть ограничения с пространством. Здесь же дополнительный плюс в том, что вредонос собирается из отдельных кусков. Однако изменение привилегий секции будет выглядеть подозрительным. Существуют продвинутые методы для модификации привилегий у областей памяти во время выполнения приложения с целью избежания прямого изменения флагов секции, но поскольку эти техники требуют специализированного шелл-кода, кодирования и парсинга таблицы IAT, эта тема будет затронута в следующих статьях.
При помощи утилиты Cminer очень легко подсчитать все code cave’ы бинарного файла. Используем команду ./Cminer putty.exe 300 для поиска code cave’ов более 300 байт.
Рисунок 6: Поиск codecave’ов размером более 300 байт
В нашем случае найдено 5 хороших экземпляров для последующего использования. Стартовый адрес задает адрес виртуальный памяти (virtual memory address, VMA) у code cave при загрузке PE-файла в память. Смещение файла, измеряемого в байтах, – адрес местонахождения нужной области внутри PE-файла.
Рисунок 7: Параметры найденных Code cave’ов
По результатам поиска выяснилось, что большинство областей находятся внутри секции данных. Поскольку в этих секциях не установлен флаг на выполнения, потребуются изменения. Размер бэкдора около 400-500 байт, и области Cave 5 хватит за глаза. Стартовый адрес этой области нужно сохранить, а после изменения привилегий секции первый этап внедрения вредоноса можно считать завершенным. Теперь нужно перенаправить выполнение на нашу область.
6. Перехват потока выполнения
На этом этапе нужно перенаправить поток выполнения на код бэкдора при помощи модификации нужной инструкции в исполняемом файле. Здесь следует упомянуть важную деталь относительно выбора инструкции для изменения. Все бинарные инструкции имеют размер в байтах. Чтобы перейти к адресу расположения бэкдора потребуется длинный переход (long jump) размером, который использует 5 или 6 байт. При изменении бинарного файла инструкция, которая будет патчиться, должна быть того же размера, что и длинный переход, иначе предыдущая и последующая инструкция будут испорчены.
Очень важен выбор правильного места для перенаправления выполнения, поскольку если перенаправление будет осуществляться напрямую, неизбежно детектирование на этапе динамического анализа антивирусными продуктами.
Сокрытие внутри пользовательских функций:
Первое, что приходит в голову, для обхода песочницы и динамического анализа – отложенное выполнение шелл-кода или использование детектора песочницы, по результатам работы которого выполняется та или иная ветка алгоритма. С другой стороны, в большинстве случаев из-за ограничений по размерам мы не можем добавить лишние участки кода в PE-файл. Кроме того, реализация техник антидетектирвоания на низком уровне требует много времени и сил.
Этот метод использует функции, требующие действий пользователя. Перенаправление выполнения внутри подобных функций будет срабатывать только в том случае, если пользователь работает в программе. Если данная техника будет реализована корректно, успех практически гарантирован, и, к тому же, не будет увеличен размер бэкдора.
По нажатию кнопки «Open» из графической оболочки запускается функция проверки установленного IP-адреса.
Рисунок 8: Графическая оболочка для настройки putty
Если поле IP-адреса не пустое, и значение корректное, запускается функция для соединения с указанным IP-адресом.
Если клиент успешно создал ssh-сессию, появится новое окно для ввода имени пользователя и пароля.
Рисунок 9: Окно для ввода учетных сведений
Именно в этом месте произойдет перенаправление. Поскольку антивирусные продукты не настолько продвинуты для анализа такого рода механизмов, внедренный бэкдор, скорее всего, не будет обнаружен при помощи динамического анализа.
При помощи несложных методов реверс-инжиниринга, предназначенных для работы со строками и ссылками на строки, будет не сложно найти адрес функции соединения после того, как клиент установил соединение с назначенным IP-адресом. Строка «login as:», появляющаяся во всплывающем окне, поможет нам найти адрес функции соединения. В поисках ссылок на строки нам поможет IDA Pro.
Для нахождения строки «login as:» в IDA Pro открываем Views->Open Subviews->Strings on IDA
Рисунок 10: Ссылка на строку «login as:»
После нахождения нужной строки дважды кликаем и переходим местонахождению. Внутри секций данных IDA находит все перекрестные ссылки на строки. Для вывода всех перекрестных ссылок нажимаем комбинацию клавиш «Ctrl+X».
На рисунке ниже показана ссылка внутри функции, которая выводит строку «login as:»:
Рисунок 11: Ссылки внутри функции, которая выводит строку «login as:»
На рисунке ниже показана инструкция, которую мы будем изменять. После выполнения кода бэкдора, эта инструкция будет использоваться вновь.
Рисунок 11: Инструкция, которую мы будем менять
После изменения инструкции PUSH 467C7C на JMP 0x47A478 процесс перенаправления потока выполнения можно считать завершенным. Важно не забывать, что адрес следующей инструкции будет использоваться как адрес возврата после выполнения кода вредоноса. Следующий шаг – инжектирование кода бэкдора.
7. Инжектирование кода бэкдора
Первая мысль, которая приходит в голову при внедрении бэкдора, - сохранение регистров перед выполнением вредоносного кода. Каждое значение внутри регистров – чрезвычайно важно для выполнения программы. Поместив инструкции PUSHAD и PUSHFD в начале code cave, мы сможем сохранить все регистры и флаги регистров внутри стека. Эти значения будут возвращены после выполнения кода вредоноса, и программа продолжит выполнение без каких-либо проблем.
Рисунок 12: Помещение инструкций PUSHAD и PUSHFD перед code cave
Как было упомянуто ранее, наш бэкдор представляет собой обратный tcp-шелл-код под meterpreter, взятый из проекта metasploit. Однако потребуется некоторые изменения внутри шелл-кода. Обычно обратный tcp-шелл-код пытается подсоединиться к обработчику некоторое количество раз, и если соединиться не удалось, процесс закрывается посредством вызова API-функции ExitProcess.
Рисунок 13: Участок шелл-кода, отвечающий за соединение с обработчиком
Проблема заключается в том, что если соединиться с обработчиком не получится, выполнение клиента putty будет остановлено. После внесения небольших изменений после неудачи шелл-код будет вновь пытаться соединиться с обработчиком. К тому же, немного уменьшится размер шелл-кода.
Рисунок 14: Модифицированная версия участка кода, отвечающего за соединение с обработчиком
После внесения изменений внутри ассемблерного кода выполняем компиляцию при помощи команды nasm -f bin stager_reverse_tcp_nx.asm. Теперь обратный tcp-шелл-код готов к употреблению, но пока еще не помещен в правильное место. Наша цель – осуществить выполнение в отдельном потоке, для создания которого потребуется отдельный шелл-код, выполняющий вызов API-функции CreateThread. Функция будет указывать на первоначальный обратный tcp-шелл-код. Код для создания потоков из проекта metasploit также написан Стивеном Фивером.
Рисунок 15: Шелл-код для создания отдельного потока
После помещения байтов шелл-кода внутрь файла createthread.asm в шестнадцатеричном формате, как показано на рисунке выше, выполняем компиляцию при помощи команды nasm -f bin createthread.asm. Теперь шелл-код готов для внедрения в code cave, но перед вставкой следует выполнить кодирования для того, чтобы обойти статический/сигнатурный анализ антивируса. Поскольку все кодировщики из проекта metasploit известны большинству антивирусов, рекомендуется использовать нестандартное кодирование. В этой статье не будет рассказано о создании нестандартных кодировщиков, но можно обойтись сочетанием нескольких кодировщиков из проекта metasploit. После каждого акта кодирования загружаем шелл-код в проект Virus Total в «сыром» формате и проверяем результаты проверки. Комбинируем кодировщики до тех пор, пока шелл-код станет полностью незаметным (или можно подождать следующей статьи).
После правильного кодирования шелл-код готов к внедрению внутрь code cave. Выбираем инструкцию, которая следует за инструкцией PUSHFD, и нажимаем Ctrl+E в отладчике Immunity Debugger. Шелл-код будет вставлен в шестнадцатеричном формате.
Рисунок 16: Вставка шелл-кода
Получить закодированный шелл-код в шестнадцатеричном формате можно двумя способами: вывести на печать при помощи команды xxd -ps createthread или открыть и скопировать в шестнадцатеричном редакторе. При копировании шестнадцатеричных значений в отладчик Immunity Debugger не забывайте об ограничениях на копируемые байты, которые присутствуют при вставке кода. Необходимо запомнить последние два вставленных байта, и после нажатия на кнопку OK нужно сделать повторное копирование последующих участков. Когда шелл-код полностью вставлен в code cave, операцию по внедрению можно считать завершенной.
8. Восстановление потока выполнения
После создания потока выполнения бэкдора необходимо возобновить выполнение программы. То есть регистр EIP должен указывать на функцию, которая перенаправила выполнение на code cave. Однако перед переходом на эту функцию следует восстановить ранее сохраненные регистры.
Рисунок 17: Инструкции для восстановления первоначального состояния регистров
Поместив инструкции POPFD и POPAD в конец шелл-кода, мы восстановим все ранее сохраненные регистры из стека в том же порядке. После восстановления регистров необходимо вспомнить еще об одном нюансе. При перехвате потока выполнения инструкция PUSH 467C7C была заменена на инструкцию JMP 0x47A478 для того, чтобы перенаправить выполнение на code cave. При помещении инструкции PUSH 467C7C в конец кода, перехваченная инструкция также восстановится. Теперь настало время вернуться к функции, которая перенаправляла выполнение к code cave при помощи вставки инструкции JMP 0x41CB73. Конец результирующего кода должен выглядеть, как показано на рисунке ниже.
Рисунок 18: Завершающие изменения в конце кода
Затем выделяем все измененные и вставленные инструкции, нажимаем правую кнопку мыши и копируем в исполняемый файл. Эта операция должна быть проделана с каждой модифицированной инструкцией. Кода все инструкции скопированы и сохранены в файл, закрываем отладчик и тестируем наше творчество. Если выполнение проходит без ошибок, бэкдор готов к употреблению.
В завершении рекомендуется изменить контрольную сумму получившегося файла, чтобы сохранить аутентичность и не вызывать лишние подозрения.
Рисунок 19: Изменение контрольной суммы в редакторе PE-файлов
9. Заключение
Если все описанные методы использованы правильно, готовый бэкдор будет полностью незаметным. В заключение будут приведены контрмеры для защиты от описанных ранее техник. Это методы будут полезны администраторам, аналитикам вредоносов и разработчикам антивирусов.
Когда речь заходит о зараженных файлах, в первую очередь следует думать о детектировании аномалий, связанных с привилегиями секций. По умолчанию компиляторы никогда не устанавливают полные права на секции, если только программист не выставил специальные настройки. Особенно не должны иметь привилегий на выполнение секции данных: .data и.rdata. Кроме того, секции кода (например, .text) не должны иметь прав на запись. Подобные аномалии, связанные с изменением привилегий, следует рассматривать как подозрительное поведение.
Если программист не вносит изменения в конфигурацию, компиляторы обычно создают 5-6 стандартных типов секций. Во все продукты, связанные с безопасностью, должен быть внедрен механизм обнаружения нестандартных и подозрительных секций. Данный механизм должен следить за энтропией и выравниваем данных внутри секции. Если секция содержит высокую энтропию и нестандартно упорядоченную информацию, это еще один подозрительный признак.
Несмотря на то, что эта техника стара как мир, метод является очень эффективным при проверке файлов, загружаемых из интернета. Проверка сигнатуры sha1 – один из наиболее надежных способов избежать заражения системы.
Если есть различие между контрольной сумму внутри заголовка образа и текущей контрольной суммой файла, это означает, что файл был изменен. В системы безопасности следует внедрить механизм проверки аутентичности файла при помощи сравнения текущей контрольной суммы и контрольной суммы заголовка образа.
Рисунок 20: Результаты проверки файла с внедренным бэкдором
10. Ссылки
http://NoDistribute.com/result/image/Ye0pnGHXiWvSVErkLfTblmAUQ.png
https://github.com/secretsquirrel/the-backdoor-factory
https://www.shellterproject.com/
https://en.wikipedia.org/wiki/Red_team
https://en.wikipedia.org/wiki/Address_space_layout_randomization
5778 К? Пф! У нас градус знаний зашкаливает!