Антивирус — крайне полезная штука, но только не тогда, когда тебе нужно остаться незамеченным в атакуемой сети. Сегодня мы поговорим о том, как при пентесте можно обмануть антивирусные программы и избежать обнаружения в скомпрометированной системе.
Эта статья — продолжение цикла публикаций, посвященных постэксплуатации. В предыдущих сериях:
Вообще, задача обхода антивируса может возникнуть в двух случаях:
Многие полезные инструменты для обхода антивируса можно сделать с помощью того же Meterpreter, но для начала его следует как минимум запустить. В каждом рассматриваемом способе именно запуск Meterpreter и будет для нас конечной целью. Ведь данное средство обладает всеми необходимыми возможностями, а при желании может использоваться и для запуска другого специализированного ПО прямо в оперативной памяти. А все, что происходит в оперативной памяти, почти недостижимо для средств защиты, поскольку детальный и постоянный анализ памяти влечет за собой колоссальные накладные расходы, на которые антивирусы пойти не могут.
Для того чтобы эффективно обойти антивирус, нам потребуется мыслить как антивирус, пытаться предугадать его логику. Антивирус для нас, безусловно, будет черным ящиком. Мы не знаем в подробностях, как он устроен, но, опираясь на его поведение в разных ситуациях, можем заключить, что он тщательно отслеживает источник загрузки исполняемого файла, а также следит, запускался ли он до этого, и если запускался, то сколько раз. Повторные запуски будут происходить уже под меньшим «надзором».
По большому счету мы имеем дело с двумя механизмами защиты:
Я предлагаю сосредоточиться именно на сигнатурном движке, обхода которого для большинства случаев достаточно. В статье не будет прямых упоминаний конкретных названий антивирусов, чтобы не создавать никому рекламы или антирекламы. В то же время мы не станем «затачиваться» под конкретный антивирус. Результаты будем проверять именно на работающем антивирусе, при этом попробуем использовать некий универсальный способ, чтобы каждый раз не придумывать все новые методы обхода. В каждом случае целью будет тайком протащить на скомпрометированную машину Meterpreter, который позволит нам исполнить в памяти что угодно, запустить весь имеющийся в нашем распоряжении хакерский арсенал.
Если же в скомпрометированной системе требуется развернуть целый «плацдарм» в виде Metasploit и аналогичных хакерских тулз, то можно укрыться за виртуализацией.
shellcode_inject.exe не содержит угроз
В свою очередь, наш shellcode выглядит еще более безобидно, если мы преобразуем его в печатные символы.
Cоздание автономного (не staged) шелл‑кода. Выглядит безобидно
Глядя на содержимое meter.txt, я бы скорее решил, что это строка в Base64, чем шелл‑код.
Стоит отметить, что мы использовали шелл‑код meterpreter_reverse_tcp, а не meterpreter/reverse_tcp. Это автономный код, который содержит в себе все функции Meterpreter, он ничего не будет скачивать по сети, следовательно, шансов спалиться у нас будет меньше. Но вот связка shellcode_inject.exe и meter.txt уже представляет опасность. Давай посмотрим, сможет ли антивирус распознать угрозу?
Инжект Meterpreter
Обрати внимание: мы использовали для инжекта кода системный процесс, он сразу работает в контексте System. И похоже, что наш подопытный антивирус хоть в конце и ругнулся на shellcode_inject.exe, но все же пропустил данный трюк.
Выполнение вредоносного кода в обход антивируса
Запустив что‑то вроде Meterpreter, атакующий получит возможность выполнить полезную нагрузку прямо в памяти, минуя тем самым HDD.
Запуск вредоносного ПО через Meterpreter в памяти
Много лет этот простой трюк выручал меня. Сработал он и на этот раз.
Графическое представление расположения функций и пустот между ними
Что представляет собой каждая такая «пустота»?
Code cave
Так, если мы более точно (сигнатурно) определим расположение всех пустот, то получим примерно следующую картину в исполняемом файле.
Поиск кодовых пустот в исполняемом файле
Визуальное расположение пустот по всему файлу
В этом примере мы поискали только 12-байтные пустоты, так что реальное их количество будет гораздо большим. Пустот хоть и немало, но их явно недостаточно для размещения полноценной программы. Поэтому данный способ годится только для вставки шелл‑кодов, размер которых редко превышает 1 Кбайт.
Давай посмотрим, сможем ли мы разложить небольшой многоступенчатый Windows/Meterpreter/reverse_tcp шелл‑код по этим пустотам. Размер code cave редко превышает 16 байт, так что нам потребуется разбивать шелл‑код сильнее, чем по базовым блокам. Следовательно, придется вставлять еще и дополнительные jmp-инструкции для их связи и корректировать адреса условных переходов. На деле это достаточно рутинная операция.
Фрагментация и вставка шелл‑кода в code caves
В результате наш шелл‑код размером в 354 байта был разбит на 62 кусочка и помещен в рандомные пустоты между функциями.
Исходный исполняемый файл — пустота между функциями из‑за выравнивания
Модифицированный исполняемый файл — зараженная пустота между функциями
По идее, такой подход должен дать нам полиморфизм, так как каждый раз шелл‑код будет помещаться в случайные пустоты по две‑три инструкции (это называется умным словом «пермутация»). Даже на уровне трассы исполнения код будет обфусцирован из‑за достаточно большого количества инструкций jmp между фрагментами.
Оригинальная трасса исполнения шелл‑кода
Обфусцированная трасса исполнения шелл‑кода
C помощью этого способа мы можем обойти таким образом много «простых» антивирусов.
Выполнение вредоносного шелл‑кода в обход антивируса
Однако серьезные антивирусные продукты таким трюком все же не проведешь.
Xor с ключом 0x77 указанных адресов и размеров
Теперь спрячем информацию о версии.
Шифрование ресурсов исполняемого файла
Секция .rdata содержит практически все палевные строки. Но вот беда, на нее проецируются директории таблиц импорта и отложенного импорта, которые трогать нельзя, иначе файл не запустится. Поэтому просто найдем в данной секции область, где главным образом содержатся строки, и зашифруем ее.
Поиск области, содержащей строки, и ее шифрование
Поскольку мы все зашифровали в исполняемом файле, то в момент запуска его смещения в коде не будут правильно скорректированы в соответствии с адресом размещения. Поэтому еще придется отключить ASLR:
Теперь самое время проверить, что мы все спрятали, и наш mimikatz больше не вызывает подозрений.
Все вредоносное содержимое успешно скрыто
Отлично. Только пока наш файл неработоспособен, так как в нем все зашифровано. Перед дальнейшими действиями рекомендую попробовать запустить файл в отладчике, чтобы убедиться, что структуры PE-формата не повреждены и файл валиден.
Теперь нам потребуется написать небольшой машинный код для расшифровки. И, что важно, ключ будет задаваться во время исполнения, то есть нигде в коде он не будет сохранен. Следовательно, запуск приложения в песочнице антивируса не должен выявить угрозы.
Наш xor_stub.asm будет сохранять начальное состояние и добавлять права на запись в зашифрованные секции.
Изменение permissions зашифрованных секций
Здесь и далее не забудь изменить адреса WinAPI-функций на свои значения. Теперь мы скорректируем точку входа, так как в нее чуть позже будет вставлен jump на данный код.
Восстановление инструкций точки входа
Запросим у пользователя ключ для расшифровки и выполним де-xor всех зашифрованных областей.
Запрос ключа и расшифровка им заксоренных областей
Наконец мы восстанавливаем начальное состояние, корректируем стек и перемещаемся в entry point.
Возврат на точку входа
Самое время добавить пустую секцию r-x в mimikatz, куда мы разместим наш xor_stub.
Добавление секции, куда будет вставлен код расшифровки
Теперь скомпилируем данный ассемблерный код и вставим его в только что созданную секцию.
Компиляция и вставка кода для расшифровки
В конце не забудем из entry point сделать jump на наш код.
Передача управления на код расшифровки
Готово. Запускаем и вводим ключ, которым мы шифровали, — в моем случае это символ w (0x77).
Выполнение вредоносного кода в обход антивируса
Вот и все. Немного автоматизировав данный процесс, попробуем запустить Meterpreter.
Автоматическое шифрование описанным способом
Запускаем и вводим ключ w.
Запуск вредоносного кода
И получаем тот же эффект.
Запуск вредоносного кода в обход антивируса
Думаю, антивирус главным образом заточен на отлов программ (на HDD) и шелл‑кодов (по сети) с известными сигнатурами или на эксплуатацию популярных уязвимостей. Но что, если уязвимость еще неизвестна для антивируса?
В этом случае используется достаточно необычная техника, которая строится на принципе внедрения уязвимости (buffer overflow) и, тем самым, неочевидном исполнении произвольного кода. По сути, это еще один вариант рефлективного исполнения кода, то есть когда код присутствует исключительно в RAM, минуя HDD. Но в нашем случае мы еще и скрываем точку входа во вредоносный код.
Антивирус, как и любое другое ПО, вряд ли способен определить статическим анализатором, что в программе содержится уязвимость и будет исполнен произвольный код. Машины пока плохо справляются с этим, и не думаю, что ситуация сильно изменится в ближайшем будущем. Но сможет ли антивирус увидеть процесс в динамике и успеть среагировать?
Чтобы проверить это, нам нужно написать и запустить простенький сетевой сервис, содержащий придуманную нами 0-day-уязвимость (buffer overflow на стеке) и проэксплуатировать ее. Чтобы все работало еще и в новых версиях Windows, придется обойти DEP. Но это не проблема, если мы можем добавить нужные нам ROP-gadgets в программу.
Не будем глубоко вдаваться в детали buffer overflow и ROP-chains, так как это выходит за рамки данной статьи. Вместо этого возьмем готовое решение. Компилируем сервис обязательно без поддержки ASLR (и можно без DEP):
Наш сетевой сервис будет работать в режимах listen и reverse connect. А все данные будут передаваться в зашифрованном виде, чтобы не спалиться на сигнатурном анализаторе. И это очень важно, поскольку некоторые антивирусы настолько «не любят» Meterpreter, что даже простая его отправка в любой открытый порт спровоцирует неминуемый алерт и последующий бан IP-адреса атакующего.
Полученный исполняемый файл технически не является вредоносным, ведь он содержит в себе лишь ошибку при работе с памятью. В противном случае любая программа может считаться вредоносной, поскольку потенциально она тоже может включать ошибки.
Запуск сервиса с заложенной нами buffer overflow
Наш уязвимый сервис успешно запущен и ждет входящих данных. Создаем payload и запускаем эксплоит с ним.
Эксплуатация нашего buffer overflow
В итоге уязвимый сервис принимает наши данные и в результате заложенной ошибки при работе с памятью непроизвольно запускает код payload.
Переполнение буфера в действии
Эта полезная нагрузка открывает нам сессию Meterpreter.
Выполнение вредоносного кода в обход антивируса
И все это безобразие происходит при работающем антивирусе. Однако было замечено, что при использовании некоторых антивирусов все еще срабатывает защита. Давай порассуждаем, почему. Мы вроде бы смогли внедрить код крайне неожиданным способом — через buffer overflow. И в то же самое время по сети мы не передавали код в открытом виде, так что сигнатурные движки не сработали бы. Но перед непосредственным исполнением мы получаем в памяти тот самый машинный код. И тут, по‑видимому, антивирус и ловит нас, узнавая до боли знакомый Meterpreter.
Антивирус не доверяет exe-файлу, скачанному неизвестно откуда и запущенному первый раз. Он эмулирует выполнение кода и достаточно глубоко анализирует его, возможно даже на каждой инструкции. За это приходится платить производительностью, и антивирус не может позволить себе делать так для всех процессов. Поэтому процессы, уже прошедшие проверку на этапе запуска (например, системные компоненты или программы с известной контрольной суммой), работают под меньшим надзором. Именно в них мы и внедрим нашу уязвимость.
Для максимальной переносимости я использую 32-битные программы, поэтому внедрять уязвимость нам придется в 32-разрядные процессы. Можно взять любой уже запущенный или запустить самому. На 64-битной Windows мы всегда можем найти 32-битные системные программы в c:\windows\syswow64.
Запуск 32-битного процесса
Теперь в тот или иной 32-битный процесс мы можем внедрить уязвимость, просто заинжектив туда нашу библиотеку.
Инжект библиотеки в только что запущенный 32-битный системный процесс
Наша DLL без ASLR успешно загружена по стандартному адресу.
Уязвимая DLL загружена
И теперь целевой процесс с занесенным buffer overflow готов получать данные по сети.
Уязвимый код начал работать в контексте легитимного процесса
Поскольку наш уязвимый модуль загрузился по адресу 0x10000000 (это дефолтный адрес для не ASLR-библиотек), нужно слегка скорректировать код эксплоита.
Небольшая корректировка кода эксплоита
Время запустить сам эксплоит.
Запуск эксплоита
В контексте легитимного процесса происходит overflow.
Переполнение буфера в легитимном процессе в действии
И мы исполняем «вредоносный» код в обход антивируса.
Выполнение вредоносного кода в обход антивируса
На самом деле мы могли использовать еще более хитрый способ — просто подменить ключевые инструкции в той или иной точке памяти процесса, где происходит обработка пользовательского ввода, и внедрить тем самым туда уязвимость (как бы сделать антипатч). А положив пару‑тройку удобных ROP-гаджетов в code caves, сделать ее еще и пригодной к эксплуатации. Но пока что этого даже не требуется.
Техника сокрытия выполнения кода через buffer overflow не нова, хоть и достаточно малоизвестна. В данном примере был использован самый тривиальный пример buffer overflow на стеке, и он принес нам успех. Но существуют куда более хитрые ошибки работы с памятью, приводящие к RCE (скрытому исполнению): use after free, double free, overflow in heap, format strings и так далее. Это открывает практически неисчерпаемый потенциал для приемов обхода антивирусных программ.
Автор @s0i37
источник: xakep.ru
Эта статья — продолжение цикла публикаций, посвященных постэксплуатации. В предыдущих сериях:
- Шпаргалка по persistence. Как надежно прописаться на хосте или выявить факт компрометации;
- Кунг‑фу pivoting. Выжимаем максимум из постэксплуатации;
- Гид по Lateral. Изучаем удаленное исполнение кода в Windows со всех сторон;
Вообще, задача обхода антивируса может возникнуть в двух случаях:
- при атаке. Тут полезную нагрузку запускает либо уязвимое приложение, либо, что чаще, пользователь (социальная инженерия). Главным образом это будет некое средство закрепления и обеспечения постоянного присутствия. Самое важное — не спалиться;
- при постэксплуатации. В этом случае мы сами запускаем программу на скомпрометированной системе. Это может быть сниффер, средство повышения привилегий или просто какой‑то хакерский софт, используемый для продвижения по сети. И при этом для нас важнее запустить программу, даже если это получилось не с первой попытки и антивирус выкинул несколько алертов.
Многие полезные инструменты для обхода антивируса можно сделать с помощью того же Meterpreter, но для начала его следует как минимум запустить. В каждом рассматриваемом способе именно запуск Meterpreter и будет для нас конечной целью. Ведь данное средство обладает всеми необходимыми возможностями, а при желании может использоваться и для запуска другого специализированного ПО прямо в оперативной памяти. А все, что происходит в оперативной памяти, почти недостижимо для средств защиты, поскольку детальный и постоянный анализ памяти влечет за собой колоссальные накладные расходы, на которые антивирусы пойти не могут.
Для того чтобы эффективно обойти антивирус, нам потребуется мыслить как антивирус, пытаться предугадать его логику. Антивирус для нас, безусловно, будет черным ящиком. Мы не знаем в подробностях, как он устроен, но, опираясь на его поведение в разных ситуациях, можем заключить, что он тщательно отслеживает источник загрузки исполняемого файла, а также следит, запускался ли он до этого, и если запускался, то сколько раз. Повторные запуски будут происходить уже под меньшим «надзором».
По большому счету мы имеем дело с двумя механизмами защиты:
- сигнатурным;
- эвристическим (поведенческим).
- i686-w64-mingw32-gcc — 11/68 детектов;
- msvc — 2/64 детекта;
- win-gcc — 0 детектов.
Я предлагаю сосредоточиться именно на сигнатурном движке, обхода которого для большинства случаев достаточно. В статье не будет прямых упоминаний конкретных названий антивирусов, чтобы не создавать никому рекламы или антирекламы. В то же время мы не станем «затачиваться» под конкретный антивирус. Результаты будем проверять именно на работающем антивирусе, при этом попробуем использовать некий универсальный способ, чтобы каждый раз не придумывать все новые методы обхода. В каждом случае целью будет тайком протащить на скомпрометированную машину Meterpreter, который позволит нам исполнить в памяти что угодно, запустить весь имеющийся в нашем распоряжении хакерский арсенал.
LEGAL
Лучший бой — это тот, которого удалось избежать. Поэтому в борьбе с антивирусами часто используются легальные средства. Да, они не могут предоставить многие «продвинутые штуки», но необходимый минимум в виде reverse shell при persistence и lateral movement, а также встроенный прокси‑сервер и гибкую систему редиректа трафика при pivoting они реализовать могут. И это замечательные, всем известные утилиты — nc.exe, ncat.exe, socat.exe, plink.exe. Примеры их использования были описаны в моих прошлых статьях. Блеки же и вовсе порою используют обычные средства облачного удаленного администрирования вроде RMS.Если же в скомпрометированной системе требуется развернуть целый «плацдарм» в виде Metasploit и аналогичных хакерских тулз, то можно укрыться за виртуализацией.
SHELLCODE INJECTING
Техника встраивания кода в уже запущенные, а значит, прошедшие проверку процессы широко известна. Идея состоит в том, что мы имеем отдельную программу shellcode_inject.exe и сам shellcode в разных файлах. Антивирусу сложнее распознать угрозу, если она раскидана по нескольким файлам, тем более по отдельности эти файлы не представляют угрозы.
shellcode_inject.exe не содержит угроз
В свою очередь, наш shellcode выглядит еще более безобидно, если мы преобразуем его в печатные символы.
Cоздание автономного (не staged) шелл‑кода. Выглядит безобидно
Глядя на содержимое meter.txt, я бы скорее решил, что это строка в Base64, чем шелл‑код.
Стоит отметить, что мы использовали шелл‑код meterpreter_reverse_tcp, а не meterpreter/reverse_tcp. Это автономный код, который содержит в себе все функции Meterpreter, он ничего не будет скачивать по сети, следовательно, шансов спалиться у нас будет меньше. Но вот связка shellcode_inject.exe и meter.txt уже представляет опасность. Давай посмотрим, сможет ли антивирус распознать угрозу?
Инжект Meterpreter
Обрати внимание: мы использовали для инжекта кода системный процесс, он сразу работает в контексте System. И похоже, что наш подопытный антивирус хоть в конце и ругнулся на shellcode_inject.exe, но все же пропустил данный трюк.
Выполнение вредоносного кода в обход антивируса
Запустив что‑то вроде Meterpreter, атакующий получит возможность выполнить полезную нагрузку прямо в памяти, минуя тем самым HDD.
Запуск вредоносного ПО через Meterpreter в памяти
Много лет этот простой трюк выручал меня. Сработал он и на этот раз.
CODE CAVES
Каждый exe-файл (PE-формат) содержит код. При этом весь код оформлен в виде набора функций. В свою очередь, функции при компиляции размещаются не одна за другой вплотную, а с некоторым выравниванием (16 байт). Еще большие пустоты возникают из‑за выравнивания между секциями (4096 байт). И благодаря всем этим выравниваниям создается множество небольших «кодовых пустот» (code caves), которые доступны для записи в них кода. Тут все очень сильно зависит от компилятора. Но для большинства PE-файлов, а нас главным образом интересует ОС Windows, картина может выглядеть примерно так, как показано на следующем скриншоте.
Графическое представление расположения функций и пустот между ними
Что представляет собой каждая такая «пустота»?
Code cave
Так, если мы более точно (сигнатурно) определим расположение всех пустот, то получим примерно следующую картину в исполняемом файле.
Поиск кодовых пустот в исполняемом файле
Визуальное расположение пустот по всему файлу
В этом примере мы поискали только 12-байтные пустоты, так что реальное их количество будет гораздо большим. Пустот хоть и немало, но их явно недостаточно для размещения полноценной программы. Поэтому данный способ годится только для вставки шелл‑кодов, размер которых редко превышает 1 Кбайт.
Давай посмотрим, сможем ли мы разложить небольшой многоступенчатый Windows/Meterpreter/reverse_tcp шелл‑код по этим пустотам. Размер code cave редко превышает 16 байт, так что нам потребуется разбивать шелл‑код сильнее, чем по базовым блокам. Следовательно, придется вставлять еще и дополнительные jmp-инструкции для их связи и корректировать адреса условных переходов. На деле это достаточно рутинная операция.
Фрагментация и вставка шелл‑кода в code caves
В результате наш шелл‑код размером в 354 байта был разбит на 62 кусочка и помещен в рандомные пустоты между функциями.
Исходный исполняемый файл — пустота между функциями из‑за выравнивания
Модифицированный исполняемый файл — зараженная пустота между функциями
По идее, такой подход должен дать нам полиморфизм, так как каждый раз шелл‑код будет помещаться в случайные пустоты по две‑три инструкции (это называется умным словом «пермутация»). Даже на уровне трассы исполнения код будет обфусцирован из‑за достаточно большого количества инструкций jmp между фрагментами.
Оригинальная трасса исполнения шелл‑кода
Обфусцированная трасса исполнения шелл‑кода
C помощью этого способа мы можем обойти таким образом много «простых» антивирусов.
Выполнение вредоносного шелл‑кода в обход антивируса
Однако серьезные антивирусные продукты таким трюком все же не проведешь.
CRYPT
Как ни странно, классический xor исполняемого файла с динамическим ключом все еще успешно работает против даже самых грозных антивирусов. Для примера возьмем какой‑нибудь очень палевный исполняемый файл и закриптуем простым xor все, что только можно. Крипт секций .text и .data выглядит примерно так.
Xor с ключом 0x77 указанных адресов и размеров
Теперь спрячем информацию о версии.
Шифрование ресурсов исполняемого файла
Секция .rdata содержит практически все палевные строки. Но вот беда, на нее проецируются директории таблиц импорта и отложенного импорта, которые трогать нельзя, иначе файл не запустится. Поэтому просто найдем в данной секции область, где главным образом содержатся строки, и зашифруем ее.
Поиск области, содержащей строки, и ее шифрование
Поскольку мы все зашифровали в исполняемом файле, то в момент запуска его смещения в коде не будут правильно скорректированы в соответствии с адресом размещения. Поэтому еще придется отключить ASLR:
Код:
PE->NT headers->Optional header->DllCharacteristics |=0x40
Все вредоносное содержимое успешно скрыто
Отлично. Только пока наш файл неработоспособен, так как в нем все зашифровано. Перед дальнейшими действиями рекомендую попробовать запустить файл в отладчике, чтобы убедиться, что структуры PE-формата не повреждены и файл валиден.
Теперь нам потребуется написать небольшой машинный код для расшифровки. И, что важно, ключ будет задаваться во время исполнения, то есть нигде в коде он не будет сохранен. Следовательно, запуск приложения в песочнице антивируса не должен выявить угрозы.
Наш xor_stub.asm будет сохранять начальное состояние и добавлять права на запись в зашифрованные секции.
Изменение permissions зашифрованных секций
Здесь и далее не забудь изменить адреса WinAPI-функций на свои значения. Теперь мы скорректируем точку входа, так как в нее чуть позже будет вставлен jump на данный код.
Восстановление инструкций точки входа
Запросим у пользователя ключ для расшифровки и выполним де-xor всех зашифрованных областей.
Запрос ключа и расшифровка им заксоренных областей
Наконец мы восстанавливаем начальное состояние, корректируем стек и перемещаемся в entry point.
Возврат на точку входа
Самое время добавить пустую секцию r-x в mimikatz, куда мы разместим наш xor_stub.
Добавление секции, куда будет вставлен код расшифровки
Теперь скомпилируем данный ассемблерный код и вставим его в только что созданную секцию.
Компиляция и вставка кода для расшифровки
В конце не забудем из entry point сделать jump на наш код.
Передача управления на код расшифровки
Готово. Запускаем и вводим ключ, которым мы шифровали, — в моем случае это символ w (0x77).
Выполнение вредоносного кода в обход антивируса
Вот и все. Немного автоматизировав данный процесс, попробуем запустить Meterpreter.
Автоматическое шифрование описанным способом
Запускаем и вводим ключ w.
Запуск вредоносного кода
И получаем тот же эффект.
Запуск вредоносного кода в обход антивируса
VULN INJECT (SPAWN)
Мне хорошо запомнился один давний случай. Я никак не мог открыть сессию Meterpreter на victim из‑за антивируса, и вместо этого мне каждый раз приходилось заново эксплуатировать старую добрую MS08-067, запуская Meterpreter сразу в памяти. Антивирус почему‑то не мог помешать этому.Думаю, антивирус главным образом заточен на отлов программ (на HDD) и шелл‑кодов (по сети) с известными сигнатурами или на эксплуатацию популярных уязвимостей. Но что, если уязвимость еще неизвестна для антивируса?
В этом случае используется достаточно необычная техника, которая строится на принципе внедрения уязвимости (buffer overflow) и, тем самым, неочевидном исполнении произвольного кода. По сути, это еще один вариант рефлективного исполнения кода, то есть когда код присутствует исключительно в RAM, минуя HDD. Но в нашем случае мы еще и скрываем точку входа во вредоносный код.
Антивирус, как и любое другое ПО, вряд ли способен определить статическим анализатором, что в программе содержится уязвимость и будет исполнен произвольный код. Машины пока плохо справляются с этим, и не думаю, что ситуация сильно изменится в ближайшем будущем. Но сможет ли антивирус увидеть процесс в динамике и успеть среагировать?
Чтобы проверить это, нам нужно написать и запустить простенький сетевой сервис, содержащий придуманную нами 0-day-уязвимость (buffer overflow на стеке) и проэксплуатировать ее. Чтобы все работало еще и в новых версиях Windows, придется обойти DEP. Но это не проблема, если мы можем добавить нужные нам ROP-gadgets в программу.
Не будем глубоко вдаваться в детали buffer overflow и ROP-chains, так как это выходит за рамки данной статьи. Вместо этого возьмем готовое решение. Компилируем сервис обязательно без поддержки ASLR (и можно без DEP):
Код:
cl.exe /c vuln_rop.c
link.exe /out:vuln_rop.exe vuln_rop.obj /nxcompat:no /fixed
Полученный исполняемый файл технически не является вредоносным, ведь он содержит в себе лишь ошибку при работе с памятью. В противном случае любая программа может считаться вредоносной, поскольку потенциально она тоже может включать ошибки.
Запуск сервиса с заложенной нами buffer overflow
Наш уязвимый сервис успешно запущен и ждет входящих данных. Создаем payload и запускаем эксплоит с ним.
Эксплуатация нашего buffer overflow
В итоге уязвимый сервис принимает наши данные и в результате заложенной ошибки при работе с памятью непроизвольно запускает код payload.
Переполнение буфера в действии
Эта полезная нагрузка открывает нам сессию Meterpreter.
Выполнение вредоносного кода в обход антивируса
И все это безобразие происходит при работающем антивирусе. Однако было замечено, что при использовании некоторых антивирусов все еще срабатывает защита. Давай порассуждаем, почему. Мы вроде бы смогли внедрить код крайне неожиданным способом — через buffer overflow. И в то же самое время по сети мы не передавали код в открытом виде, так что сигнатурные движки не сработали бы. Но перед непосредственным исполнением мы получаем в памяти тот самый машинный код. И тут, по‑видимому, антивирус и ловит нас, узнавая до боли знакомый Meterpreter.
Антивирус не доверяет exe-файлу, скачанному неизвестно откуда и запущенному первый раз. Он эмулирует выполнение кода и достаточно глубоко анализирует его, возможно даже на каждой инструкции. За это приходится платить производительностью, и антивирус не может позволить себе делать так для всех процессов. Поэтому процессы, уже прошедшие проверку на этапе запуска (например, системные компоненты или программы с известной контрольной суммой), работают под меньшим надзором. Именно в них мы и внедрим нашу уязвимость.
VULN INJECT (ATTACH)
Самый простой и удобный способ выполнить код в чужом адресном пространстве (процессе) — инжект библиотеки. Благо DLL мало чем отличается от EXE и мы можем перекомпилировать наш уязвимый сервис в форм‑фактор библиотеки, просто изменив main() на DllMain():
Код:
cl.exe /c vuln_rop.c
link.exe vuln_rop.obj /out:vuln_rop.dll /dll /nxcompat:no /fixed
Запуск 32-битного процесса
Теперь в тот или иной 32-битный процесс мы можем внедрить уязвимость, просто заинжектив туда нашу библиотеку.
Инжект библиотеки в только что запущенный 32-битный системный процесс
Наша DLL без ASLR успешно загружена по стандартному адресу.
Уязвимая DLL загружена
И теперь целевой процесс с занесенным buffer overflow готов получать данные по сети.
Уязвимый код начал работать в контексте легитимного процесса
Поскольку наш уязвимый модуль загрузился по адресу 0x10000000 (это дефолтный адрес для не ASLR-библиотек), нужно слегка скорректировать код эксплоита.
Небольшая корректировка кода эксплоита
Время запустить сам эксплоит.
Запуск эксплоита
В контексте легитимного процесса происходит overflow.
Переполнение буфера в легитимном процессе в действии
И мы исполняем «вредоносный» код в обход антивируса.
Выполнение вредоносного кода в обход антивируса
ВЫВОДЫ
Мы использовали эффект «неожиданного» исполнения кода в памяти через 0-day-уязвимость, антивирус не смог ее спрогнозировать и заблокировать угрозу. Загрузка DLL в чужой процесс — трюк достаточно известный, и мы использовали его исключительно для удобства: нам почти не пришлось ничего менять.На самом деле мы могли использовать еще более хитрый способ — просто подменить ключевые инструкции в той или иной точке памяти процесса, где происходит обработка пользовательского ввода, и внедрить тем самым туда уязвимость (как бы сделать антипатч). А положив пару‑тройку удобных ROP-гаджетов в code caves, сделать ее еще и пригодной к эксплуатации. Но пока что этого даже не требуется.
Техника сокрытия выполнения кода через buffer overflow не нова, хоть и достаточно малоизвестна. В данном примере был использован самый тривиальный пример buffer overflow на стеке, и он принес нам успех. Но существуют куда более хитрые ошибки работы с памятью, приводящие к RCE (скрытому исполнению): use after free, double free, overflow in heap, format strings и так далее. Это открывает практически неисчерпаемый потенциал для приемов обхода антивирусных программ.
Автор @s0i37
источник: xakep.ru