Открыть Электронные книги
Категории
Открыть Аудиокниги
Категории
Открыть Журналы
Категории
Открыть Документы
Категории
ВВЕДЕНИЕ
Следует помнить, что ни одна из рассматриваемых техник защиты или атаки не является
абсолютной.
Для каждой есть меры противодействия.
Некоторые техники используются несмотря на устаревание, т.к.:
- гарантируют запуск на старых ОС, коих всегда будет много;
- ничего не изобрели вместо.
1. ИНЪЕКЦИЯ В ПРОЦЕСС
Инъекция в процесс нужна для выполнения своего кода в чужом процессе того же
пользователя, в ситуации, когда уже есть доступ в систему.
Классическая цель - броузеры (перехват и замена трафика для реализации атаки MITM),
мессенджеры, почтовые клиенты итд.
Последовательность вызовов примерно такая:
https://github.com/stephenfewer/ReflectiveDLLInjection
https://github.com/dismantl/ImprovedReflectiveDLLInjection
Если специального пролога нет, хук все равно можно поставить - не всегда. Нужно
смотреть на содержимое пролога.
Просто затираемые jmp инструкции нужно скопировать и перенести в тело нового
обработчика - но там творческий подход,
и универсальных рецептов нет.
Компилятор Microsoft имеет переключатель: свойства проекта -> C/C++ -> Создание
кода -> Создать образ с обновлением
(patchable image) специально для цели генерации пустых прологов функций.
3. PROCESS HOLLOWING
4. PROCESS DOPPELGäNGING
Для всех остальных процессов выглядит так, как будто бы запускается оригинальный
файл.
Но открывший транзакцию поток видит новый текст в запускаемом файле.
Техника успела устареть, т.к. начиная с какой-то версии Windows 10 транзакции NTFS
вроде бы отменили совсем.
Описание на английском:
https://hshrzd.wordpress.com/2017/12/18/process-doppelganging-a-new-way-to-
impersonate-a-process/
https://jxy-s.github.io/herpaderping/
https://github.com/jxy-s/herpaderping.git
5. ЗАМЕНА PEB
Еще один интересный прием - затирание таблицы экспорта .dll после загрузки в
память.
После того, как получены адреса всех необходимых адресов функций, можно затереть
весь экспорт и таким образом
осложнить идентификацию процесса по известным/уникальным именам функций.
6. SHELL CODE
Название идет с былинных времен, когда инъектируемый код был призван получить
remote shell на атакуемой машине.
Потому код должен был удовлетворять требованиям:
- быть кратким насколько возможно (чтобы затереть чем поменьше в атакуемом
процессе)
- быть позиционно-независимым (только относительная адресация)
Ныне, shell code обозначает любую вставку машинного кода вне зависимости от
контекста применения.
Однако типичный контекст по-прежнему - инъекция в процесс, например грубо в кусок
исполняемого потока (чтобы обойти DEP).
Так что указанные выше требования в силе.
Типичный shell code для процессов Windows делает bootstrapping (в том числе для
обхода ASLR):
- определяет важные вехи в памяти - указатели на TEB и PEB
- из PEB получает адрес таблицы импорта
- из таблицы импорта получает адрес kernel32.dll
- из kernel32.dll получает адреса LoadLibrary, GetProcAddress (обычно по хешу
имени, а не по самому имени)
- когда есть LoadLibrary и GetProcAddress, можно делать все что угодно.
https://habr.com/ru/post/522966/
http://www.hick.org/code/skape/papers/win32-shellcode.pdf
(У этого чувака http://www.hick.org/~mmiller/ кстати есть куча интересного, хотя и
устаревшего)
https://www.corelan.be/index.php/2010/02/25/exploit-writing-tutorial-part-9-
introduction-to-win32-shellcoding/
Конвертер PE->shell
https://github.com/hasherezade/pe_to_shellcode
7. INJECT PE / INFECT PE
Windows Defender использует драйвер типа minifilter под названием WdFilter для
подписки на события в системе.
Перехватываются:
- создание и запуск процесса
- загрузка образа процесса
- запуск потоков
- манипуляции с неродным процессом - запись в память и запуск удаленного потока
- верифицируются загружаемые драйвера
- перехватываются операции с реестром.
Из всего полезного что удалось найти в разборе - список условий, при которых можно
избежать такого внимания:
- белый список процессов (в него входят в т.ч. svchost, werfault и процессы самого
WinDefender)
- список hardened ключей реестра
https://n4r1b.com/posts/2020/01/dissecting-the-windows-defender-driver-wdfilter-
part-1/
https://n4r1b.com/posts/2020/02/dissecting-the-windows-defender-driver-wdfilter-
part-2/
https://n4r1b.com/posts/2020/03/dissecting-the-windows-defender-driver-wdfilter-
part-3/
https://n4r1b.com/posts/2020/04/dissecting-the-windows-defender-driver-wdfilter-
part-4/
УСТРОЙСТВО ЭМУЛЯТОРА
ПРОТИВОДЕЙСТВИЕ АНТИВИРУСАМ
Известна техника KHOBE (Kernel HOoks Bypassing Engine). В паблике отсутствует код,
известны лишь общие сведения.
Нужно использовать race condition (чей?), предъявляя для анализа благополучный код
в момент и быстро переключить контекст сразу после анализа АВ.
Подробнее см.раздел "Чистка от антивирусных детектов".
Самый простой способ - поищите уже известные сигнатуры в базе сигнатур YARA.
Yara - это свободное средство для сигнатурного анализа малвари
https://virustotal.github.io/yara/
Но сами правила от него не выложены централизованно.
Есть коммерческие сборники правил, например
https://www.nextron-systems.com/2018/12/21/yara-rule-sets-and-rule-feed/
Есть бесплатные низкого качества https://github.com/Neo23x0/signature-base/
Большой сборник баз, среди которых есть и шлак, и актуальные:
https://github.com/InQuest/awesome-yara
Синтаксис и возможности правил: https://habr.com/ru/company/varonis/blog/584618/
Старая, но до сих пор действенная техника GetApi была применена в троянце Carberp:
https://github.com/hzeroo/Carberp/blob/master/source%20-%20absource/pro/all
%20source/RemoteCtl/DrClient/GetApi.h
В конечном итоге, системные вызовы все равно ловятся АВ, работающих в режиме ядра
через подписки на перехват вызовов.
ОБФУСКАЦИЯ КОДА
Обычно код в виде .dll сжимают, шифруют и пакуют в массив (см.ниже Скрытие данных в
сегменте кода),
во время выполнения аллоцируют память, извлекают и распаковывают код, далее
настраивают .dll в памяти
(релокации, импорт и все такое). См.ниже про крипторы и упаковщики - это оно и
есть.
Этот трюк известен АВ и от проактивной защиты такое не спасает.
2. собственные JIT-интерпретаторы.
Например, https://github.com/jacob-baines/jit_obfuscation_poc
Идея ясна из названия - транслируем один код в другой код, который неизвестен
антивирусам и недоступен
автоматическому реверсу и анализу.
ОБФУСКАЦИЯ СТРОК
Есть также простой трюк, используемый при отсутствии С++ (в чистом Си)
char str[] = { 'H', 'E', 'L', 'L', 'O' };
В такой инициализации строка заносится в массив на стеке mov'ами в во время
выполнения, и как строка не попадет в сегмент .data
(т.е. превратится в набор ассемблерных инструкций в .text такого вида:
char str[] = "HELLO"; заполнится во время компиляции как строка и попадет в .data.
АВ чутко реагируют на необычно большую секцию данных (.data, .rdata) - это признак
сокрытия в ней зашифрованного кода-нагрузки.
Данные можно скрывать в секции текста. Компилятор Microsoft C++ позволяет сделать
это с помощью такого трюка:
#pragma comment(linker,"/SECTION:.data,EW")
unsigned char PayloadName0[]={}
#pragma comment(linker,"/SECTION:.rdata,R")
unsigned char PayloadName2[]={}
КРИПТОРЫ И УПАКОВЩИКИ
Протектор с "наномитами":
https://www.apriorit.com/white-papers/293-nanomite-technology
Применена блокировка отладки процесса своим собственным отладчиком;
замена в дочернем потоке ВСЕХ инструкций переходов опкодами INT 3 (отладочное
прерывание);
и формирование адреса перехода в процессе-отладчике.
СНЯТИЕ ХУКОВ
Для снятия чужих хуков можно использовать сравнение пролога функции в памяти
процесса
с прологом в файле соответствующей .dll. Если они различаются, это признак того,
что на функцию проставлен чужой хук.
Борьба соответствующая: прочесть тело функции из файла и заменить тело функции в
памяти.
Достаточно первых 10 байт.
Обзор и сравнение разных техник:
https://www.first.org/resources/papers/telaviv2019/Ensilo-Omri-Misgav-Udi-Yavo-
Analyzing-Malware-Evasion-Trend-Bypassing-User-Mode-Hooks.pdf
Демо: https://github.com/apriorit/antirootkit-anti-splicer
Еще: https://github.com/CylanceVulnResearch/ReflectiveDLLRefresher
Детект хуков: https://github.com/asaurusrex/Probatorum-EDR-Userland-Hook-Checker
Сравнение userland-хуков разных EDR; прямая работа с сисвызовами:
https://github.com/Mr-Un1k0d3r/EDRs
Имейте в виду, что хуки на ваш процесс могут быть восстановлены после того, как вы
их сняли.
Этой технике можно успешно противодействовать - извне процесса можно убрать хук на
BaseThreadInitThunk
по описанной выше методике снятия хуков, после чего инъекция возможна.
Еще один способ - сразу после старта процесса выкрутить на максимум все митигации
(см.ниже) в частности по DEP
и подписи кода.
https://ethicalchaos.dev/2020/06/14/lets-create-an-edr-and-bypass-it-part-2/
Здесь описана защита процесса с помощью:
- "невинной" разработки кода
- снятия хуков
- работы сисвызовами напрямую
- митигаций
- SharpBlock - еще одна техника, использующая перехват старта дочернего потока с
помощью событий отладки, и патчинг его точки входа для запутывания EDR.
1. Запрет доступа через дискреционный список контроля доступа (DACL). DACL пустой
=> процесс сможет убить только админ:
https://stackoverflow.com/questions/6185975/prevent-user-process-from-being-killed-
with-end-process-from-process-explorer
RtlSetProcessIsCritical:
https://www.codeproject.com/Articles/43405/Protecting-Your-Process-with-
RtlSetProcessIsCriti
3. Если есть приватный ключ от цифровой подписи кода Microsoft (лол))), то начиная
с Windows Vista
можно сделать любой процесс защищенным от любых изменений извне.
Также, защищенный родительский процесс может порождать защищенный дочерний процесс,
используя вызов функции CreateProcess с флагом CREATE_PROTECTED_PROCESS.
Этот механизм был улучшен в Windows 8.1, но он не идеален и не исключает
возможности сделать любой процесс защищенным
или же снять защиту с системных процессов, имеющих цифровую подпись.
Пример на MSDN:
https://docs.microsoft.com/en-us/windows/console/registering-a-control-handler-
function
МИТИГАЦИИ
см.
SetProcessMitigationPolicy()/UpdateProcThreadAttribute(PROC_THREAD_ATTRIBUTE_MITIGA
TION_POLICY)
Позволяет включить у процесса DEP, ASLR, запретить динамическую генерацию кода,
доп.проверки на проверки подписи, валидности дескрипторов, SEHOP исключений, и
много чего еще.
Этим пользуются броузеры и АВ, выкручивая митигации на максималки с целью
затруднить инъекции в них или сковырнуть процесс.
На Windows 10 это действительно эффективно.
СЕТЕВАЯ АСИММЕТРИЯ
Перед чисткой нужно первым делом убедиться, что антивирус не сливает образцы:
- virustotal ВСЕГДА сливает образцы в прямом эфире
- dyncheck сливает образцы при динамических проверках по поведению. При статических
проверках - вроде бы нет
- у Windows Defender нужно отключить опцию "Отправка образцов"
- у остальных АВ нужно найти опцию отправки образцов и облачной защиты и отключить
их.
Под Linux, не забываем убрать символы и лишние строки утилитой strip (а еще лучше
sstrip).
HANDLE h = LoadLibrary(_OBFUSCATED("dll.dll"));
void* f = GetProcAddress(h, _OBFUSCATED("funcname"));
Еще одна тактика снятия детектов - рандомизация адресов функций в итоговом .exe-
файле.
Этого можно добиться простым перемешиванием списка объектных файлов в командной
строке компоновщика:
РЕПУТАЦИЯ ФАЙЛА
РАЗРЫВ ЦЕПОЧКИ
Одна добрая душа сделала каталог таких системных утилит и критериев для их
использования https://lolbas-project.github.io/
https://github.com/api0cradle/LOLBAS
ZERG RUSH
Запускаем 100500 разных хэшей крипта одной и той же нагрузки, перегружая тем самым
АВ.
Если повезет, АВ выпилит только 10499:
https://habr.com/ru/company/solarsecurity/blog/519994/
ОБХОД AMSI
AMSI - это Antimalware Scan Interface, антивирусный модуль для анализа кода
скриптовых языков Windows.
Он обрабатывает код на PowerShell, C#, VBScript, JavaScript, Windows Script Host
(wscript.exe and cscript.exe),
макросов Office VBA, и UAC.
Анти-AMSI обфускатор:
https://amsi.fail/
Обфускатор PowerShell-скриптов:
https://reconshell.com/chimera-powershell-obfuscation-script-for-bypass-amsi-and-
antivirus/
FAT BINARY
ОБНАРУЖЕНИЕ ПЕСОЧНИЦ
ОБХОД ЭМУЛЯТОРОВ
Эмулятор как правило является частью антивируса, и ему нужно очень быстро
определить, разрешить ли работу данного кода или нет.
Из-за этого, проверка в эмуляторе как правило не занимает много времени.
На этом строится основная стратегия обхода эмуляторов - задержка выполнения.
Простой Sleep() уже давно не работает, т.к. перехватывается эмулятором, реальной
задержки не происходит.
Поэтому как правило вместо задержки используется цикл вычислений (например расчет
числа Пи с большой точностью).
При реализации канала связи с C&C следует помнить, что в ОС может быть настроен
свой системный прокси.
Для начального поиска C&C можно использовать алгоритм генерации домена (DGA -
Domain Generation Algorithm)
http://www.marc-blanchard.com/BotInvaders/index.php
Смысл его в том, чтобы сгенерировать псевдослучайные доменные имена
- которых не слишком много (до 10к), чтобы перебрать за разумное время;
- их достаточно много, чтобы их нельзя было засквоттить или еще как-то
забанить/заспуфить;
- список доменов для одного месяца отличается от списка доменов для другого месяца;
- трудно сделать регулярку, чтобы вырезать их на dns-серверах.
Обычно для связи с C&C используется протокол HTTPS, но не всегда нужный порт или
протокол открыт.
Если DPI-фильтр режет HTTP(s)-трафик, используются другие протоколы:
- можно делать специфические DNS-запросы на _нужный_ (свой) DNS-сервер, и прятать
информацию в доменных именах
- можно делать специфические ICMP-посылки ***
- используются почтовые протоколы SMTP/IMAP/POP3
https://habr.com/ru/company/kaspersky/blog/522128/
Словом, можно использовать разные варианты модуляции полезного сигнала поверх
несущей,
которая гарантировано проходит через файрволл.
ОБХОД UAC
ПЕРЕХВАТ ТРАФИКА
Wireshark, можно узнать адреса, порты, протоколы. Если повезет, можно даже
посмотреть подробности.
Из-за повсеместного SSL стало непросто, но можно подсунуть свой корневой сертификат
в систему, поднять прокси с его использованием,
и ловить трафик уже на прокси.
https://mitmproxy.org/ прокси для HTTPS, также полезен для ловли проблем на стыках
подсистем
FUZZING
Для этого есть масса инструментов, вручную так никто не делает, все
автоматизированно.
И еще раз:
- VEH - это AddVectoredExceptionHandler() и статья https://docs.microsoft.com/ru-
ru/windows/win32/debug/using-a-vectored-exception-handler
- SEH - это __try ... __except и статья
https://docs.microsoft.com/en-us/cpp/cpp/try-except-statement?view=vs-2019
Если используется SEH, то обернуть в try/except нужно все основные потоки.
Если используется VEH, то достаточно установки одного общего обработчика в прологе
программы.
Из минусов SEH - Ошибка C2712 Cannot use __try in functions that require object
unwinding, и решение описано тут
https://stackoverflow.com/questions/51701426/cannot-use-try-in-functions-that-
require-object-unwinding-fix
Свойства / C/C++ / Создание кода / Включить С++ исключения: Нет
Для боевых сборок это не подойдет, но для отладки на внутренних ресурсах можно так.
Для боевых сборок в стеке будут просто адреса, что тоже немало.
#include <windows.h>
#include <Psapi.h>
process = GetCurrentProcess();
DWORD offset_from_symbol = 0;
#ifdef _WIN64
IMAGEHLP_LINE64* line = (IMAGEHLP_LINE64*)calloc(sizeof(IMAGEHLP_LINE64), 1);
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
#else
IMAGEHLP_LINE* line = (IMAGEHLP_LINE*)calloc(sizeof(IMAGEHLP_LINE), 1);
line->SizeOfStruct = sizeof(IMAGEHLP_LINE);
#endif
free(symbol);
free(line);
return 1;
}
Включаются в компоновщике:
Visual Studio / Свойства проекта / Компоновщик / Отладка / Создавать файл
сопоставления: ДА
hello.cpp:
#include <windows.h>
int MyMain()
{
MessageBoxA(NULL,str,str,MB_OK);
ExitProcess(0);
return 0;
}
build.bat:
set PATH=c:\LLVM9\bin
По умолчанию точка входа - WinMainCRTStartup в CRT. Когда своя точка входа, CRT не
нужен.
Вдобавок здесь указан отключающий CRT ключ /nodefaultlib.
Но функции strcpy придется писать самому, и исключения не получится использовать.
Но strcpy уже есть в shlwapi.lib, потому делаем в коде
#include <Shlwapi.h>
#pragma comment(lib,"Shlwapi.lib")
ОПТИМИЗАЦИИ
- Отключить кадры стека (Omit frame pointers) - по умолчанию при входе в функцию
текущая вершина стека в регистре BSP.
Таким образом можно отграничить личный стек текущей функции от стека вышестоящих
функций.
Если отключить сохранение кадров стека, то высвободится еще один регистр, и
уменьшится число ассемблерных инструкций,
как за счет отказа от записи в него, так и за счет того, что больше переменных
можно хранить и передавать через регистры.
http://ntcore.com/files/richsign.htm
http://bytepointer.com/articles/the_microsoft_rich_header.htm
Нештатная секция в PE-заголовке, которая вставляется компоновщиками Microsoft с
1998 года и версии Microsoft Visual Studio 6.0.
В нее записывается статистика о тулчейне, собравшем данный бинарник, как-то - кол-
во объектных файлов C,
кол-во объектных файлов C++, кол-во объектных файлов ASM, версия компоновщика,
версия компилятора ресурсов,
кол-во функций в импорте, и всякое такое.
Сделано это было скорее всего для отладочных целей (чтобы отладить тулчейн сборки).
Однако нужно понимать, что этот заголовок может быть использован для криминалистики
как отпечаток.
КРОСС-БИТНЫЙ КОД
ЛИТЕРАТУРА