
PoC Реализация полностью динамического спуфера стека вызовов
PoC-реализация полностью динамического спуфера стека вызовов
SilentMoonwalk — это PoC-реализация полностью динамического спуфера стека вызовов, реализующая технику удаления исходного вызывающего из стека вызовов с использованием ROP для десинхронизации размотки (unwinding) от потока управления.
Данный PoC является результатом совместного исследования по теме спуфинга стека. Авторы исследования:
Хочу подчеркнуть, что эта работа была бы невозможна без работы Waldo-IRC и Trickster0, которые оба внесли вклад в ранние стадии PoC и в исследование, лежащее в его основе.
Этот репозиторий демонстрирует PoC-реализацию для спуфинга стека вызовов при вызове произвольных Windows API.
Эта попытка была вдохновлена этой веткой в Twitter и этой веткой в Twitter, где сэнсэй namazso показал и предложил расширить подход размотки стека с помощью ROP-цепочки для десинхронизации размотки от реального потока управления и последующего восстановления исходного стека.
Данный PoC пытается сделать нечто подобное описанному выше и использует десинхронизированный стек для полного сокрытия исходного стека вызовов, а также удаления из него базового адреса EXE-файла. При возврате вызывается ROP-гаджет для восстановления исходного стека. В коде этот процесс повторяется 10 раз в цикле, каждый раз с разными фреймами, для доказательства стабильности.
В настоящее время инструмент поддерживает 2 режима, причем один из них фактически является неправильным патчем для неработающего фрейма pop RBP, который был идентифицирован и работает путем смещения текущего RSP и добавления двух фейковых фреймов в стек вызовов. Поскольку он работает с синтетическими фреймами, я называю этот режим «SYNTHETIC».
При выборе фрейма, который разматывается путем извлечения регистра RBP из стека, инструмент может выбрать неподходящий фрейм, что приведет к резко оборванному стеку вызовов, как показано ниже.

Глупым решением проблемы было бы создать два фейковых фрейма и связать их обратно с оборванным стеком вызовов. Это создало бы своего рода внешне легитимный стек вызовов, даже без подходящего фрейма, который разматывается вызовом POP RBP, но:
Результат синтетического спуфинга можно увидеть на изображении ниже:

Рисунок 1: Windows 10 — внешне легитимный, неразматываемый стек вызовов, в котором модуль EXE был полностью удален (вызов функции без параметров getchar)
Примечание: этот режим работы отключен по умолчанию. Чтобы включить его, измените CALLSTACK_TYPE на 1
Этот режим является правильным решением вышеуказанной проблемы, при котором неподходящий фрейм просто заменяется другим, подходящим.

Рисунок 2: Windows 10 — легитимный, разматываемый стек вызовов, в котором модуль EXE был полностью удален (вызов функции с 4 параметрами MessageBoxA)
В репозитории вы также можете найти небольшую утилиту для инспекции runtime-функций, которая может быть полезна для анализа записей runtime-функций.
UnwindInspector.exe -h
Unwind Inspector v0.100000
Mandatory args:
-m <module>: Target DLL
-f <function>: Target Function
-a <function-address>: Target Function Address
Пример вывода:
UnwindInspector.exe -m kernelbase -a 0x7FFAAE12182C
[*] Using function address 0x7ffaae12182c
Runtime Function (0x000000000000182C, 0x00000000000019ED)
Unwind Info Address: 0x000000000026AA88
Version: 0
Ver + Flags: 00000000
SizeOfProlog: 0x1f
CountOfCodes: 0xc
FrameRegister: 0x0
FrameOffset: 0x0
UnwindCodes:
[00h] Frame: 0x741f - 0x04 - UWOP_SAVE_NONVOL (RDI, 0x001f)
[01h] Frame: 0x0015 - 0x00 - UWOP_PUSH_NONVOL (RAX, 0x0015)
[02h] Frame: 0x641f - 0x04 - UWOP_SAVE_NONVOL (RSI, 0x001f)
[03h] Frame: 0x0014 - 0x00 - UWOP_PUSH_NONVOL (RAX, 0x0014)
[04h] Frame: 0x341f - 0x04 - UWOP_SAVE_NONVOL (RBX, 0x001f)
[05h] Frame: 0x0012 - 0x00 - UWOP_PUSH_NONVOL (RAX, 0x0012)
[06h] Frame: 0xb21f - 0x02 - UWOP_ALLOC_SMALL (R11, 0x001f)
[07h] Frame: 0xf018 - 0x00 - UWOP_PUSH_NONVOL (R15, 0x0018)
[08h] Frame: 0xe016 - 0x00 - UWOP_PUSH_NONVOL (R14, 0x0016)
[09h] Frame: 0xd014 - 0x00 - UWOP_PUSH_NONVOL (R13, 0x0014)
[0ah] Frame: 0xc012 - 0x00 - UWOP_PUSH_NONVOL (R12, 0x0012)
[0bh] Frame: 0x5010 - 0x00 - UWOP_PUSH_NONVOL (RBP, 0x0010)
Чтобы собрать PoC и наблюдать поведение, аналогичное показанному на изображении, убедитесь, что:
/GS-)/Od)/GL)/Os, /Ot)/Oi)Стоит упомянуть предыдущие работы по этой теме, которые заложили основу данной работы.