Всем привет! Меня зовут Сергей Кислухин, я работаю аналитиком 3 линии SOC, и мне есть чем поделиться в области реагирования на компьютерные инциденты на хостах под управлением Windows.

Введение

В своей работе, работники ИБ, особенно те, кто связаны с реагированием на инциденты, форензикой или compromise assessment, обязательно сталкиваются с необходимостью копаться в операционной системе глубже и быстрее, чем это делают остальные. Так, при реагировании на аномальное событие (или его поиске), специалисты, в подавляющих случаях, смотрят только журналы событий систем (локально или же через SIEM). И, если информации оттуда оказывается недостаточно, то прибегают к особым мерам, вроде локальных анализа дампа ОЗУ и анализа Amcache, Prefetch, MFT, AppCompatCache, UserAssist и т.д. 

К анализу реестра же прибегают, либо для ручного просмотра какой-то уникальной информации о хосте, либо для поиска событий с помощью сторонних автоматизированных утилит (Sysinternals Autoruns, PersistenceSniper, RegRipper), которые смотрят на определенный набор мест, которыми может воспользоваться злоумышленник. Этот вариант анализа реестра, а также прочие методы, в которых требуется просматривать вручную каждую ветку, я считаю неэффективными, и далее в статье я поясню почему, и покажу пример, как я решил эту проблему. 

Оглавление

Краткая теория

Реестр Windows это база данных системы, которая отслеживает информацию, касающуюся конфигурации, действий, пользователей и т. д. Состоит он из иерархического набора папок (веток), в которых хранятся другие папки, ключи и их значения. Физически, кусты (корневые ветки) и ветки хранятся в системе, в нескольких местах:

  • В оперативной памяти - куст HKEY_LOCAL_MACHINE\Hardware;

  • %SystemRoot%\System32\Config\* - основные ветки SYSTEM, SAM, SECURITY, SOFTWARE и DEFAULT куста HKEY_LOCAL_MACHINE;

  • C:\Users\%Username%\NTUSER.DAT - файлы кустов реестра пользователей HKEY_CURRENT_USER;

  • %AppData%\Local\Microsoft\Windows\UsrClass.dat - куст HKEY_CLASSES_ROOT (типы информации из подраздела HKEY_LOCAL_MACHINE\Software\Classes);

  • %SystemRoot%\AppCompat\Programs\Amcache.hve - файл ключа Amcache (содержит информацию о всех исполняемых файлах).

Значение ключа может иметь один из типов данных: 

  • Строка (REG_SZ) - Строки с буквенно-цифровыми символами;

  • Многострочное значение (REG_MULTI_SZ) - Список значений, а не однострочный текст;

  • Расширяемое строковое значение (REG_EXPAND_SZ) - Переменные, фактическое значение которых используется операционной системой во время выполнения или при необходимости;

  • Двоичные значения (REG_BINARY);

  • Шестнадцатеричные числа (DWORD и QWORD).

  • ...

Так как реестр содержит информацию, связанную с конфигурацией системы, то злоумышленники активно этим пользуются, добавляя или изменяя ветки в своих целях, что бы при определенных действиях выполнялась их злонамеренная программа или предоставлялся несанкционированный доступ к системе. Наиболее популярные ветки реестра, используемые во вредоносных целях описаны распределенно в различных техниках тактики Закрепления в матрице MITRE (Persistence, Tactic TA0003 | MITRE ATT&CK). Так, самой распространенной тактикой считается добавление ссылки на исполняемый файл в ветку реестра автозапуска системы HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run, в результате чего, указанная программа будет выполнятся каждый раз при включении операционной системы.

Для анализа реестра существует несколько утилит и способов:

  • Стандартный, встроенный в систему, для работы с реестром хоста, наживую:

    • %SystemRoot%\regedit.exe - позволяет просматривать и изменять, а также импортировать и экспортировать ветки и ключи реестра;

  • Сторонние аналоги, для работы с выгруженными файлами реестра, других хостов:

    • Registry Explorer - с графическим интерфейсом;

    • RECmd - консольный вариант;

  • Анализатор выгруженных файлов или веток реестра, по сторонним алгоритмам:

    • RegRipper;

    • Sysinternals Autoruns (требуется дополнительно выгрузка некоторых библиотек для работы);

  • Анализаторы работающей системы, по сторонним алгоритмам:

  • Анализ изменений, выполняемых процессами системы в событиях безопасности (требуется дополнительная предварительная настройка):

    • События Sysmon: 12 (Создание/удаление объекта реестра), 13 (Задание значения реестра), 14 (Переименование ключа и значения реестра);

    • События Security: 4657 (Значение реестра изменено);

  • Анализ реестра из дампа оперативной памяти:

Проблема

Потратив несколько вечеров на консолидацию всей информации из матрицы MITRE и дополнительный сбор, не описанных в ней методов автозапуска ПО, можно получить подобную картину по веткам реестра, которые могут использоваться для вредоносных целей (в скобках, при возможности, указано к какой технике MITRE ATT&CK относится данная ветка реестра):

