На последнем внутреннем пентесте финтех-проекта я собрал кастомный загрузчик с indirect syscalls -
NtCreateThreadEx через ручной маппинг SSN всё равно улетел в алерт CrowdStrike Falcon за 800 миллисекунд. Проблема не в обёртке над syscall. Проблема в подходе: любая инъекция процессов, которая явно создаёт поток в чужом адресном пространстве, - красный флаг для EDR с kernel-телеметрией. EntryPoint Hijacking меняет правила игры: payload записывается в целевой процесс через WriteProcessMemory, но выполняется только тогда, когда процесс сам создаёт легитимный поток. Без CreateRemoteThread, без APC-очередей, без SetThreadContext. Разберём механику на уровне PEB и LDR-структур, два доступных PoC и то, где техника ломается.Бизнес-логика: зачем атакующему скрытое внедрение кода
Process Injection (T1055, Defense Evasion / Privilege Escalation) - одна из самых эксплуатируемых техник как APT-группами, так и операторами commodity-малвари. Цель: запустить произвольный код от имени легитимного процесса, унаследовать его привилегии, сетевые подключения и репутацию в глазах EDR. По данным Red Canary (Tier-2), process injection остаётся "a versatile tool that adversaries lean on to evade defensive controls and gain access to sensitive systems""универсальный инструмент, который противники используют для обхода средств защиты и получения доступа к чувствительным системам." Подробнее - в нашем материале про living off the land атаки windows.На стадии post-exploitation инъекция в долгоживущий системный процесс (например,
svchost.exe) даёт стабильный beacon для C2-канала. При lateral movement - запуск credential-dumping payload в контексте lsass.exe без подозрительного дочернего процесса. При exfiltration - сетевые соединения из процесса браузера, который и так генерирует исходящий трафик.Классические process injection техники - DLL Injection (T1055.001), Process Hollowing (T1055.012), Thread Execution Hijacking (T1055.003) - опираются на API-вызовы, которые EDR мониторят на уровне ядра. EntryPoint Hijacking предлагает обход именно этого ограничения.
Место в kill chain: от foothold до persistence
[Применимо: внутренний пентест, grey box / white box, Windows 10/11/Server 2019+]EntryPoint Hijacking - техника post-exploitation. Для применения нужен полученный foothold: RCE на хосте, сессия через начальный загрузчик или физический доступ. Типичная цепочка:
- Initial access - фишинг, эксплуатация уязвимости, supply chain
- Foothold - первичный загрузчик с минимальным функционалом
- EntryPoint Hijacking - миграция payload в легитимный процесс (текущий этап)
- Post-exploitation - запуск beacon, credential dumping, lateral movement
- Exfiltration - вывод данных через C2-канал из контекста доверенного процесса
Почему классические process injection техники детектируются EDR
CreateRemoteThread и DLL Injection (T1055.001)
ЦепочкаVirtualAllocEx -> WriteProcessMemory -> CreateRemoteThread с адресом LoadLibrary - учебник по stealth process injection, и детектится соответственно. По данным Elastic (Tier-1), эта техника "it is tracked and flagged by many security products""отслеживалась и помечалась множеством продуктов безопасности". CrowdStrike Falcon и SentinelOne регистрируют kernel callback через PsSetCreateThreadNotifyRoutine, который срабатывает при создании любого потока. В обработчике EDR определяет cross-process создание (PID создателя != PID владельца потока) и генерирует телеметрию, которую из userland не обойти. В SigmaHQ по тегу T1055.001 - 10 правил, включая детект Cobalt Strike beacon через паттерны Sysmon EventID 8 (CreateRemoteThread) (create_remote_thread_win_hktl_cobaltstrike.yml).Process Hollowing (T1055.012)
ПаттернCreateProcess(CREATE_SUSPENDED) -> NtUnmapViewOfSection -> WriteProcessMemory -> SetThreadContext -> ResumeThread ловится по последовательности вызовов. Elastic описывает: подозрительна уже комбинация создания suspended-процесса с последующим изменением контекста потока. SentinelOne детектирует подмену entrypoint через сравнение содержимого секций PE на диске и в памяти.APC Injection (T1055.004)
NtQueueApcThread / QueueUserAPC в alertable-поток - техника, которую Atomic Red Team тестирует тремя вариантами: через C#, EarlyBird в Go и NtQueueApcThreadEx. Elastic Defend через ETW-TI (провайдер Microsoft-Windows-Threat-Intelligence, требует PPL-подписки) получает события ThreatIntQueueApcThread для вызовов NtQueueApcThread(Ex). По D3FEND, ключевые контрмеры - System Call Analysis (D3-SCA) и Process Spawn Analysis (D3-PSA).Общий паттерн детекта
Все перечисленные malware injection techniques объединяет одно: они явно инициируют выполнение кода в целевом процессе через API-вызов. EDR перехватывают момент передачи управления - создание потока, вызов APC, изменение контекста. Именно этот паттерн EntryPoint Hijacking разрывает.Механика EntryPoint Hijacking: инъекция процессов через точку входа DLL
EntryPoint Hijacking принципиально отличается от классических методов тем, что не вызывает API для запуска кода. Вместо этого техника модифицирует внутренние структуры загрузчика Windows, чтобы код выполнился при следующем легитимном событии создания потока.
Как Windows вызывает DllMain
Каждый загруженный модуль (DLL) имеет функциюDllMain(), которую ОС автоматически вызывает при создании и завершении потоков (DLL_THREAD_ATTACH / DLL_THREAD_DETACH). Загрузчик Windows хранит информацию о каждом модуле в списках PEB_LDR_DATA (структуры LDR_DATA_TABLE_ENTRY), включая поле EntryPoint - адрес DllMain(). При событиях DLL_THREAD_ATTACH функция ntdll!LdrpCallInitRoutine обходит эти списки и вызывает EntryPoint каждого модуля.Когда процесс создаёт новый поток, загрузчик проходит по списку загруженных модулей и дёргает
DllMain() каждого с параметром DLL_THREAD_ATTACH. Именно эту механику эксплуатирует EntryPoint Hijacking.Последовательность атаки
- Атакующий получает handle к целевому процессу (
OpenProcessс правамиPROCESS_VM_WRITE | PROCESS_VM_READ) - Через
NtQueryInformationProcessполучает адрес PEB целевого процесса - Обходит список
InMemoryOrderModuleListвPEB_LDR_DATA, находит подходящую DLL (обычноkernelbase.dll) - Записывает shellcode или runner-функцию в память процесса через
WriteProcessMemory - Подменяет поле
EntryPointвLDR_DATA_TABLE_ENTRYна адрес записанного кода - Ждёт - код выполнится автоматически, когда процесс создаст новый поток
- После выполнения EntryPoint восстанавливается до оригинального значения для стабильности
WriteProcessMemory. Никаких CreateRemoteThread, NtQueueApcThread, SetThreadContext. Shellcode injection происходит скрыто, а EDR, строящие детект на перехвате потоковых API, пропускают технику.Что видно в WinDbg
Для анализа LDR-структур в отладчике (по данным iPurple Team):
Код:
lm m kernelbase
dt ntdll!_LDR_DATA_TABLE_ENTRY <адрес_из_PEB>
EntryPoint покажет текущий адрес точки входа модуля. Если он не совпадает с DllBase + IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint - структура модифицирована. Тут всё просто: несовпадение = кто-то уже побывал.PoC-инструменты: EPI и LdrShuffle - обход антивируса инъекцией
Сейчас доступны два публичных PoC, реализующих EntryPoint Hijacking с разными подходами к исполнению.EPI - EntryPoint Injection (2023)
Автор: Kurosh Dabbagh Escalante. Репозиторий: Kudaes/EPI/.EPI патчит
EntryPoint загруженной kernelbase.dll. При создании нового потока целевым процессом перехваченная DllMain выполняется в контексте этого потока; чтобы не упереться в loader lock, EPI сразу через QueueUserWorkItem передаёт основной payload в системный thread pool, где он и исполняется. После выполнения PEB восстанавливается к предыдущему состоянию. Запуск: epi.exe -p <PID>.LdrShuffle (x33fcon 2025)
Автор: Hugo Valette. Репозиторий: RWXstoned/LdrShuffle.LdrShuffle реализует ту же идею, но с другим механизмом исполнения - Runner-функцией, которая хранится в heap и определяет, какой Windows API вызвать. Ключевое условие выбора DLL для хайджека (из исходного кода LdrShuffle):
C:
(pDte->EntryPoint != NULL && pDte->DontCallForThreads == 0 && i > 5)
DontCallForThreads == 0 проверяет, что DLL принимает thread-уведомления, а i > 5 пропускает первые пять модулей - их патч вызывает нестабильность процесса. Логично: первые пять - это ntdll, kernel32 и компания, трогать которые себе дороже. LdrShuffle работает в двух режимах: локальная инъекция (LdrShuffle.exe) и удалённая (LdrInject.exe <PID> <shellcode>.bin).Ограничения обоих PoC
- Deadlock при сложных API.
InternetOpenWи аналогичные функции изwininet/winhttpвызывают deadlock из-за thread-синхронизации внутриDllMain(loader lock). Для C2-callback нужен отдельный поток - что частично снижает преимущество техники - Race condition. Между записью shellcode и патчем EntryPoint есть окно, в котором другой поток может вызвать DllMain с уже изменённым, но ещё не готовым EntryPoint
- Целостность процесса. Некорректный патч или невосстановленный EntryPoint приводят к краху процесса. На реальном engagement crash целевого процесса - это инцидент, который SOC заметит мгновенно
Сравнение инструментов
| Критерий | EPI | LdrShuffle |
|---|---|---|
| Год выхода | 2023 | 2025 (x33fcon) |
| Режим работы | Удалённый процесс | Локальный + удалённый |
| Механизм исполнения | QueueUserWorkItem (thread pool) | Runner-структура в heap |
| Целевая DLL | kernelbase.dll | Любая после 5-й в списке |
| Стабильность | Средняя | Средняя (пропуск первых 5 DLL) |
Практика: запуск EntryPoint Hijacking в лабораторной среде
Требования к окружению
- ОС: Windows 10 21H2+ / Windows 11 / Server 2019+ (x64)
- RAM: минимум 4 ГБ (8 ГБ рекомендуется при одновременной работе отладчика)
- Инструменты: Visual Studio 2022 Build Tools, WinDbg, Process Hacker 2
- Зависимости: Windows SDK 10.0.22621+
- Сетевые условия: offline-среда допустима, интернет не требуется
- EDR для валидации evasion: изолированная VM с CrowdStrike Falcon, SentinelOne или Microsoft Defender for Endpoint
Пошаговый сценарий с LdrShuffle
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
При верификации через Process Hacker: в целевом процессе не появляется новых потоков с подозрительным стартовым адресом. Shellcode выполняется в контексте существующего потока
DllMain, который загрузчик вызвал штатно.Детект и ограничения: когда EDR bypass не работает
Детектирование через целостность LDR-структур
По данным iPurple Team, надёжный детект строится на проверке:
Код:
LDR_DATA_TABLE_ENTRY.EntryPoint == DllBase + OptionalHeader.AddressOfEntryPoint
LdrShuffleDetect
Вместе с LdrShuffle опубликована утилитаLdrShuffleDetect: каждые 10 секунд сканирует все процессы через CreateToolhelp32Snapshot, открывает каждый через OpenProcess, читает LDR-структуры через ReadProcessMemory и сверяет EntryPoint. Инструмент демонстрирует подход, но 10-секундный интервал - ловушка: если hijack + restore занимают менее секунды, point-in-time сканирование пропускает событие. По сути это как ставить камеру, которая делает снимок раз в 10 секунд, и надеяться поймать кого-то на пробежке.Вендор-специфика обнаружения
| EDR | Механизм детекта | Применимость к EntryPoint Hijacking |
|---|---|---|
| CrowdStrike Falcon | Kernel callbacks на CreateThread + user-mode hooks | Фиксирует cross-process WriteProcessMemory, но не коррелирует с LDR-модификацией. Специфичного правила нет |
| SentinelOne | Behavioral AI + user-mode hooks | Потенциально детектирует аномальный cross-process write, но без правила для LDR-патча |
| Microsoft Defender for Endpoint | ETW-TI + kernel sensors | ETW-TI фиксирует NtAllocateVirtualMemory / NtProtectVirtualMemory в cross-process контексте; прямой телеметрии NtWriteVirtualMemory нет, детект строится на аллокации RW/RWX страниц в чужом процессе. Встроенного детекта нет |
| Elastic 8.x+ с kernel ETW-TI | System Call Analysis (D3-SCA) | Самый перспективный вектор: мониторинг цепочки WriteProcessMemory + ReadProcessMemory к PEB/LDR без последующего CreateThread |
В SigmaHQ по тегу T1055 - 54 правила, но ни одно не таргетирует модификацию LDR EntryPoint. Ближайшие аналоги - правила на
mavinject (proc_creation_win_lolbin_mavinject_process_injection.yml), ловящие совсем другой вектор.Когда техника НЕ работает
- Процесс не создаёт потоков. Если целевой процесс однопоточный и не порождает новых потоков -
DllMainсDLL_THREAD_ATTACHне вызовется, shellcode не выполнится. Просто будет лежать в памяти мёртвым грузом - DontCallForThreads == 1. У выбранной DLL установлен флаг, запрещающий thread-уведомления - загрузчик пропустит EntryPoint
- Protected Processes Light (PPL).
OpenProcessс правами записи в PPL-процессы (csrss.exe,lsass.exeс RunAsPPL) невозможен без kernel-level эксплойта - HVCI (Hypervisor-protected Code Integrity). Credential Guard и HVCI ограничивают модификацию исполняемых страниц памяти
- Блокирующие операции в DllMain. Любой сетевой вызов, ожидание мьютекса - deadlock из-за loader lock. C2-callback из DllMain-контекста невозможен без выноса в отдельный поток
EntryPoint Hijacking vs process hollowing альтернативы: сравнение
| Критерий | DLL Injection (T1055.001) | Process Hollowing (T1055.012) | Thread Hijacking (T1055.003) | EntryPoint Hijacking |
|---|---|---|---|---|
| API для запуска кода | CreateRemoteThread | ResumeThread + SetThreadContext | SuspendThread + SetThreadContext + ResumeThread | Нет (легитимный поток) |
| Требует создания потока | Да | Нет (возобновляет suspended) | Нет (перехватывает существующий) | Нет |
| Артефакт на диске | DLL-файл | Нет | Нет | Нет |
| Стабильность | Высокая | Средняя | Низкая (crash при неверном контексте) | Низкая-Средняя (race conditions) |
| Детектируемость (2025) | Высокая - все EDR | Высокая - паттерн CREATE_SUSPENDED | Средняя - зависит от EDR | Низкая - нет встроенных правил |
| Контроль момента исполнения | Полный | Полный | Полный | Отсутствует (ждём поток) |
| Поддержка C2 из контекста | Да | Да | Да | Нет (deadlock в DllMain) |
Главный trade-off EntryPoint Hijacking - стелс в обмен на контроль. Нет способа точно определить, когда код выполнится. Для time-sensitive операций (credential dumping в окне между аутентификацией и ротацией) это ограничение критично.
EntryPoint Hijacking - не серебряная пуля и не замена отлаженным AV bypass техникам. Это ещё один примитив в арсенале, который работает именно потому, что EDR-вендоры пока не добавили специфичный детект. CrowdStrike Falcon, SentinelOne, MDE - ни один из них на момент публикации не имеет встроенного правила на патч LDR EntryPoint. Но "пока не имеет" - ключевое слово. После публикации LdrShuffle на x33fcon 2025 - вопрос времени, когда PEB/LDR integrity check станет стандартом в endpoint-телеметрии.
На практике я вижу два сценария, где техника оправдана. Первый - миграция из начального загрузчика в долгоживущий процесс (
svchost.exe, RuntimeBroker.exe) на этапе post-exploitation, когда нужен устойчивый beacon без лишних алертов. Второй - инъекция в процесс с нужными сетевыми подключениями для маскировки C2-трафика, при условии что C2-вызов вынесен в отдельный поток из Runner-контекста.Рассчитывать на эту технику как на единственный evasion-вектор - ошибка.
WriteProcessMemory в cross-process контексте уже мониторится большинством EDR. Вопрос только в корреляции: EDR видит запись, но не видит последующего запуска потока - и это само по себе выглядит подозрительно. Когда вендоры начнут коррелировать cross-process write с LDR-аномалиями, окно закроется. Если хочешь повторить шаги в контролируемой инфре - задачи по memory injection techniques ждут на HackerLab (https://hackerlab.pro).
Последнее редактирование модератором: