Продвинутая техника обхода в памяти, переключающая защиту памяти шеллкода между RW/NoAccess и RX, а затем шифрующая/дешифрующая его содержимое
PoC-реализация ещё одной техники внутрипроцессного обхода, которая циклически шифрует и расшифровывает содержимое шеллкода, заставляя его «колебаться» между правами памяти RW (или NoAccess) и RX.
Когда наш шеллкод находится на страницах памяти RW или NoAccess, такие сканеры, как Moneta или pe-sieve, не смогут его отследить и выгрузить для дальнейшего анализа.
После публикации ThreadStackSpoofer я получил несколько вопросов о следующем пункте из README:
Измените защиту страниц памяти вашего Beacon на RW (с RX/RWX) и зашифруйте их содержимое перед засыпанием (это может обойти такие сканеры, как Moneta или pe-sieve)
Ранее я полагал, что сообщество уже знает, как шифровать/расшифровывать свои полезные нагрузки и менять права памяти, чтобы просто обходить сканеры памяти, ищущие аномальные исполняемые области. Вопросы показали обратное, поэтому я решил опубликовать этот «невооружённый» PoC, чтобы задокументировать ещё одну стратегию обхода и предложить сообществу пример реализации для работы.
Этот PoC демонстрирует довольно простую технику, уже известную наступательному сообществу (так что здесь я вряд ли привношу что-то новое), в надежде приоткрыть завесу тайны над магией, показываемой некоторыми коммерческими фреймворками, которые демонстрируют свои возможности обхода, нацеленные на оба упомянутых сканера памяти.
Вот сравнение при колебании до RW (другой вариант — колебание до PAGE_NOACCESS — описан ниже):

Эта реализация вместе с моим ThreadStackSpoofer даёт сообществу Offensive Security примеры реализаций, чтобы мы могли не отставать от предложений коммерческих C2-продуктов и быть ничуть не хуже в наших Red Team-инструментах. 💪
Эта программа выполняет самоинъекцию шеллкода (примерно через классические VirtualAlloc + memcpy + CreateThread).
Когда шеллкод запускается (эта реализация специально нацелена на импланты Cobalt Strike Beacon), хукается функция Windows, перехватывающая момент засыпания Beacon — kernel32!Sleep.
Всякий раз, когда вызывается захученная функция MySleep, она определяет границы своего выделения памяти, меняет их защиту на RW и побайтово выполняет xor32 над всеми байтами, хранящимися там.
Выждав ожидаемое количество времени, когда шеллкод вернётся к нашему обработчику MySleep, мы расшифровываем данные шеллкода и возвращаем защиту памяти обратно на RX.
PAGE_READWRITE работает следующим образомkernel32!Sleep, указывая на наш callback.VirtualAlloc + memcpy + CreateThread. В отличие от того, что было в ThreadStackSpoofer, здесь мы не хукаем ничего в ntdll для запуска шеллкода, а переходим на него из собственной функции. Это позволяет избежать оставления в памяти простых IOC, указывающих на изменённую память ntdll.MySleep.RW.kernel32!Sleep, чтобы не оставлять в памяти простой IOC, указывающий на то, что Sleep был трамплинирован (inline-хук).::Sleep, чтобы дать Beacon поспать в ожидании дальнейшей коммуникации.RX и повторно хукаем kernel32!Sleep, чтобы обеспечить перехват последующих засыпаний.PAGE_NOACCESS работает следующим образомkernel32!Sleep, указывая на наш callback.VirtualAlloc + memcpy + CreateThread ...MySleep.PAGE_NOACCESS.kernel32!Sleep, чтобы не оставлять в памяти простой IOC, указывающий на то, что Sleep был трамплинирован (inline-хук).::Sleep, чтобы дать Beacon поспать в ожидании дальнейшей коммуникации.kernel32!Sleep, чтобы обеспечить перехват последующих засыпаний.RX, после чего выполнение шеллкода возобновляется.Техника не новая, и не я её придумал. Это лишь реализация, показывающая концепцию и её практическое применение, чтобы наше сообщество Offensive Security могло не отставать от предложений коммерческих C2-фреймворков.
Вообще-то, с идеей переключения защиты памяти шеллкода я познакомился пару лет назад благодаря работе Джоша Лоспинозо в его потрясающем Gargoyle.
Вот ещё немного дополнительной информации:
Gargoyle развивает концепцию самоосознающего и самофлуктуирующего шеллкода гораздо дальше, используя ROP-последовательность с вызовом VirtualProtect.
Однако, хотя техника впечатляет, её так же сложно применить к Cobalt Strike Beacon без необходимости убивать его поток и постоянно переинициализировать Beacon в памяти.
Это далеко от идеала, но поскольку мы уже действуем с позиций собственного процесса-загрузчика самоинъекции, мы можем делать с окружением, в котором работает шеллкод, всё, что захотим, и скрывать его как угодно. Эта техника (и предыдущая — ThreadStackSpoofer) показывает преимущества запуска наших шеллкодов именно таким способом.
Реализация колебания до PAGE_NOACCESS вдохновлена работой ORCA666, представленной в его https://github.com/ORCA666/0x41 инжекторе.
Он показал, что:
Эта реализация содержит данную идею, доступную с опцией 2 в <fluctuate>.
Обязательно посмотрите и другие его проекты.