Возможные места закрепления в реестре Windows
  • SOFTWARE\Classes\CLSID\* (# Event Triggered Execution: Component Object Model Hijacking T1546/015)

  • SOFTWARE\Classes\*\shell\*\command (# Event Triggered Execution: Change Default File Association T1546/001)

  • SOFTWARE\Microsoft\Active Setup\Installed Components\ (# Boot or Logon Autostart Execution: Active Setup T1547/014)

  • SOFTWARE\Microsoft\Active Setup\Installed Components\*\ShellComponent ()

  • SOFTWARE\Microsoft\Command Processor\AutoRun ()

  • SOFTWARE\Microsoft\Ctf\LangBarAddin\*\FilePath ()

  • SOFTWARE\Microsoft\Internet Explorer\Extensions\*\Exec ()

  • SOFTWARE\Microsoft\Internet Explorer\Extensions\*\Script ()

  • SOFTWARE\Microsoft\Netsh (# Event Triggered Execution: Netsh Helper DLL T1546/007)

  • SOFTWARE\Microsoft\Office\*\Word\Options\GlobalDotName (# Office Application Startup: Add-ins T1137/006)

  • SOFTWARE\Microsoft\Office\*\Word\Security\Trusted Documents (# Office Application Startup: Add-ins T1137/006)

  • SOFTWARE\Microsoft\Office\*\Word\Security\Trusted Locations\*\AllowSubFolders (# Office Application Startup: Add-ins T1137/006)

  • SOFTWARE\Microsoft\Office\*\Word\Security\Trusted Locations\*\Path (# Office Application Startup: Add-ins T1137/006)

  • SOFTWARE\Microsoft\Office\*\Word\Security\VBAWarning (# Office Application Startup: Add-ins T1137/006)

  • SOFTWARE\Microsoft\Office\*\Common\General\SharedTemplates (# Office Application Startup: Add-ins T1137/006)

  • SOFTWARE\Microsoft\Office\*\Common\General\UserTemplates (# Office Application Startup: Add-ins T1137/006)

  • SOFTWARE\Microsoft\Office test\Special\Perf (# Office Application Startup: Office Test T1137/002)

  • SOFTWARE\Microsoft\Windows CE Services\AutoStartOnConnect\MicrosoftActiveSync ()

  • SOFTWARE\Microsoft\Windows CE Services\AutoStartOnDisconnect\MicrosoftActiveSync ()

  • SOFTWARE\Microsoft\Windows nt\CurrentVersion\Appcompatflags\Custom (# Event Triggered Execution: Application Shimming T1546/011)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Appcompatflags\Installedsdb (# Event Triggered Execution: Application Shimming T1546/011)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options (# Event Triggered Execution: Accessibility Features T1546/008)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\*\VerifierDlls ()

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\IniFileMapping\system.ini\boot ()

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks (# Scheduled Task/Job: Scheduled Task T1053/005)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree (# Scheduled Task/Job: Scheduled Task T1053/005)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\ (# Event Triggered Execution: Image File Execution Options Injection T1546/012)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\* ()

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001; # Event Triggered Execution: AppInit DLLs T1546/010)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\IconServiceLib ()

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\Load (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\Run (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\ (# Boot or Logon Autostart Execution: Winlogon Helper DLL T1547/004)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\AppSetup (# Boot or Logon Autostart Execution: Winlogon Helper DLL T1547/004)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GpExtensions\*\DllName (# Boot or Logon Autostart Execution: Winlogon Helper DLL T1547/004)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Taskman (# Boot or Logon Autostart Execution: Winlogon Helper DLL T1547/004)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\VmApplet (# Boot or Logon Autostart Execution: Winlogon Helper DLL T1547/004)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\SYSTEM\Shell (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\Run (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\StartupApproved\Run (# RegRipper run.pl)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\StartupApproved\Run32 (# RegRipper run.pl)

  • Software\Microsoft\Windows\CurrentVersion\StartupApproved\StartupFolder (# RegRipper run.pl NTUSER)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Microsoft\Windows\Windows Error Reporting\Hangs\ReflectDebugger ()

  • SOFTWARE\Policies\Microsoft\Windows\Control Panel\Desktop\SCRNSAVE.EXE (# Event Triggered Execution: Screensaver T1546/002)

  • SOFTWARE\Policies\Microsoft\Windows\System\Scripts (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Policies\Microsoft\Windows\System\Scripts\Logoff\Script ()

  • SOFTWARE\Policies\Microsoft\Windows\System\Scripts\Logon\Script ()

  • SOFTWARE\Policies\Microsoft\Windows\System\Scripts\Shutdown\Script ()

  • SOFTWARE\Policies\Microsoft\Windows\System\Scripts\Startup\Script ()

  • SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run\ (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001)

  • SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run (# RegRipper run.pl)

  • SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ (# Event Triggered Execution: Image File Execution Options Injection T1546/012)

  • SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows (# Event Triggered Execution: AppInit DLLs T1546/010)

  • SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_Dlls ()

  • SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Winlogon\ (# Boot or Logon Autostart Execution: Winlogon Helper DLL T1547/004)

  • SYSTEM\ControlSet001\Control\BootVerificationProgram\ImagePath ()

  • SYSTEM\ControlSet001\Control\Print\Environments\[Windows architecture]\Print Processors\*\Driver (# Boot or Logon Autostart Execution: Print Processors T1547/012)

  • SYSTEM\ControlSet001\Control\Lsa\Notification Packages (# Modify Authentication Process: Password Filter DLL T1556/002)

  • SYSTEM\ControlSet001\Control\Lsa\OSConfig\Security Packages (# Boot or Logon Autostart Execution: Authentication Package T1547/002; # Boot or Logon Autostart Execution: Security Support Provider T1547/005)

  • SYSTEM\ControlSet001\Control\Lsa\Security Packages (# Boot or Logon Autostart Execution: Authentication Package T1547/002; # Boot or Logon Autostart Execution: Security Support Provider T1547/005)

  • SYSTEM\ControlSet001\Control\NetworkProvider\Order (# Modify Authentication Process: Network Provider DLL T1556/008)

  • SYSTEM\ControlSet001\Control\Print\Environments\[Windows architecture]\Print Processors\[user defined]\Driver (# Boot or Logon Autostart Execution: Print Processors T1547/012)

  • SYSTEM\ControlSet001\Control\Print\Monitors (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001; # Boot or Logon Autostart Execution: Port Monitors T1547/010)

  • SYSTEM\ControlSet001\Control\SafeBoot\AlternateShell ()

  • SYSTEM\ControlSet001\Control\ServiceControlManagerExtension ()

  • SYSTEM\ControlSet001\Control\Session Manager (# Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder T1547/001; # Event Triggered Execution: AppCert DLLs T1546/009)

  • SYSTEM\ControlSet001\Control\Session Manager\AppCertDLLs\* (# Event Triggered Execution: AppCertDLLs T1546/009)

  • SYSTEM\ControlSet001\Control\Session Manager\Environment\Path (# Hijack Execution Flow: Path Interception by PATH Environment Variable T1574/007)

  • SYSTEM\ControlSet001\Control\Session Manager\BootExecute ()

  • SYSTEM\ControlSet001\Control\Session Manager\Execute ()

  • SYSTEM\ControlSet001\Control\Session Manager\S0InitialCommand ()

  • SYSTEM\ControlSet001\Control\Session Manager\SetupExecute ()

  • SYSTEM\ControlSet001\Services (# Hijack Execution Flow: Services Registry Permissions Weakness T1574/011; # Create or Modify SYSTEM Process: Windows Service T1543/003)

  • SYSTEM\ControlSet001\Services\*\NetworkProvider (# Modify Authentication Process: Network Provider DLL T1556/008)

  • SYSTEM\ControlSet001\Services\W32Time\TimeProviders\ (# Boot or Logon Autostart Execution: Time Providers T1547/003)

  • SYSTEM\ControlSet001\Services\servicename\Parameters\ServiceDll (# Hijack Execution Flow: Services Registry Permissions Weakness T1574/011)

  • SYSTEM\ControlSet001\services\TermService\Parameters\ (# Server SOFTWARE Component: Terminal Services DLL T1505/005)

  • SYSTEM\ControlSet001\Control\Terminal Server\Wds\rdpwd\StartupPrograms ()

  • SYSTEM\ControlSet001\Control\Terminal Server\WinStations\RDP-Tcp\InitialProgram ()

  • SYSTEM\Setup\CmdLine ()

  • NTUSER.DAT\Environment\UserInitMprLogonScript (# Boot or Logon Initialization Scripts: Logon Script` (Windows) T1037/001)

  • NTUSER.DAT\Control Panel\Desktop\scrnsave.exe (# Event Triggered Execution: Screensaver T1546/002)

  • NTUSER.DAT\Software\* (Все ключи выше, начинающиеся с SOFTWARE)

  • NTUSER.DAT\System\* (Все ключи выше, начинающиеся с SYSTEM)

  • COR_ENABLE_PROFILING, COR_PROFILER и COR_PROFILER_PATH (# Hijack Execution Flow: COR_PROFILER T1574/012)

Как видно мест много и есть множество способов залезть внутрь реестра для их анализа, но ни один из них (по крайне мере без дополнительных доработок) не является идеальным и универсальным, т.к.:

  • Использовать утилиты для ручного просмотра реестра - неразумно из-за больших времязатрат на анализ каждого ключа, особенно если нужно исследовать много хостов;

  • Используя анализаторы, которые автоматически парсят файлы реестра (RegRipper, Autoruns, PersistenceSniper), у Вас никогда не будет уверенности, что заложенные алгоритмы проверяют все известные Вам места закрепления, особенно учитывая частоту их обновлений (как разработчиком, так и Вами). Как пример, я добавил вручную EICAR тестовый вирус в контекстное меню изменения текстовых файлов и запустил AtomicTest для техники T1546.011, и ни один их этих приемов, Autoruns не показал:

Autoruns показывает не все
Autoruns показывает не все
  • Если полагаться на события безопасности с изменениями, то они могут либо вообще отсутствовать (например удалены злоумышленником), либо быть не настроены на определенные ветки.

Таким образом, идеальное и универсальное решение должно подчиняться следующим критериям:

  • Уметь работать с выгруженными файлами реестра (доступ к работающему хосту не всегда будет, а выгрузить файлы можно даже с выключенных систем, с помощью Live CD);

  • Автоматически парсить файлы реестра, в соответствии с легко добавляемыми правилами парсинга (на случай обнаружения новых мест закрепления, которые не были учтены в прошлой версии);

  • Уметь парсить много файлов реестра с разных хостов (как для compromise assessment, так и для массового компьютерного инцидента);

  • Иметь удобный человекочитаемый вид и не требовать много действий для обработки (чтобы не было больно работать).

Предложенное решение

В результате проб и ошибок был реализован следующий автоматизированный конвейер по обработке файлов реестра:

  1. С хоста(-ов) выгружаются файлы реестра (в моем случае с помощью KAPE, в каталог E:\temp\%имя_хоста%\);

Пример скрипта.bat, лежащего рядом с папкой с KAPE по получению реестра
set “dest_folder=E:\temp\%COMPUTERNAME%
md “%dest_folder%”
cd /d%~dp0 
KAPE\kape.exe ‑tsource C: ‑tdest%dest_folder% ‑target RegistryHivesOther,RegistryHivesSystem,RegistryHivesUser

  1. Скриптом на Python перемещаются файлы реестра в каталог: E:\temp\%имя_хоста%\Reg\, а файлы реестра NTUSER и UsrClass каждого пользователя, переименовываются на имя пользователя владельца и оказываются в каталоге: E:\temp\%имя_хоста%\NTUSER\ и E:\temp\%имя_хоста%\UsrClass\, соответственно (если хостов немного, то можно и вручную сделать).

    • В каталогах должны также лежать файлы %Реестр%.LOG1 и %Реестр%.LOG2, для корректного парсинга сырых данных.

Пример скрипта по перемещению файлов реестра
import os
import shutil
for host_dir in os.scandir(r'E:\temp'): # Обход всех папок хостов
    if host_dir.is_dir():
        # Перемещение Реестра
        src_path = host_dir.path + '\C\Windows\System32\config'
        dst_path = host_dir.path + '\Reg'
        if os.path.exists(src_path):
            os.makedirs(dst_path, exist_ok=True)
            for file in os.scandir(src_path):
                shutil.move(file.path, dst_path)
        # Перемещение NTUSER
        users_folder = host_dir.path + r'\C\Users'
        if os.path.exists(users_folder):
            for user_dir in os.scandir(users_folder):
                if user_dir.is_dir():
                    for file_name in ["NTUSER.DAT", "ntuser.dat.LOG1", "ntuser.dat.LOG2"]:
                        file_path = os.path.join(user_dir.path, file_name)
                        if os.path.exists(file_path):
                            # Перемещаем файл в \NTUSER\
                            dst_folder = host_dir.path + r'\NTUSER'
                            os.makedirs(dst_folder, exist_ok=True)
                            if file_name == "NTUSER.DAT":
                                dst_file = os.path.join(dst_folder, user_dir.name + '.' + file_name.split('.')[-1])
                            else:
                                dst_file = os.path.join(dst_folder, user_dir.name + '.' + file_name.split('.')[-2].upper() + '.' + file_name.split('.')[-1])
                            shutil.move(file_path, dst_file)
        # Перемещение UsrClass
        users_folder = host_dir.path + r'\C\Users'
        if os.path.exists(users_folder):
            for user_dir in os.scandir(users_folder):
                if user_dir.is_dir():                                   
                    for file_name in ["UsrClass.dat", "UsrClass.dat.LOG1", "UsrClass.dat.LOG2"]:
                        file_path = os.path.join(user_dir.path, 'AppData\\Local\\Microsoft\\Windows\\', file_name)
                        if os.path.exists(file_path):
                            # Перемещаем файл в \UsrClass\
                            dst_folder = host_dir.path + r'\UsrClass'
                            os.makedirs(dst_folder, exist_ok=True)
                            if file_name == "UsrClass.dat":
                                dst_file = os.path.join(dst_folder, user_dir.name + '.' + file_name.split('.')[-1])
                            else:
                                dst_file = os.path.join(dst_folder, user_dir.name + '.' + file_name.split('.')[-2].upper() + '.' + file_name.split('.')[-1])
                            shutil.move(file_path, dst_file)

  1. Далее запускается конвейер скриптов на Python:

    • Вместо сотни веток реестра, указанных выше в статье, выгружается всего несколько десятков, из-за того, что убраны многие ветки, у которых есть общая родительская. Выгружать около сотни веток оказалось довольно долгим занятием (на каждый компьютер тратилось по 20 минут), т.к. на каждую ветку приходится отдельный запуск утилиты и проверка наличия данной ветки в файле реестра.

    • В текущей реализации скриптов, на каждую систему тратится на обработку, в среднем, по 3 минуты.

    • При необходимости, в код программы легко вносятся дополнительные ветки реестра, при обнаружении новых техник, которые ее будут использовать.

1. Запуск RECmd для необходимых файлов реестра, который выгружает только необходимые ветки
import os
import subprocess
from pathlib import Path

# Список ключей реестра, которые нужно парсить
registry_keys_software = [
    "Classes",
    "Microsoft\\Active Setup\\Installed Components",
    "Microsoft\\Command Processor\\Autorun",
    "Microsoft\\Ctf\\LangBarAddin",
    "Microsoft\\Internet Explorer\\Extensions",
    "Microsoft\\Netsh",
    "Microsoft\\Office",
    "Microsoft\\Office test\\Special\\Perf",
    "Microsoft\\Windows CE Services\\AutoStartOnConnect\\MicrosoftActiveSync",
    "Microsoft\\Windows CE Services\\AutoStartOnDisconnect\\MicrosoftActiveSync",
    "Microsoft\\Windows NT\\CurrentVersion",
    "Microsoft\\Windows\\CurrentVersion",
    "Microsoft\\Windows\\Windows Error Reporting\\Hangs\\ReflectDebugger",
    "Policies\\Microsoft\\Windows\\Control Panel\\Desktop",
    "Policies\\Microsoft\\Windows\\System\\Scripts",
    "Wow6432Node\\Microsoft\\Windows\\CurrentVersion",
    "Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion"
]

registry_keys_system = [
    "ControlSet001\\Control\\BootVerificationProgram",
    "ControlSet001\\Control\\Lsa\\Notification Packages",
    "ControlSet001\\Control\\Lsa\\OSConfig\\Security Packages",
    "ControlSet001\\Control\\Lsa\\Security Packages",
    "ControlSet001\\Control\\NetworkProvider\\Order",
    "ControlSet001\\Control\\Print\\Environments",
    "ControlSet001\\Control\\Print\\Monitors",
    "ControlSet001\\Control\\SafeBoot\\AlternateShell",
    "ControlSet001\\Control\\ServiceControlManagerExtension",
    "ControlSet001\\Control\\Session Manager",
    "ControlSet001\\Control\\Terminal Server\\Wds\\rdpwd\\StartupPrograms",
    "ControlSet001\\Control\\Terminal Server\\WinStations\\RDP-Tcp\\InitialProgram",
    "ControlSet001\\Services",
    "Setup\\CmdLine"
]

registry_keys_ntuserdat = [
    "Software\\Microsoft",
    "Software\\Policies\\Microsoft\\Windows\\System\\Scripts",
    "Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion",
    "Software\\Wow6432Node\\Microsoft\\Windows NT\\CurrentVersion",
    "System\\CurrentControlSet\\Control",
    "System\\CurrentControlSet\\Services",
    "System\\Setup\\CmdLine",
    "Environment",
    "Control Panel\\Desktop"
]

registry_keys_usrclassdat = [
    "ROOT"
]

base_dir = Path('E:/temp') # Базовая папка, где все лежит
reg_dir_name = 'Reg' # В какой папке искать кусты реестра хоста
ntuser_dir_name = 'NTUSER' # В какой папке искать кусты реестра пользователей
usrclass_dir_name = 'UsrClass'
special_regs = ['SOFTWARE', 'SYSTEM']  # Какие кусты реестра хоста парсить

# Обход поддиректорий базовой директории
for host_dir in base_dir.iterdir(): # подкаталог - имя хоста
    if host_dir.is_dir():
        host_name = host_dir.name
        reg_dir = host_dir / reg_dir_name # Поиск папки кустов реестра хоста
        ntuser_dir = host_dir / ntuser_dir_name # Поиск папки кустов реестра пользователя
        usrclass_dir = host_dir / usrclass_dir_name
        if reg_dir.exists():
            for reg_name in special_regs: # Обход software и system
                reg_file = reg_dir / reg_name 
                if reg_file.exists():
                    output_dir = base_dir / 'ToElastic' / host_name # Определение директории куда сливать результаты (закрепеление за именем хоста)
                    output_dir.mkdir(parents=True, exist_ok=True)
                    if reg_name == "SOFTWARE": # Парсинг Software
                       for key in registry_keys_software: # Только нужные ветки реестра
                            json_file_name = key.replace('\\', '%5C').replace(' ', '') # Замена плохих символов, мешающих работе

                            # Запуск команды RECmd.exe
                            cmd = (
                                f'E:/Scripts/2.Reg/RECmd.exe -f "{str(reg_file)}" --kn "{key}" --details --json "{output_dir}" --jsonf "Reg-Software%5C{str(json_file_name)}.json"'
                            )
                            with open(os.devnull, 'w') as FNULL:
                                subprocess.run(cmd, stderr=subprocess.STDOUT, stdout=FNULL)
                    if reg_name == "SYSTEM": # Аналогично Software
                        for key in registry_keys_system: # Только нужные ветки реестра
                            json_file_name = key.replace('\\', '%5C').replace(' ', '')
                            cmd = (
                                f'E:/Scripts/2.Reg/RECmd.exe -f "{str(reg_file)}" --kn "{key}" --details --json "{output_dir}" --jsonf "Reg-System%5C{str(json_file_name)}.json"'
                            )
                            with open(os.devnull, 'w') as FNULL:
                                subprocess.run(cmd, stderr=subprocess.STDOUT, stdout=FNULL)
        if ntuser_dir.exists(): # Обход кустов реестра пользователей
            for ntuser_file in ntuser_dir.iterdir():
                if ntuser_file.is_file() and not ntuser_file.name.endswith(("LOG1", "LOG2")): # Не обходить требующиеся для парсинга .dat.LOG
                    output_dir = base_dir / 'ToElastic' / host_name # Определение директории куда сливать результаты (закрепеление за именем хоста)
                    output_dir.mkdir(parents=True, exist_ok=True)
                    for key in registry_keys_ntuserdat: # Только нужные ветки реестра
                        json_file_name = f"Reg-Ntuser-{ntuser_file.stem}" # Определение имени нового файла, для сохранения имени пользователя
                        json_file_name = json_file_name.replace('.dat', '') + "%5C"
                        json_file_name += key.replace('\\', '%5C').replace(' ', '')

                        # Запуск команды RECmd.exe
                        cmd = (
                            f'E:/Scripts/2.Reg/RECmd.exe -f "{str(ntuser_file)}" --kn "{key}" --details --json "{output_dir}" --jsonf "{json_file_name}.json"'
                        )
                        with open(os.devnull, 'w') as FNULL:
                            subprocess.run(cmd, stderr=subprocess.STDOUT, stdout=FNULL)

        if usrclass_dir.exists(): # Обход кустов реестра пользователей
            for usrclass_file in usrclass_dir.iterdir():
                if usrclass_file.is_file() and not usrclass_file.name.endswith(("LOG1", "LOG2")): # Не обходить требующиеся для парсинга .dat.LOG
                    output_dir = base_dir / 'ToElastic' / host_name # Определение директории куда сливать результаты (закрепеление за именем хоста)
                    output_dir.mkdir(parents=True, exist_ok=True)
                    for key in registry_keys_usrclassdat: # Только нужные ветки реестра
                        json_file_name = f"Reg-UsrClass-{usrclass_file.stem}" # Определение имени нового файла, для сохранения имени пользователя
                        json_file_name = json_file_name.replace('.dat', '') + "%5C"
                        json_file_name += key.replace('\\', '%5C').replace(' ', '')

                        # Запуск команды RECmd.exe
                        cmd = (
                            f'E:/Scripts/2.Reg/RECmd.exe -f "{str(usrclass_file)}" --kn "{key}" --details --json "{output_dir}" --jsonf "{json_file_name}.json"'
                        )
                        with open(os.devnull, 'w') as FNULL:
                            subprocess.run(cmd, stderr=subprocess.STDOUT, stdout=FNULL)

2. Выгруженные ветки приводятся в адекватный вид (удаляются ненужные поля и значения, древовидная структура реестра приводится в ndjson вид, где на один ключ приходится одна строчка) и все файлы объединяются в один для каждого хоста

import json
import os

# Исключения по полям ValueName и ValueData, для удаления
with open("value_name_to_remove.txt", "r") as file:
    value_name_to_remove = [line.strip() for line in file.readlines()]

with open("value_data_to_remove.txt", "r") as file:
    value_data_to_remove = [line.strip() for line in file.readlines()]

def remove_fields(data): # Удаление ненужных полей "DataRaw" и "Slack", если они присутствуют
    if isinstance(data, dict):
        data.pop("DataRaw", None)
        data.pop("Slack", None)
        for key, value in data.items():
            data[key] = remove_fields(value)
    elif isinstance(data, list):
        data = [remove_fields(item) for item in data]
    return data

def remove_values(data):# Удаление ненужных значений и оставление нужных (в которых может быть путь/файл)
    value_types_to_keep = ["RegSz", "RegExpandSz", "RegMultiSz"]  # Указанные типы значения оставляем
    if "Values" in data:
        values = data["Values"]
        filtered_values = [value for value in values if ((value["ValueType"] in value_types_to_keep) and (not value["ValueName"] in value_name_to_remove) and (not value["ValueData"] in value_data_to_remove) and (not (value["ValueData"].isdigit() or not value["ValueData"]))) or value["ValueName"].endswith(".sdb")]
        data["Values"] = filtered_values
    return data

def is_empty(data):    # Удаление пустых значений "Values"
    return "Values" in data and not data["Values"]

def process_json(json_data): # Исправление многовложенности словаря
    json_data = remove_fields(json_data)
    json_data = remove_values(json_data)
    
    # Если есть SubKeys, обработаем их
    if 'SubKeys' in json_data:
        subkeys = json_data['SubKeys']
        del json_data['SubKeys']
        yield json_data

        for subkey in subkeys:
            yield from process_json(subkey)

def flatten_json(data): # Убирание словарей внутри ndjson
    if "Values" in data:
        values = data["Values"]
        if len(values) == 1:
            # Если только один набор значений в Values, вставляем его в родительский словарь
            value = values[0]
            data["ValueName"] = value["ValueName"]
            data["ValueType"] = value["ValueType"]
            data["ValueData"] = value["ValueData"]
            del data["Values"]
            yield data
        else:
            # Если несколько наборов значений в Values, дублируем родительский словарь для каждого набора
            for value in values:
                new_data = data.copy()
                new_data["ValueName"] = value["ValueName"]
                new_data["ValueType"] = value["ValueType"]
                new_data["ValueData"] = value["ValueData"]
                del new_data["Values"]
                yield new_data

def process_registry_file(input_file): # Основная функция открытия и обработки
    with open(input_file, 'r', encoding='utf-8') as file:
        data = json.load(file)

    filtered_data = [entry for entry in process_json(data) if not is_empty(entry)]
    flattened_data = [new_entry for entry in filtered_data for new_entry in flatten_json(entry)]

    return flattened_data

def main(input_directory):
    for dir_name in os.listdir(input_directory): # Перебор поддиректорий в указанной директории (Имена хостов)
        dir_path = os.path.join(input_directory, dir_name)
        if os.path.isdir(dir_path):
            registry_data = [] # Обнуление общего json фрейма

            for root, dirs, files in os.walk(dir_path): # Перебор файлов в текущей поддиректории (Файлы веток реестра)
                for file in files:
                    if file.startswith("Reg-"): # Поиск только файлов с реестром
                        registry_file = os.path.join(root, file)    
                        registry_data_temp = process_registry_file(registry_file) # json фрейм для определенного файла (для корректной замены ROOT)
                        
                        # Замена ROOT на соответствующее значение куста реестра
                        if file.startswith("Reg-Software"):
                            for entry in registry_data_temp:
                                entry["KeyPath"] = entry["KeyPath"].replace("ROOT", "Software")
                        elif file.startswith("Reg-System"):
                            for entry in registry_data_temp:
                                entry["KeyPath"] = entry["KeyPath"].replace("ROOT", "System")                        
                        elif file.startswith("Reg-Ntuser"):
                            username_start = file.find("Reg-Ntuser-") + len("Reg-Ntuser-")
                            username_end = file.find("%5C", username_start)
                            username = file[username_start:username_end]
                            for entry in registry_data_temp:
                                entry["KeyPath"] = entry["KeyPath"].replace("ROOT", f"NTUSER.DAT-{username}") 
                        elif file.startswith("Reg-UsrClass-"):
                            username_start = file.find("Reg-UsrClass-") + len("Reg-UsrClass-")
                            username_end = file.find("%5C", username_start)
                            username = file[username_start:username_end]
                            for entry in registry_data_temp:
                                entry["KeyPath"] = f"UsrClass-{username}-" + entry["KeyPath"]
                        
                        registry_data += registry_data_temp # Добавлнение скорректированного json фрейма в общий
                        os.remove(registry_file) # Удаление обработанных файлов

            output_file = os.path.join(dir_path, "Registry.json") # Создание отдельного Registry.json в директории хоста
            with open(output_file, 'w', encoding='utf-8') as out_file:
                for entry in registry_data:
                    # Запись каждого объекта JSON на новой строке
                    json.dump(entry, out_file, ensure_ascii=False, separators=(',', ':'))
                    out_file.write('\n')

if __name__ == "__main__":
    input_directory = r'E:\temp\ToElastic'  # Папка, в которой находятся файлы для обработки
    main(input_directory)

3. Переименование полей, форматирование времени
import os
import json
import jsonlines
from datetime import datetime, timedelta

def process_registry_file(file_path):
    updated_data = []
    with jsonlines.open(file_path, 'r') as reader:
        for entry in reader: # Переименование полей для выгрузки в ELK
            entry["reg.key.path"] = entry.pop("KeyPath")
            entry["reg.key.name"] = entry.pop("KeyName")
            timestamp = entry["LastWriteTimestamp"]
            timestamp = int(timestamp[6:-2]) / 1000 # Получение числа секунд из строки времени
            timestamp = datetime.fromtimestamp(timestamp) - timedelta(hours=3) # UTC-3
            entry["@timestamp"] = timestamp.strftime('%Y-%m-%dT%H:%M:%S') # Формат, понятный ELK
            entry.pop("LastWriteTimestamp")
            entry["file.name"] = entry.pop("ValueName")
            entry["file.path"] = entry.pop("ValueData")
            entry.pop("ValueType")
            updated_data.append(entry)
    return updated_data

def split_json_file(input_file, chunk_size): # Разделение на несколько json файлов
    with open(input_file, 'r', encoding='utf-8') as infile:
        data = [json.loads(line) for line in infile]
    total_records = len(data)
    num_chunks = (total_records + chunk_size - 1) // chunk_size # Определение на сколько частей разделять
    for i in range(num_chunks): # Создание ограниченных файлов json
        input_file_temp = input_file.replace('.json', '')
        output_file = f"{input_file_temp}{i + 1}.json"
        start = i * chunk_size
        end = (i + 1) * chunk_size
        chunk_data = data[start:end]
        with open(output_file, 'w', encoding='utf-8') as outfile:
            for item in chunk_data:
                outfile.write(json.dumps(item, ensure_ascii=False) + '\n')

def process_directory(directory_path, chunk_size): # Проверка необходимости разделения на блоки
    for root, dirs, files in os.walk(directory_path): # Поиск всех файлов обработанного реестра
        for filename in files:
            if filename == 'Registry.json':
                file_path = os.path.join(root, filename)
                with open(file_path, 'r', encoding='utf-8') as file:
                    line_count = sum(1 for line in file)
                if line_count > chunk_size: # Нужно ли разделять
                    split_json_file(file_path, chunk_size)
                    os.remove(file_path)

def main(input_directory, chunk_size):
    for root, dirs, files in os.walk(input_directory): # Поиск всех файлов обработанного реестра
        for file in files:
            if file == "Registry.json":
                registry_file = os.path.join(root, file)
                updated_data = process_registry_file(registry_file) # Переименование полей
                with jsonlines.open(registry_file, 'w') as writer:
                    writer.write_all(updated_data)
                process_directory(root, chunk_size) # Разделение на блоки 

if __name__ == "__main__":
    input_directory = r'E:\temp\ToElastic'
    chunk_size = 100000 # По сколько строк разделять json-файлы, для корректной выгрузки в ELK
    main(input_directory, chunk_size)

  1. В результате мы получаем существенный набор ключей реестра на анализ в следующем виде (названия полей легко меняются по Вашему желанию):

{"reg.key.path": "Software\\Classes\\.htm\\OpenWithList\\Excel.exe\\shell\\edit\\command", 
"reg.key.name": "command", 
"@timestamp": "2019-10-07T22:49:35", 
"file.name": "(default)", 
"file.path": "\"C:\\Program Files\\Microsoft Office\\Office15\\EXCEL.EXE\" /dde"}
  1. После проверки корректности обработки сырых данных в json файл, все файлы отправляются в ElasticStack (вручную, с помощью API или FileBeat) или другую, удобную Вам SIEM систему.

  2. В ElasticStack далее производится анализ:

    • C помощью фильтра по всем местам закрепления, или в котором ищется что-то конкретное (1). В реальных ситуациях можно искать в 3 захода: два захода с самыми шумными фильтрами (*Software*Classes*CLSID* (T1546/015) и *Software*Classes*shell*command (T1546/001)), и один со всем остальным. 

    • Удобный поиск проводится с помощью визуализации, в которой формируется таблица (2) по полю file.path (3), которая равна выполняемой команде / ссылке на файл (Группировка событий).

    • Отсортировав по возрастанию количества раз встречаемости (4), можно легко найти исполняемые файлы, которые не должны присутствовать на компьютере (наиболее встречаемые команды = бОльшая вероятность ее легитимности).

    • Затем, поправив фильтр, что бы найти только нужные файлы, можно перейти в список всех событий для более глубокого анализа (5).

Группировка выполняемых команд / ссылок на файлы в местах закрепления
Группировка выполняемых команд / ссылок на файлы в местах закрепления
  1. В событиях уже показывается в какой ветке реестра находится нужная ссылка/команда и когда она была добавлена, из чего можно делать выводы - легитимно это или нет. 

Найденные пути к веткам реестра, в которых закрепился вредонос
Найденные пути к веткам реестра, в которых закрепился вредонос
  1. Здесь показан лишь упрощенный поиск для примера, а то время как реальные кейсы поиска ограничены только Вашей фантазией. Кстати, фильтр для поиска не шумных мест закрепления:

Фильтр поиска закрепления в реестре
reg.key.path : (*Software*Microsoft*Active*Setup*Installed*Components
OR *Software*Microsoft*Active*Setup*Installed*Components*ShellComponent
OR *Software*Microsoft*Command*Processor*Autorun
OR *Software*Microsoft*Ctf*LangBarAddin*FilePath
OR *Software*Microsoft*Internet*Explorer*Extensions*Exec
OR *Software*Microsoft*Internet*Explorer*Extensions*Script
OR *Software*Microsoft*NetSh
OR *Software*Microsoft*Office*Word*Options*GlobalDotName
OR *Software*Microsoft*Office*Word*Security
OR *Software*Microsoft*Office*Word*Security*Trusted*Documents
OR *Software*Microsoft*Office*Word*Security*Trusted*Locations
OR *Software*Microsoft*Office*Word*Security*Trusted*Locations*
OR *Software*Microsoft*Office*Word*Security*Trusted*Locations*AllowSubFolders
OR *Software*Microsoft*Office*Word*Security*Trusted*Locations*Path
OR *Software*Microsoft*Office*Word*Security*VBAWarning
OR *Software*Microsoft*Office*Common*General*SharedTemplates
OR *Software*Microsoft*Office*Common*General*UserTemplates
OR *Software*Microsoft*Office*test*Special*Perf
OR *Software*Microsoft*Windows*Services*AutoStartOnConnect*MicrosoftActiveSync
OR *Software*Microsoft*Windows*Services*AutoStartOnDisconnect*MicrosoftActiveSync
OR *Software*Microsoft*Windows*CurrentVersion*AppCompatFlags*Custom*
OR *Software*Microsoft*Windows*CurrentVersion*AppCompatFlags*Installedsdb
OR *Software*Microsoft*Windows*CurrentVersion*Image*File*Execution*Options
OR *Software*Microsoft*Windows*CurrentVersion*Image*File*Execution*Option*VerifierDlls
OR *Software*Microsoft*Windows*CurrentVersion*IniFileMapping*system.ini*boot
OR *Software*Microsoft*Windows*CurrentVersion*Schedule*TaskCache*Tasks
OR *Software*Microsoft*Windows*CurrentVersion*Schedule*TaskCache*Tree
OR *Software*Microsoft*Windows*CurrentVersion*SilentProcessExit*
OR *Software*Microsoft*Windows*CurrentVersion*Terminal*Server*Install*
OR *Software*Microsoft*Windows*CurrentVersion*Windows*AppInit_DLLs
OR *Software*Microsoft*Windows*CurrentVersion*Windows*IconServiceLib
OR *Software*Microsoft*Windows*CurrentVersion*Windows*Load
OR *Software*Microsoft*Windows*CurrentVersion*Windows*Run
OR *Software*Microsoft*Windows*CurrentVersion*Winlogon
OR *Software*Microsoft*Windows*CurrentVersion*Winlogon*AppSetup
OR *Software*Microsoft*Windows*CurrentVersion*Winlogon*GpExtensions*
OR *Software*Microsoft*Windows*CurrentVersion*Winlogon*Taskman
OR *Software*Microsoft*Windows*CurrentVersion*Winlogon*VmApplet
OR *Software*Microsoft*Windows*CurrentVersion*Winlogon*Shell
OR *Software*Microsoft*Windows*CurrentVersion*Winlogon*Userinit
OR *Software*Microsoft*Windows*CurrentVersion*Explorer*Shell*Folders
OR *Software*Microsoft*Windows*CurrentVersion*Explorer*User*Shell*Folders
OR *Software*Microsoft*Windows*CurrentVersion*Policies*Explorer*Run
OR *Software*Microsoft*Windows*CurrentVersion*Policies*System*Shell
OR *Software*Microsoft*Windows*CurrentVersion*Run
OR *Software*Microsoft*Windows*CurrentVersion*RunOnce
OR *Software*Microsoft*Windows*CurrentVersion*RunOnceEx
OR *Software*Microsoft*Windows*CurrentVersion*RunServices
OR *Software*Microsoft*Windows*CurrentVersion*RunServicesOnce
OR *Software*Microsoft*Windows*CurrentVersion*ShellServiceObjectDelayLoad
OR *Software*Microsoft*Windows*CurrentVersion*policies*Explorer*Run
OR *Software*Microsoft*Windows*Windows*Error*Reporting*Hangs*ReflectDebugger
OR *Software*Policies*Microsoft*Windows*Control*Panel*Desktop*SCRNSAVE.EXE
OR *Software*Policies*Microsoft*Windows*System*Scripts
OR *Software*Policies*Microsoft*Windows*System*Scripts*Logoff*Script
OR *Software*Policies*Microsoft*Windows*System*Scripts*Logon*Script
OR *Software*Policies*Microsoft*Windows*System*Scripts*Shutdown*Script
OR *Software*Policies*Microsoft*Windows*System*Scripts*Startup*Script
OR *Software*Wow6432Node*Microsoft*Windows*CurrentVersion*Run*
OR *Software*Wow6432Node*Microsoft*Windows*CurrentVersion*Image*File*Execution*Options*
OR *Software*Wow6432Node*Microsoft*Windows*CurrentVersion*Windows
OR *Software*Wow6432Node*Microsoft*Windows*CurrentVersion*Windows*AppInit_Dlls
OR *Software*Wow6432Node*Microsoft*Windows*CurrentVersion*Winlogon*
OR *System*ControlSet001*Control*BootVerificationProgram*ImagePath
OR *System*ControlSet001*Control*Print*Environments*Print*Processors*Driver
OR *System*ControlSet001*Control*Lsa*Notification*Packages
OR *System*ControlSet001*Control*Lsa*OSConfig*Security*Packages
OR *System*ControlSet001*Control*Lsa*Security*Packages
OR *System*ControlSet001*Control*NetworkProvider*Order
OR *System*ControlSet001*Control*Print*Environments***Print*Processors***Driver
OR *System*ControlSet001*Control*Print*Monitors
OR *System*ControlSet001*Control*SafeBoot*AlternateShell
OR *System*ControlSet001*Control*ServiceControlManagerExtension
OR *System*ControlSet001*Control*Session*Manager
OR *System*ControlSet001*Control*Session*Manager*AppCertDLLs**
OR *System*ControlSet001*Control*Session*Manager*Environment*Path
OR *System*ControlSet001*Control*Session*Manager*BootExecute
OR *System*ControlSet001*Control*Session*Manager*Execute
OR *System*ControlSet001*Control*Session*Manager*KnownDLLs
OR *System*ControlSet001*Control*Session*Manager*S0InitialCommand
OR *System*ControlSet001*Control*Session*Manager*SetupExecute
OR *System*ControlSet001*Services
OR *System*ControlSet001*Services*NetworkProvider
OR *System*ControlSet001*Services*W32Time*TimeProviders*
OR *System*ControlSet001*Services*servicename*Parameters*ServiceDll
OR *System*ControlSet001*services*TermService*Parameters*
OR *System*ControlSet001*Control*Terminal*Server*Wds*rdpwd*StartupPrograms
OR *System*ControlSet001*Control*Terminal*Server*WinStations*RDP-Tcp*InitialProgram
OR *System*Setup*CmdLine
OR *Environment*UserInitMprLogonScript
OR *Control*Panel*Desktop)

Демонстрация на примерах

Пример того как в моем решении видны изменения контекстного меню - представлены выше. 

Для дальнейшей демонстрации были взяты 3 техники, применённые с помощью AtomicTests, которые не детектируются с помощью Autoruns:

  • T1546.011 (Злоупотребление подсистемой совместимости приложений), рассмотренная ранее в разделе с Проблемой;

  • T1546.002 (Манипуляция параметрами экранной заставки);

  • T1546.007 (Злоупотребление вспомогательными библиотеками DLL Netsh):

Autoruns опять показывает не все
Autoruns опять показывает не все
  1. Собрав, обработав и отправив в ElasticStack все необходимые данные, при поиске всех мест закрепления (кроме 2 самых шумящих), получаем следующее: 

Числа считаются аномальными, т.к. они фильтруются при парсинге
Числа считаются аномальными, т.к. они фильтруются при парсинге
  1. Необходимые нам пути к файлам и аномальные ключи находятся наверху списка. Совершив поиск по этим событиям, сразу находим где находятся данные значения в реестре: 

Результат
Результат
  1. Для увеличения удобства дальнейшего поиска, все легитимные значения для Вашего "золотого" образа систем Вашей организации легко добавляются в исключения

Выводы

На текущий момент существует множество утилит для анализа реестра Windows на поиск мест закрепления в нем вредоносного ПО. Наиболее удобным и эффективным является Sysinternals Autoruns, однако как было продемонстрировано, даже он не способен детектировать все, что нам нужно. 

Для решения этой проблемы был продемонстрирован автоматизированный конвейер скриптов по обработке сырых файлов реестра Windows в SIEM-понятный формат данных. Далее, в SIEM-е, при правильно настроенных исключениях, поиск вредоноса не составит большого труда.

На обратной стороне монеты же, безусловно выделяется бОльшая сложность в подготовке и дальнейшем анализе, по сравнению с тем же Autoruns. Так что если в Вашей работе, скорость анализа играет решающую роль, то предложенное решение можно рассматривать в качестве дополнения к существующим решениям, в которых можно проводить первичный анализ.

Ссылки на источники

Комментарии (1)


  1. msuhanov
    04.11.2023 12:49

    В каталогах должны также лежать файлы %Реестр%.LOG1 и %Реестр%.LOG2, для корректного парсинга сырых данных.

    Целесообразно исследовать два состояния куста реестра: до и после применения логов. С учетом того, что разница между состояниями может составлять час непрерывной работы ОС — без учета спящего режима (т. е. реально разница между состояниями может быть более суток, особенно с ноутбуками).

    Можно еще промежуточные состояния добавлять (т. е. применять записи из логов поэтапно и на каждом этапе парсить куст еще раз), но это уже избыточно в большинстве случаев, куда разумнее смотреть еще на теневые копии и в RegBack.

    Запуск RECmd для необходимых файлов реестра, который выгружает только необходимые ветки

    Но зачем? RECmd и Registry Explorer восстанавливают меньше удаленных данных, чем реально есть.

    Попробуйте сделать from yarp import *. Плюс, у вас "открываются" дополнительные метаданные (вроде "этот ключ реестра ничто не открывало с такой-то даты").

    "file.name": "(default)",

    А чем строка "(default)", полученная из пустой строки (для значения без имени), отличается от строки "(default)" (значение именно с таким именем)?