Стеки вызовов являются беспроигрышным вариантом для поведенческой защиты, одновременно улучшая ложноположительные и ложноотрицательные результаты и объясняя оповещения. В этой статье мы покажем вам, как вы можете использовать стеки вызовов.
Когда функция A, выполняющая поток, вызывает функцию B, ЦП автоматически сохраняет адрес текущей инструкции (внутри A) в область памяти, специфичную для потока, называемую стеком. Этот сохраненный указатель известен как адрес возврата — именно здесь выполнение возобновится после того, как B завершит свою работу. Если B вызовет третью функцию C, то адрес возврата внутри B также будет сохранен в стек. Эти адреса возврата можно получить с помощью процесса, известного как обход стека, который восстанавливает последовательность вызовов функций, которые привели к текущему состоянию потока. При обходе стека адреса возврата перечисляются в обратном хронологическом порядке, поэтому самая последняя функция всегда находится вверху.
Например, в Windows, когда мы дважды щелкаем по файлу notepad.exe,
вызывается следующая серия функций:
На следующем снимке экрана показан стек вызовов для этой цепочки выполнения:
Вот пример создания файла с помощью notepad.exe, где мы видим аналогичную картину:
Помимо использования стеков вызовов для поиска известных проблем, таких как неподдерживаемые области памяти с разрешениями RWX, которые могут быть остатками предыдущего внедрения кода. Стеки вызовов обеспечивают очень низкоуровневую видимость, которая часто позволяет получить более подробную информацию, чем в противном случае могут предоставить журналы.
Например, при поиске подозрительных запусков процессов, запущенных WmiPrvSe.exe через WMI, вы обнаруживаете этот экземпляр notepad.exe:
Просматривая стандартные поля журнала событий, можно предположить, что он был запущен с использованием класса Win32_Process с использованием синтаксиса вызова процесса wmic.exe create notepad.exe. Однако в деталях события описывается ряд модулей и функций:
В синем разделе показаны стандартные промежуточные API CreateProcess Windows, а в красном разделе выделена более подробная информация: мы видим, что перед первым вызовом CreateProcessW используется библиотека wbemcons.dll, а при проверке ее свойств мы видим, что она связана с событием потребителей WMI. Мы можем сделать вывод, что этот экземпляр notepad.exe, скорее всего, связан с подпиской на события WMI. Это потребует конкретных действий по реагированию на инциденты, чтобы смягчить действие механизма сохранения WMI.
Еще один отличный пример — запланированные задачи Windows. При выполнении они создаются как дочерние элементы службы расписания, которая запускается в ведущем процессе svchost.exe. На современных компьютерах с Windows 11 может быть запущено 50 или более процессов svchost.exe. К счастью, служба Schedule имеет специальный аргумент процесса -s Schedule, который отличает ее:
В более старых версиях Windows служба запланированных задач является членом группы сетевых служб и выполняется как компонент общего экземпляра svchost.exe netsvcs. Не все дочерние элементы этого процесса обязательно являются запланированными задачами в этих старых версиях:
Просматривая стек вызовов в обеих версиях, мы видим, что модуль, соседний с вызовом CreateProcess, — это один и тот же ubpm.dll (DLL Unified Background Process Manager), выполняющий экспортированную функцию ubpm.dll!UbpmOpenTriggerConsumer:
Используя следующий запрос KQL, мы можем отслеживать выполнение задач в обеих версиях:
Еще один интересный пример возникает, когда пользователь дважды щелкает файл сценария из ZIP-архива, который был открыт с помощью проводника Windows. Глядя на дерево процессов, вы увидите, что explorer.exe является родительским, а дочерний процесс — интерпретатором сценариев, например wscript.exe или cmd.exe.
Это дерево процессов можно спутать с двойным щелчком пользователем файла сценария в любом месте файловой системы, что не очень подозрительно. Но если мы проверим стек вызовов, мы увидим, что родительский стек указывает на zipfld.dll (расширение оболочки Zipped Folders):
Теперь, когда у нас есть лучшее представление о том, как использовать стек вызовов для лучшей интерпретации событий, давайте рассмотрим некоторые примеры расширенного обнаружения для каждого типа событий.
Dirty Vanity — это новейший метод внедрения кода, который злоупотребляет разветвлением процесса для выполнения шелл-кода внутри копии существующего процесса. Когда процесс разветвляется, ОС создает копию существующего процесса, включая его адресное пространство и любые наследуемые дескрипторы в нем.
При выполнении Dirty Vanity создаст форк экземпляра целевого процесса (уже запущенного или жертвуемого), а затем внедрит в него. Использование обратных вызовов уведомлений о создании процесса не будет регистрировать разветвленные процессы, поскольку начальный поток разветвленного процесса не выполняется. Но в случае этого метода внедрения разветвленный процесс будет внедрен и запущен поток, который запускает журнал событий запуска процесса со следующим стеком вызовов:
Мы видим вызов RtlCreateProcessReflection и RtlCloneUserProcess для разветвления процесса. Теперь мы знаем, что это раздвоенный процесс, и следующий вопрос: «Распространено ли это в нормальных условиях?» Хотя с диагностической точки зрения такое поведение кажется распространенным и единичным, оно не является явным сигналом чего-то вредоносного. Далее проверяем, выполняют ли разветвленные процессы какие-либо сетевые подключения, загружают библиотеки DLL или порождают дочерние процессы, которые оказались менее распространенными и созданы для хорошего обнаружения:
descendant of
[process where event.action == "start" and
_arraysearch(process.parent.thread.Ext.call_stack, $entry,
$entry.symbol_info:
("*ntdll.dll!RtlCreateProcessReflection*",
"*ntdll.dll!RtlCloneUserProcess*"))] and
not (process.executable :
("?:\\WINDOWS\\SysWOW64\\WerFault.exe",
"?:\\WINDOWS\\system32\\WerFault.exe") and
process.parent.thread.Ext.call_stack_summary :
"*faultrep.dll|wersvc.dl*")
// EQL detecting a forked process loading a network DLL
// or performs a network connection - very suspicious
sequence by process.entity_id with maxspan=1m
[process where event.action == "start" and
_arraysearch(process.parent.thread.Ext.call_stack,
$entry, $entry.symbol_info:
("*ntdll.dll!RtlCreateProcessReflection*",
"*ntdll.dll!RtlCloneUserProcess*"))]
[any where
(
event.category : ("network", "dns") or
(event.category == "library" and
dll.name : ("ws2_32.dll", "winhttp.dll", "wininet.dll"))
)]
Вот пример разветвления explore.exe и выполнения шеллкода, который порождает cmd.exe из разветвленного экземпляра explorer.exe:
Второй и последний пример событий процесса — создание процесса посредством прямого системного вызова. При этом напрямую используется инструкция системного вызова вместо вызова API NtCreateProcess. Злоумышленники могут использовать этот метод, чтобы избежать продуктов безопасности, которые полагаются на перехват API пользовательского режима (чем Elastic Defend не является):
process where event.action : "start" and
// EQL detecting a call stack not ending with ntdll.dll
not process.parent.thread.Ext.call_stack_summary : "ntdll.dll*" and
/* last call in the call stack contains bytes that execute a syscall
manually using assembly <mov r10,rcx, mov eax,ssn, syscall> */
_arraysearch(process.parent.thread.Ext.call_stack, $entry,
($entry.callsite_leading_bytes : ("*4c8bd1b8??????000f05",
"*4989cab8??????000f05", "*4c8bd10f05", "*4989ca0f05")))
Этот пример соответствует случаю, когда последняя область памяти в стеке вызовов не резервирована и содержит ассемблерные байты, которые заканчиваются инструкцией системного вызова (0F05):
Следующая логика правила определяет подозрительные расширения файлов, записанные процессом Microsoft Office из встроенного потока OLE, которые часто используются вредоносными документами для удаления полезных данных для первоначального доступа.
// EQL detecting file creation event with call stack indicating
// OleSaveToStream call to save or load the embedded OLE object
file where event.action != "deletion" and
process.name : ("winword.exe", "excel.exe", "powerpnt.exe") and
_arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info:
("*!OleSaveToStream*", "*!OleLoad*")) and
(
file.extension : ("exe", "dll", "js", "vbs", "vbe", "jse", "url",
"chm", "bat", "mht", "hta", "htm", "search-ms") or
/* PE & HelpFile */ file.Ext.header_bytes : ("4d5a*", "49545346*")
Пример совпадений:
Некоторые программы-вымогатели могут внедряться в подписанные процессы перед запуском процедуры шифрования. События переименования и изменения файлов будут казаться происходящими из доверенного процесса, что потенциально позволяет обойти некоторые эвристики, которые исключают подписанные процессы как предполагаемые ложные срабатывания. Следующий запрос KQL ищет переименование файлов документов из подписанного двоичного файла и с подозрительным стеком вызовов:
file where event.action : "rename" and
process.code_signature.status : "trusted" and file.extension != null and
file.Ext.original.name : ("*.jpg", "*.bmp", "*.png", "*.pdf", "*.doc",
"*.docx", "*.xls", "*.xlsx", "*.ppt", "*.pptx") and
not file.extension : ("tmp", "~tmp", "diff", "gz", "download", "bak",
"bck", "lnk", "part", "save", "url", "jpg", "bmp", "png", "pdf", "doc",
"docx", "xls", "xlsx", "ppt", "pptx") and
process.thread.Ext.call_stack_summary :
("ntdll.dll|kernelbase.dll|Unbacked",
"ntdll.dll|kernelbase.dll|kernel32.dll|Unbacked",
"ntdll.dll|kernelbase.dll|Unknown|kernel32.dll|ntdll.dll",
"ntdll.dll|kernelbase.dll|Unknown|kernel32.dll|ntdll.dll",
"ntdll.dll|kernelbase.dll|kernel32.dll|Unknown|kernel32.dll|ntdll.dll",
"ntdll.dll|kernelbase.dll|kernel32.dll|mscorlib.ni.dll|Unbacked",
"ntdll.dll|wow64.dll|wow64cpu.dll|wow64.dll|ntdll.dll|kernelbase.dll|
Unbacked", "ntdll.dll|wow64.dll|wow64cpu.dll|wow64.dll|ntdll.dll|
kernelbase.dll|Unbacked|kernel32.dll|ntdll.dll",
"ntdll.dll|Unbacked", "Unbacked", "Unknown")
Вот несколько примеров совпадений, в которых explorer.exe (Windows Explorer) внедряется программой-вымогателем KNIGHT/CYCLOPS:
Определенные типы вредоносных программ сохраняют свое присутствие, маскируясь под служебные библиотеки Windows. Чтобы служебная DLL распознавалась и управлялась диспетчером управления службами, она должна экспортировать функцию с именем ServiceMain. Приведенный ниже запрос KQL помогает идентифицировать случаи, когда создается исполняемый файл, а стек вызовов включает функцию ServiceMain.
Следующий запрос EQL идентифицирует загрузку неподписанной библиотеки службой очереди печати, где стек вызовов указывает, что загрузка поступает из SplAddMonitor. Злоумышленники могут использовать мониторы портов для запуска предоставленной злоумышленником DLL во время загрузки системы для обеспечения устойчивости или повышения привилегий.
library where
process.executable : ("?:\\Windows\\System32\\spoolsv.exe",
"?:\\Windows\\SysWOW64\\spoolsv.exe") and not dll.code_signature.status :
"trusted" and _arraysearch(process.thread.Ext.call_stack, $entry,
$entry.symbol_info: "*localspl.dll!SplAddMonitor*")
Пример совпадения:
Это правило EQL определяет загрузку библиотеки из необычных смещений win32u или ntdll. Это может указывать на попытку обойти мониторинг API с помощью гаджетов сборки возвратно-ориентированного программирования (ROP) для выполнения инструкции системного вызова из доверенного модуля.
library where
// adversaries try to use ROP gadgets from ntdll.dll or win32u.dll
// to construct a normal-looking call stack
process.thread.Ext.call_stack_summary : ("ntdll.dll|*", "win32u.dll|*") and
// excluding normal Library Load APIs - LdrLoadDll and NtMapViewOfSection
not _arraysearch(process.thread.Ext.call_stack, $entry,
$entry.symbol_info: ("*ntdll.dll!Ldr*",
"*KernelBase.dll!LoadLibrary*", "*ntdll.dll!*MapViewOfSection*"))
Этот пример соответствует ситуации, когда AtomLdr загружает DLL с помощью гаджетов ROP из win32u.dll вместо использования API-интерфейсов библиотеки загрузки ntdll (LdrLoadDll и NtMapViewOfSection).
[LdrpKernel32(https://github.com/rbmm/LdrpKernel32DllName) — это интересный метод, позволяющий перехватить раннее выполнение процесса на этапе начальной загрузки путем перезаписи имени начальной библиотеки DLL, на которое ссылается память ntdll.dll, — вынуждая процесс загрузить вредоносную DLL.
library where
// BaseThreadInitThunk must be exported by the rogue bootstrap DLL
_arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info :
"*!BaseThreadInitThunk*") and
// excluding kernel32 that exports normally exports BasethreadInitThunk
not _arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info
("?:\\Windows\\System32\\kernel32.dll!BaseThreadInitThunk*",
"?:\\Windows\\SysWOW64\\kernel32.dll!BaseThreadInitThunk*",
"?:\\Windows\\WinSxS\\*\\kernel32.dll!BaseThreadInitThunk*",
"?:\\Windows\\WinSxS\\Temp\\PendingDeletes\\*!BaseThreadInitThunk*",
"\\Device\\*\\Windows\\*\\kernel32.dll!BaseThreadInitThunk*"))
Пример совпадения:
Как и в примере с запланированным заданием, служба удаленного реестра размещается в svchost.exe. Мы можем использовать стек вызовов для обнаружения изменений в реестре, отслеживая, когда служба удаленного реестра указывает на исполняемый файл или файл сценария. Это может указывать на попытку перемещения в сторону посредством удаленного изменения конфигурации.
registry where
event.action == "modification" and
user.id : ("S-1-5-21*", "S-1-12-*") and
process.name : "svchost.exe" and
// The regsvc.dll in call stack indicate that this is indeed the
// svchost.exe instance hosting the Remote registry service
process.thread.Ext.call_stack_summary : "*regsvc.dll|rpcrt4.dll*" and
(
// suspicious registry values
registry.data.strings : ("*:\\*\\*", "*.exe*", "*.dll*", "*rundll32*",
"*powershell*", "*http*", "* /c *", "*COMSPEC*", "\\\\*.*") or
// suspicious keys like Services, Run key and COM
registry.path :
("HKLM\\SYSTEM\\ControlSet*\\Services\\*\\ServiceDLL",
"HKLM\\SYSTEM\\ControlSet*\\Services\\*\\ImagePath",
"HKEY_USERS\\*Classes\\*\\InprocServer32\\",
"HKEY_USERS\\*Classes\\*\\LocalServer32\\",
"H*\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\*") or
// potential attempt to remotely disable a service
(registry.value : "Start" and registry.data.strings : "4")
)
Этот пример соответствует случаю, когда значение реестра ключа запуска изменяется удаленно через службу удаленного реестра:
Стеки вызовов полезны не только для поиска известных плохих шаблонов, но также для уменьшения неоднозначности в стандартных событиях EDR и облегчения интерпретации поведения. Приведенные здесь примеры представляют собой лишь незначительную часть потенциальных возможностей обнаружения, которые могут быть достигнуты путем применения расширенного обогащения к тому же набору данных.
Чтобы взломать сеть Wi-Fi с помощью Kali Linux, вам нужна беспроводная карта, поддерживающая режим мониторинга…
Работа с консолью считается более эффективной, чем работа с графическим интерфейсом по нескольким причинам.Во-первых, ввод…
Конечно, вы также можете приобрести подписку на соответствующую услугу, но наличие SSH-доступа к компьютеру с…
С тех пор как ChatGPT вышел на арену, возросла потребность в поддержке чата на базе…
Если вы когда-нибудь окажетесь в ситуации, когда вам нужно взглянуть на спектр беспроводной связи, будь…
Elastic Security стремится превзойти противников в инновациях и обеспечить защиту от новейших технологий злоумышленников. В…