Файл дампа процесса что это
Файл дампа — это моментальный снимок, показывающий выполнявшийся процесс и загруженные для приложения модули в определенный момент времени. Дамп со сведениями о куче также содержит моментальный снимок памяти приложения на этот момент.
Открытие файла дампа с кучей в Visual Studio в чем-то подобно остановке в точке останова во время сеанса отладки. Хотя вы не можете продолжить выполнение, но можете проверить стеки, потоки и значения переменных приложения на момент создания дампа.
В основном дампы используются для отладки проблем на компьютерах, к которым у разработчиков нет доступа. Если вы не можете воспроизвести на своем компьютере аварийное завершение или зависание программы, возникшие на компьютере клиента, вы можете записать файл дампа с его компьютера. Дампы также создаются тест-инженерами, чтобы сохранить данные для дополнительного тестирования.
Отладчик Visual Studio может сохранять файлы дампа для управляемого и машинного кода. Он может отлаживать файлы дампа, созданные Visual Studio или другими приложениями, способными сохранять файлы в формате минидампа.
Установка WinDbg в Windows
Утилита, являющаяся отладчиком для юзермодных приложений и драйверов, позволяет проанализировать снимок памяти и выяснить, что спровоцировало BSoD. Поставляется она в составе пакета SDK для Windows 10, инсталлятор скачивается на сайте Microsoft. Для Семёрки и ранних версий систем WinDbg можно найти в пакете Microsoft Windows SDK for Windows 7 and NET Framework 4.
Анализ дампов
Вы можете анализировать дампы с помощью dotnet-dump средства CLI или с помощью Visual Studio.
Если необходима отладка машинного кода, можно использовать расширение отладчика SOS совместно с LLDB в Linux и macOS. SOS также поддерживается в Windows c Windbg/cdb, хотя рекомендуется использовать Visual Studio.
Как удалить файлы дампа памяти
Если понадобилось удалить memory dump, это можно выполнить вручную, пройдя по пути месторасположения объекта на диске. Так, в системном каталоге Windows нужно найти и удалить файл MEMORY.DMP, а также элементы в каталоге Minidump. Кроме того, можно использовать штатный инструмент системы «Очистка диска»:
- вызываем консоль «Выполнить» (Win+R) и вводим команду «Cleanmgr», чтобы перейти к службе;
- жмём кнопку очищения системных файлов, затем находим и отмечаем в списке строчки, касающиеся memory dump. Если не нашлось, значит, их не создавали.
Создание снимков бывает отключено, даже если вы когда-либо активировали эту функцию по причине деятельности специального софта. Если речь о SSD-накопителе, это могут быть программы для работы с твердотельными дисками. Отключение некоторых опций ОС выполняется ими с целью оптимизации работы, поскольку многократные процессы чтения/записи сокращают продолжительность жизни диска. Также причиной отключения дампа памяти могут быть различные программы очистки компьютера и оптимизации системы.
Настройка сервера отладочных символов
Отладочные символы, которые генерируются в процессе компиляции приложения вместе с исполняемым файлом, нужны при отладке. Настраиваем WinDbg на извлечение символов из сети:
Physmem2profit
Последним творением, которое мы сегодня рассмотрим, будет проект Physmem2profit от F-Secure LABS. Его подход к дампу LSASS отличается от остальных тем, что вместо того, чтобы сосредотачиваться на методах уклонения от хуков AV / EDR в userland, он использует драйвер WinPmem (часть форензик-проекта rekall) для получения доступа ко всей физической памяти целевого узла и ищет там область, соответствующую памяти процесса lsass.exe, через монтирование виртуальной ФС FUSE.
Покажем в действии, как заставить это чудо работать:
Для начала клонируем репозиторий проекта, рекурсивно разрешая зависимости в виде git-подмодулей.
Далее исправим версии библиотек acora и pycryptodome в зависимостях rekall-core , чтобы они дружили с актуальным Python 3.
Теперь можно запустить инсталлер, который накатит питонячую виртуальную среду и поставит все, что ему нужно.
Установка Physmem2profit
Следуя рекомендациям из этого issue, я скачал крайний релиз WinPmem (нам понадобится только файл kernel/binaries/winpmem_x64.sys ) и обновил эти константы для изменившегося интерфейса взаимодействия с драйвером. Внесенные изменения можно посмотреть в моем форке проекта.
Также среди внесенных изменений — захардкоженный файл драйвера, который автоматически кладется в файловую систему «жертвы» перед установкой соответствующей службы и стирается после ее остановки и удаления:
Смотрим, как всем этим пользоваться:
Дампим LSASS с помощью Physmem2profit
Вуаля, хеши из LSASS получены!
Краткий ликбез
Если не сильно углубляться в теорию, то Local Security Authority Subsystem Service (он же LSASS) — это процесс (исполняемый файл C:\Windows\System32\lsass.exe ), ответственный за управление разными подсистемами аутентификации ОС Windows. Среди его задач: проверка «кред» локальных и доменных аккаунтов в ходе различных сценариев запроса доступа к системе, генерация токенов безопасности для активных сессий пользователей, работа с провайдерами поддержки безопасности (Security Support Provider, SSP) и др.
Несмотря на то, что в 2к22 при успешном дампе LSASS злоумышленнику чаще всего остается довольствоваться NT-хешами и билетами Kerberos, это все равно с большой вероятностью позволит ему повысить свои привилегии в доменной среде AD за короткий промежуток времени. Реализуя схемы Pass-the-Hash, Overpass-the-Hash и Pass-the-Ticket, злоумышленник может быстро распространиться по сети горизонтально, собирая по пути все больше хешей и «тикетов», что в конечном итоге дарует ему «ключи от Королевства» в виде данных аутентификации администратора домена.
Ассоциирование файлов .dmp с WinDbg
Для того чтобы объекты при нажатии на них открывались посредством утилиты:
-
В консоли командной строки, запущенной от имени администратора (например, через меню Пуск) выполняем команды (зависимо от разрядности ОС):
cd C:\Progran Files (x86)\Windows Kits\10\Debuggers\x64
exe –IA
cd C:\Progran Files (x86)\Windows Kits\10\Debuggers\x86
exe –IA
Теперь файлы типов .DMP, .HDMP, .MDMP, .KDMP, .WEW будут ассоциироваться с приложением.
Требования и ограничения
- Для отладки файлов дампа, полученных с 64-разрядных компьютеров, необходимо запустить Visual Studio на 64-разрядном компьютере.
- Visual Studio поддерживает отладку файлов дампа управляемых приложений в Linux.
Visual Studio поддерживает отладку файлов дампа, создаваемых приложениями в машинных кодах на устройствах ARM. Он также поддерживает отладку дампов управляемых приложений с устройств ARM, но только в отладчике машинного кода.
Для отладки файлов дампа, полученных в режиме ядра, или использования расширения отладки SOS.dll в Visual Studio загрузите средства отладки для Windows из комплекта разработки драйверов для Windows (WDK).
Visual Studio не поддерживает отладку файлов дампа, сохраненных в старом формате полного дампа в режиме пользователя. Полный дамп в режиме пользователя не то же самое, что и дамп с кучей.
Отладка с использованием файлов дампа оптимизированного кода может сопровождаться ложной информацией. К примеру, встраивание компилятором функций может приводить к непредвиденным стекам вызовов, а другие виды оптимизации могут изменять время существования переменных.
Для чего нужен дамп памяти Windows
Содержимое оперативной памяти и материалы, касающиеся сбоя, могут писаться в файл подкачки, при следующем старте операционки создаётся аварийный дамп с информацией об отладке, сформированной на базе сохранённых данных (ОС может создавать memory dump и минуя файл подкачки). В журнале событий будет сделана запись об ошибке, если данная опция настроена.
Вывод участка дампа 32-х битной ОС Windows с помощью программы Debug.exe
Тип записываемого дампа может задаваться в свойствах ОС, поддерживаются варианты:
- Малый дамп памяти. Включает немного сведений, в частности это код ошибки с параметрами, список установленных в Виндовс драйверов и т. д., но этой информации бывает достаточно для выявления источника проблемы. Элемент, как правило, будет записан в каталоге C:\Windows\Minidump.
- Дамп памяти ядра. Выполняется сохранение сведений оперативной памяти, связанных только с режимом ядра, исключая информацию, не указывающую на источник появления сбоя.
- Полный дамп системы. Содержимым является вся память операционки, что может создать проблемы при создании снимка, если объём ОЗУ составляет более 4Гб. Обычно пишется в файл C:\Windows\MEMORY.DMP.
- Автоматический дамп памяти (стал доступным с восьмой версии Виндовс). Содержит те же записи, что и memory dump ядра, при этом отличается способом управления системой размером файла подкачки.
- Активный дамп памяти (представлен в «Десятке»). Содержит только активную память хоста из режимов ядра и пользователя* (возможность была изначально реализована для серверов, чтобы при диагностике в дамп не попадали виртуальные машины).
*Дамп пользовательского режима представляет собой дамп определённого процесса. Так, содержимым может являться полная память процесса или фрагмент, список, стек, состояние потоков, списки библиотек, состояние потоков, дескрипторы объектов ядра.
ВАЖНО. При отказе диска или возникновении BSoD на первой стадии запуска системы аварийный дамп создан не будет.
Как настроить дамп памяти в Windows
Настройки действий, производимых при аварийной остановке работы ОС, выполняются в том же окне, что и включение создания memory dump («Загрузка и восстановление»), куда мы попадаем из свойств системы.
Здесь можно настроить параметры запуска ОС и назначить определённые действия в случае её отказа, например:
- указать режим записи дампа со сведениями отладки (по умолчанию выбран автоматический, но может быть выставлено значение «Нет»);
- записать события в журнал (записи добавляются в логи);
- отмеченный пункт «Выполнить автоматическую перезагрузку» позволяет системе перезагрузиться после сбоя и продолжить функционировать;
- при выборе опции «Заменять существующий файл дампа», объект будет подвергаться перезаписи при каждой появляющейся ошибке.
При эксплуатации SSD лучше оставить тип записи «Автоматический дамп памяти», но если нужен файл аварийного дампа, лучше выставить «Малый дамп памяти», он самый лёгкий и его несложно переслать другому пользователю, если вам нужна помощь в анализе состояния.
Иногда может потребоваться увеличение размера файла подкачки больше, чем доступно в оперативке, чтобы он соответствовал полному дампу.
Прочитать memory dump можно посредством специализированных утилит, таких как Microsoft Kernel Debugger, BlueScreenView и других.
Mimikatz
Было бы преступлением не начать повествование с такого мастодонта в области потрошения подсистем аутентификации Windows как Mimikatz, которым хоть раз пользовался любой пентестер.
Модуль sekurlsa::logonpasswords позволяет «налету» парсить память lsass.exe с целью поиска секретиков без сохранения соответствующего дампа на диск. Этот инструмент поистине произвел революцию в наступательных операциях и положил начало многим другим исследованием в области извлечения чувствительной информации с хостов под управлением Windows.
Использование Mimikatz (logonpasswords)
На заметку: официальная вики Mimikatz покрывает далеко не все его возможности, поэтому энтузиасты InfoSec-комьюнити создали вот такой замечательный ресурс, которым я рекомендую пользоваться в случае возникновения вопросов, что делает та или иная команда этого замечательного инструмента.
comsvcs.dll
Безусловно, интересной находкой стало обнаружение экспорта функции MiniDumpW в системной библиотеке C:\Windows\System32\comsvcs.dll , которая дергает вызов Win32 API MiniDumpWriteDump и позволяет делать слепки процессов в рамках концепции Living Off The Land Binaries And Scripts (LOLBAS), когда злоумышленнику не нужно приносить ничего лишнего на атакуемую машину.
Анализ библиотеки comsvcs.dll с помощью PE-bear
Эта библиотека легла в основу первых версий замечательной утилиты lsassy, позволяющей делать слепки LSASS и удаленно читать необходимые области памяти созданного дампа, а не перенаправлять его целиком на машину атакующего (подробнее о принципе работы можно почитать в блоге автора утилиты).
Если взглянуть на код, можно найти суперские «однострочники» для Cmd и PowerShell, которые автоматически позволяют получить идентификатор процесса lsass.exe и сдампить его память по заданному пути.
Примечание: лучше пользоваться PowerShell-версией команды, так как для оболочки PowerShell в отличии от Cmd по дефолту включена привилегия SeDebugPrivilege для привилегированной сессии шелла, которая понадобится для доступа к памяти lsass.exe.
Дампим LSASS с помощью LOLBAS-техники comsvcs.dll
Стоит ли говорить, что создание дампа по такой простой технике, разумеется, будет предотвращено хотя бы мало-мальски неравнодушным антивирусом?
См. также раздел
В руководстве по отладке дампов Linux представлены пошаговые инструкции по отладке дампа, собранного в Linux.
Потоки
Сразу после блока SMR следует список потоков. Первый поток в нашем списке — Reference Handler:
Краткое описание потока
Состояние потока
Вторая строка — это текущее состояние потока. Возможные состояния потока приведены в enum:
Thread.State:
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
Более подробную информацию смотрите в документации.
Thread Stack Trace
Следующая секция содержит stack trace потока в момент снятия дампа. Этот stack trace очень похож на stack trace, который формируется не перехваченным исключением. И содержит имена классов и строк, которые выполнялись в момент формирования дампа. В случае потока Reference Handler мы не видим ничего интересного.
Однако в трассировке потока Thread-02 есть кое-что интересное, отличное от стандартного трейса:
В трассировке мы видим, что добавилась информация о блокировке. Этот поток ожидает блокировку на объекте с адресом 0x00000000894465b0 (тип объекта java.lang.Object). Более того поток сам удерживает блокировку с адресом 0x00000000894465a0 (тоже объект java.lang.Object). Эта информация нам пригодится далее для диагностики deadlock-а.
Захваченные примитивы синхронизации (Ownable Synchronizer)
В последней секции приводится список примитивов синхронизации (synchronizers), захваченных потоком. Это объекты, которые могут быть использованы для синхронизации потоков, например, защелки (locks).
В соответствии с официальной документацией Java, Ownable Synchronizer — это наследники AbstractOwnableSynchronizer (или его подкласса), которые могут быть эксклюзивно захвачены потоком для целей синхронизации.
ReentrantLock и write-lock, но не read-lock класса ReentrantReadWriteLock — два хороших примера таких «ownable synchronizers», предлагаемых платформой.
Для получения более подробной информации по этому вопросу можно обратиться к этому
посту.
Потоки JVM
В следующей секции дампа содержится информация о технических потоках JVM, которые не являются частью приложения и связаны с потоками операционной системы. Т.к. эти потоки работают вне приложения, у них нет идентификаторов потока. Чаще всего это потоки сборщика мусора и другие технические потоки JVM:
Глобальные ссылки JNI
В этой секции указывается количество глобальных ссылок, используемых JVM через JNI. Эти ссылки не обслуживаются сборщиком мусора и в определенных обстоятельствах могут стать причиной утечки памяти.
В большинстве простых случаев эта информация не используется. Однако важность глобальных ссылок надо понимать. Более подробную информацию смотрите в этом посте.
Взаимно заблокированные (Deadlocked) потоки
В первом подразделе описывается сценарий взаимной блокировки (deadlock):
Поток Thread-0 ожидает возможность захватить монитор (это обращение к блоку synchronized(secondResource) в нашем приложении), в то же время этот поток удерживает монитор, который пытается захватить поток Thread-1 (это обращение к тому же фрагменту кода: synchronized(secondResource) в нашем приложении).
Эта циклическая блокировка по другому называется deadlock. На рисунке ниже
эта ситуация представлена в графическом виде:
Во втором подразделе для обоих заблокированных потоков приведен stack trace.
Этот stack trace позволяет нам проследить за работой каждого потока до появления блокировки.
В нашем случае, если мы посмотрим на строку:
Это приложение завершится без взаимной блокировки, и в качестве результата мы получим следующий вывод (обратите внимание на то, что адреса экземпляров класса Object изменились):
Экскурс в историю дампов LSASS
Рассмотрим первопроходцев в ремесле извлечения данных аутентификации из памяти LSASS.
ProcDump
Другим фаворитом внутренних пентестов долгое время был метод создания снимка памяти LSASS с помощью служебной программы ProcDump из состава Windows Sysinternals. Этот инструмент позволяет создавать дампы процессов с целью их дальнейшего анализа, и процесс lsass.exe тому не исключение (если права позволяют, разумеется, хе-хе).
Создание слепка памяти процесса lsass.exe
Теперь можно притащить слепленный дамп к себе на тачку и распарсить его с помощью того же Mimikatz.
Парсим lsass.dmp с помощью Mimikatz
Или его аналога для Linux – Pypykatz.
Парсим lsass.dmp с помощью Pypykatz
Прелесть этого метода заключается в том, что все необходимые операции по созданию слепка памяти выполняет ProcDump, подписанный Microsoft, и этичному взломщику не требуется тащить на хост никакой малвари. Однако разработчики корпоративных антивирусных решений тоже долго не стояли в стороне и оперативно прикрыли возможность делать дампы LSASS с помощью ProcDump, включив его в разряд PDM:HackTool.Win32.CreDump.rbaa .
«Касперский» не доволен активностью ProcDump
Анализ аварийного дампа памяти в WinDbg
Перед анализом memory dump необходимо выполнить некоторые настройки. Для работ с софтом понадобится пакет символов отладки Debugging Symbols, загруженный с учётом версии и разрядности системы.
Можно настроить извлечение утилитой символов из интернета, что безопасно, поскольку используется официальный ресурс компании Майкрософт.
Анализ более сложных Thread Dump-ов
Дампы настоящих приложений могут быть очень большими и сложными.
В одной JVM одновременно могут быть запущены сотни тысяч потоков. И заблокированными могут оказаться больше чем два потока (или может быть несколько проблем многопоточности, вызванных одной проблемой).
Анализ такого громадного объема информации может стать настоящей проблемой.
Для анализа больших дампов предназначены специальные утилиты-анализаторы — Thread Dump Analyzers (TDAs). Эти утилиты парсят Java thread dump-ы и выводят информацию в человеко-читаемом виде, часто с применением графических средств. Более того, некоторые из них могут выполнить статический анализ и найти причину проблемы. Конечно, выбор конкретной утилиты зависит от целого ряда обстоятельств.
Тем не менее приведем список наиболее популярных TDA:
Заключение
Thread dump-ы — это отличное средство анализа состояния Java-приложения, особенно в случаях неожиданного поведения многопоточных приложений. Однако без надлежащего багажа знаний, дампы могут внести дополнительную сложность и в без того сложную ситуацию.
В этой статье мы разработали приложение с deadlock, сформировали дамп зависшего приложения. Проанализировали дамп и нашли причину блокировки и исправили её. Это не всегда так просто, для анализа большинства реальных приложений очень полезно использовать специальные утилиты — анализаторы.
Тем не менее, каждый профессиональный Java-разработчик должен понимать основы анализа thread dump-ов. Должен ориентироваться в их структуре, знать какую информацию можно извлечь и как ее использовать для решения проблем многопоточности.
Хотя, thread dump — это не «серебряная пуля» в мире многопоточности, тем не менее это важное средство диагностирования сложных, но довольно распространенных проблем многопоточных Java-приложений.
В программе курса Разработчик Java вопросы многопоточности занимают заметную часть. Мы детально рассматриваем как разрабатывать программы так, чтобы не приходилось по ночам разбираться с deadlock-в продакшене.
Как всегда интересны ваши мнения и комментарии, которые можно оставить тут или заглянуть к Виталию на день открытых дверей.
С критическими ошибками «оконной» ОС знаком практически каждый её пользователь, и появляющиеся при этом синие экраны смерти (BSoD) обычно ничего хорошего не предвещают. Они могут быть спровоцированы программными или аппаратными причинами, и поскольку источник неприятности не всегда очевиден, решение начинается с диагностических мероприятий.
Как включить создание дампа памяти в Windows
Чтобы активировать автоматическое сохранение memory dump в Виндовс, нужно сделать следующее:
- Переходим к свойствам системы любым удобным способом. Например, жмём правой кнопкой мыши по значку «Мой компьютер» (или «Этот компьютер» на «Десятке»). Выбираем «Свойства», затем в перечне опций в левой колонке жмём «Дополнительные параметры системы». Альтернативный вариант – использование Панели управления, где следует перейти в раздел «Система» (то же окно появится при использовании клавиш Win+Pause), а затем в «Дополнительные параметры системы». В Виндовс 10 также можно применить оснастку «Параметры»(Win+I). В окне нужно перейти к разделу «Система – «О системе» – «Сведения о системе» и далее в дополнительные параметры ОС.
- В открывшемся окне на вкладке «Дополнительно» в области «Загрузка и восстановление» жмём «Параметры».
- В итоге манипуляций откроется следующее окно, где следует выбрать тип записи отладочной информации, задать параметры, проставив в нужных пунктах галочки, после чего нажать кнопку «ОК».
Пути поиска для EXE-файлов
Visual Studio автоматически ищет EXE-файлы, не включенные в файл дампа, в следующих расположениях.
- В папке, содержащей файл дампа.
- В пути к модулю, указанному файлом дампа (это путь к модулю на компьютере, на котором был собран дамп).
- В путях к символам, указанных в разделе Инструменты (или Отладка) >Параметры >Отладка >Символы. Можно также открыть страницу Символы из панели Действия окна Сводка файла дампа. На этой странице можно добавить другие расположения для поиска.
MirrorDump
Первым обнаруженным мною проектом, который на удивление мог обходить защиту KES, был MirrorDump от исследователя @_EthicalChaos_.
Его ключевые особенности:
Применяет магию Boo.Lang и плагина DllExport для генерации «на лету» псевдопровайдера аутентификации LSA SSP и его загрузки в память LSASS для получения дескриптора процесса lsass.exe вместо использования API NtOpenProcess.
В минусы этого способа безусловно входит то, что библиотека DLL псевдопровайдера аутентификации LSA должна быть сохранена на диск скомпрометированного хоста для возможности ее использования в API SpLsaModeInitialize, и которая, ко всему прочему, не может быть удалена после создания дампа без перезагрузки ПК.
Данный проект существует как Proof-of-Concept, который «из коробки» в конечном итоге все равно сохраняет дамп памяти на диск даже с учетом того, что генерация такого дампа проходит столь необычным образом. Поэтому я решил сделать свой форк, добавив две новые фичи:
Парсинг слепка прямо в памяти с помощью библиотеки MiniDump (работает не на всех версиях ОС Windows).
Возможность сжатия и отправки байт слепка памяти по TCP-каналу на машину атакующего, где парсинг может быть произведен силами сторонних инструментов (Mimikatz / Pypykatz).
Для первой фичи был добавлен флаг --parse , при наличии которого байты слепка передаются на EntryPoint MiniDump.
Бесфайловый дамп LSASS с парсингом слепка в памяти
Для второй фичи был написан вспомогательный скрипт на Python, содержащий тривиальный сокет-сервер, ожидающий «зиппованный» дамп. Скрипт также автоматически распакует прилетевший дамп, по желанию проверит контрольную сумму и распрасит его с помощью Pypykatz.
Бесфайловый дамп LSASS с отправкой слепка по TCP
Также метод создания слепков lsass.exe с помощью MirrorDump был добавлен мной для использования вместе с lsassy.
К сожалению, недолго музыка играла и примерно полгода спустя «Касперский» начал блокировать создание дампов LSASS через данную технику на уровне поведенческого анализа, что заставило нас искать другой «непалящийся» способ извлечения кред на внутряках.
Поиск файлов EXE, PDB и исходных файлов
Для использования всех возможностей отладки в файле дампа Visual Studio требуются следующие файлы.
- EXE-файл, для которого был создан дамп, и другие двоичные файлы (DLL и т. п.), использовавшиеся процессом дампа.
- Файлы символов ( .pdb) для EXE-файлов и других двоичных файлов.
- EXE- и PDB-файлы, в точности соответствующие версии и сборке файлов, использовавшихся при создании дампа.
- Исходные файлы для соответствующих модулей. Если не удается найти исходные файлы, можно использовать дизассемблирование модулей.
Если в дампе содержатся данные кучи, Visual Studio может обойтись без двоичных файлов для некоторых модулей, но необходимы двоичные файлы для достаточного количества модулей, чтобы создавать допустимые стеки вызовов.
Использование страниц No Binary, No Symbols или No Source Found
Если Visual Studio не может найти файлы, необходимые для отладки модуля в дампе, отображается соответствующая страница No Binary Found (Двоичные файлы не найдены), No Symbols Found (Символы не найдены) или No Source Found (Исходные файлы не найдены). На этих страницах содержатся подробные сведения о причине проблемы, а также ссылки на действия, которые могут помочь найти файлы. См. статью Указание файлов символов (.pdb) и файлов с исходным кодом в отладчике Visual Studio.
Меня зовут @snovvcrash, и я работаю в отделе анализа защищенности компании Angara Security. Отвечаю я, значится, за инфраструктурный пентест, и в этой статье я хотел бы поговорить об одном из самых эффективных методов добычи учетных данных на «внутряке» — извлечении секретов из памяти процесса lsass.exe (MITRE ATT&CK T1003.001) — и, в частности, об особенностях реализации этого метода в ру-сегменте тестирования на проникновение.
За два года работы пентестером мои нервы были изрядно потрепаны нашим любимым отечественным антивирусным решением Kaspersky Endpoint Security (далее — KES), который установлен у каждого первого второго нашего клиента, и который, в отличие от других средств антивирусной защиты, наглухо блокирует все попытки потенциального злоумышленника получить доступ к lsass.exe (не реклама!).
Далее я расскажу свой опыт использования и кастомизации публично доступных инструментов, которые в разные промежутки времени позволяли мне сдампить память LSASS при активном «Касперском». Погнали!
Сбор дампов при сбое
Вы можете использовать переменные среды, чтобы настроить приложение на сбор дампа при сбое. Это полезно, если вы хотите получить представление о причинах сбоя. Например, запись дампа при возникновении исключения помогает выявить проблемы путем проверки состояния приложения при его сбое.
В следующей таблице приведены переменные среды, которые можно настроить для сбора дампов при сбое.
Переменная среды | Описание | Значение по умолчанию |
---|---|---|
COMPlus_DbgEnableMiniDump или DOTNET_DbgEnableMiniDump | Если задано значение 1, включите создание дампа ядра. | 0 |
COMPlus_DbgMiniDumpType или DOTNET_DbgMiniDumpType | Тип собираемого дампа. Дополнительные сведения см. в таблице ниже. | 2 ( MiniDumpWithPrivateReadWriteMemory ) |
COMPlus_DbgMiniDumpName или DOTNET_DbgMiniDumpName | Файл, в который записывается дамп. | /tmp/coredump. |
COMPlus_CreateDumpDiagnostics или DOTNET_CreateDumpDiagnostics | Если задано значение 1, включите ведение журнала диагностики для процесса дампа. | 0 |
В следующей таблице приведены все значения, которые можно использовать для DOTNET_DbgMiniDumpType . Например, если установить для параметра DOTNET_DbgMiniDumpType значение 1, при сбое будет собираться дамп типа MiniDumpNormal .
Значение | Имя | Описание |
---|---|---|
1 | MiniDumpNormal | Включение только сведений, необходимых для записи трассировок стека для всех существующих потоков в процессе. Ограниченная память кучи сборки мусора и информация. |
2 | MiniDumpWithPrivateReadWriteMemory | Включение кучи сборки мусора и сведений, необходимых для записи трассировок стека для всех существующих потоков в процессе. |
3 | MiniDumpFilterTriage | То же, что и MiniDumpNormal , но удаляет личные сведения пользователей, например пути и пароли. |
4 | MiniDumpWithFullMemory | Включение в процесс всей доступной памяти. Необработанные данные памяти включаются в конец, чтобы начальные структуры можно было сопоставить напрямую без необработанных данных памяти. Этот вариант может привести к созданию очень большого файла. |
Противодействие
Вместо заключения приведу несколько рекомендаций, которые помогут свести к минимуму возможности для потенциального злоумышленника сдампить LSASS или извлечь из сделанного слепка значительную выгоду:
Свести к минимуму доступ к любым сетевым узлам в домене с учетными данными пользователей, входящих в привилегированные доменные группы (Domain Admins, Enterprise Admins, Administrators и др.), а для администрирования серверов и рабочих станций использовать выделенные для данных целей УЗ с минимально необходимым набором привилегий (смотрим концепцию Tiered Access Model).
Настроить механизм безопасности Remote Credential Guard для предотвращения сохранения аутентификационных данных пользователей при подключении к удаленным сетевым узлам по протоколу RDP для привилегированных УЗ.
Использовать механизм Protected Process (PPL) для предотвращения потенциальной возможности доступа к памяти процесса lsass.exe.
Использовать группу безопасности Windows «Защищенные пользователи» (Protected Users Security Group) и добавить в нее УЗ критически важных пользователей, например, администраторов домена (эта фича требует тестирования перед внедрением в прод, поэтому аккуратнее).
Следовать рекомендациям производителя ОС для снижения риска проведения атак типа Pass-the-Hash.
Ну а пока извечная игра в кошки-мышки между пентестерами и вендорами антивирусного ПО продолжается, Happy hacking!
В программе курса Разработчик Java довольно много тем, посвященных внутренностям работы JVM. Мы разбираемся в механизмах работы коллекций, байт-кода, сборщика мусора и т.д. Сегодня предлагаем Вашему внимаю перевод довольно интересной статьи о thread dump-е. Что это такое, как его получить и как использовать.
Хотите узнать, как анализировать thread dump (дамп потоков)? Заходите под кат, чтобы узнать больше о том как в Java получить thread dump и что с ним потом делать.
Большинство современных Java-приложений являются многопоточными. Многопоточность может существенно расширить функционал приложения, в то же время она вносит существенную сложность.
В однопоточном приложении все ресурсы (разделяемая память, операции ввода/вывода и т.д.) могут использоваться без синхронизации, т.к. в любой момент времени только один поток пользуется ресурсом.
В случае многопоточных приложений необходимо найти компромисс между усложнением программы и возможным повышением производительности, когда несколько потоков могут использовать все доступные (часто больше одного) ядра центрального процессора (CPU). Если сделать все правильно, то используя многопоточность (формализована в Amdahl's Law), можно добиться существенного прироста производительности приложения. Однако при этом надо помнить об обеспечении синхронного доступа нескольких потоков к разделяемому ресурсу. В большинстве случаев, фреймворки, такие как Spring, инкапсулируют работу с потоками и скрывают от пользователей многие технические детали. Однако и в случае применения современных сложных фреймворков что-то может пойти не так, и мы, как пользователи, столкнемся со сложно решаемыми багами многопоточности.
К счастью, Java оснащена специальным механизмом для получения информации о текущем состоянии всех потоков в любой момент времени — это thread dump (своего рода моментальный снимок). В этой статье мы изучим, как получить thread dump для приложения реалистичных размеров и как этот dump проанализировать.
Предполагается, что читатель владеет базовой информацией о многопоточном программировании и знает о проблемах синхронизации потоков и использовании совместных ресурсов. Тем не менее будет не лишним освежить в памяти некоторые основные термины и понятия.
Основная терминология
С первого взгляда Java thread dump-ы могут показаться «китайской грамотой», ключом к ее понимаю являются следующие понятия. В общем, давайте, повторим основные термины многопоточности, которые будем использовать для анализа дампов.
-
Thread или поток — дискретная единица многопоточности, управляемая Java Virtual Machine (JVM). Потоки JVM соответствуют потокам в операционной системе (OS) — native threads («естественные потоки»), которые и реализуют механизм выполнения кода.
У каждого потока есть уникальный идентификатор и имя. Потоки могут быть «демонами» и «не демонами».
Программа завершает свою работу, когда завершаются все потоки «не демоны» или вызывается метод Runtime.exit. Работающие «демоны» не влияют на завершение работы программы. Т.е. JVM ждем когда доработают все «не демоны» и завершает работу, на «не демонов» не обращает внимание.
За более подробной информацией обращайтесь к документации класса Thread.
Поток может находится в одном из следующих состояний:
- Alive thread или «живой» — поток, который выполняет некоторую работу (нормальное состояние).
- Blocked thread или «заблокированный» — поток, который попытался зайти в секцию синхронизации (synchronized), однако другой поток уже успел зайти в этот блок первым, и все следующие потоки, которые попытаются зайти в этот же блок оказываются заблокированными.
- Waiting thread или «ожидающий» — поток, который вызвал метод wait (возможно, с указанием таймаута) и сейчас ждет, когда другой метод выполнит notify или nonifyAll на этом же объекте.
Каждый объект в Java имеет монитор, при помощи которого поток может синхронизоваться, т.е. выставить блокировку, которая гарантирует, что ни один другой поток не получит доступ к этому объекту, пока блокировка не будет снята, т.е. поток — владелец блокировки не выйдет из блока synchronized.
Более детальную информацию можно найти в этих источниках:
Применяя эти простые понятия о потока в Java, мы можем создать тестовое приложение. Для этого приложения мы соберем thread dump. Полученный дамп проанализируем и извлечем полезную информацию о текущих потоках приложения.
Создание примера программы
Прежде, чем создать thread dump, нам надо разработать Java-приложение. Традиционный «hello, world!» для нашей цели слишком прост, а дамп среднего размера приложения может оказаться слишком сложным для демонстрации. Исходя из этого, мы создадим достаточно простое приложение, в котором создаются два потока. Причем потоки попадают в deadlock:
Эта программа создает два ресурса: resourceA и resourceB, и стартует два потока: threadLockingResourceAFirst и threadLockingResourceBFirst, которые блокируют ресурсы друг друга.
Причиной возникновения deadlock-а является «перекрестная» блокировка ресурсов потоками.
Причиной возникновения deadlock является попытка «взаимного» захвата ресурсов, т.е. поток threadLockingResourceAFirst захватывает ресурс resourceA, поток threadLockingResourceBFirst захватывает ресурс resourceB. После этого поток threadLockingResourceAFirst, не отпуская свой ресурс, пытается захватить resourceB, а поток threadLockingResourceBFirst, не отпуская свой ресурс, пытается захватить ресурс resourceA. В результате потоки блокируются. Задержка в 1с добавлена, чтобы гарантировать возникновение блокировки. Потоки ждут освобождение нужных ресурсов, но это никогда не случится.
Вывод программы будет таким (числа после java.lang.Object@ будут разные для каждого запуска):
Генерация Thread Dump
На практике, Java-программа может аварийно завершиться и при этом создать thread dump. Однако в ряде случаев (например в случае deadlock-ов), программа не завершается и thread dump не создает, она просто зависает. Для создания дампа таких зависших программ, прежде всего надо выяснить идентификатор процесса программы, т.е. Process ID (PID). Для этого можно воспользоваться утилитой JVM Process Status (JPS), которая начиная с версии 7, входит в состав Java Development Kit (JDK). Чтобы найти PID процесса нашей зависшей программы, мы просто выполним jps в терминале (Windows или Linux):
Первая колонка — это идентификатор локальной виртуальной машины (Local VM ID, т.е. lvmid) для выполняемого Java-процесса. В контексте локальной JVM, lvmid указывает на PID Java-процесса.
Надо отметить, что это значение, скорее всего, будет отличаться от значения выше. Вторая колонка — это имя приложения, которое может указывать на имя main-класса, jar-файла или быть равно «Unknown». Все зависит от того, как приложение было запущено.
В нашем случае имя приложения DeadlockProgram — это имя main-классы, который был запущен при старте программы. В примере выше PID программы 11568, этой информации достаточно для генерации thread dump'а. Для генерации дампа мы воспользуемся утилитой jstack, которая входит в состав JDK, начиная с версии 7. Чтобы получить дамп мы передадим в jstack в качестве параметра PID нашей программы и укажем флаг -l (создание длинного листинга). Вывод утилиты перенаправим в текстовый файл, т.е. thread_dump.txt:
Полученный файл thread_dump.txt содержит thread dump нашей зависшей программы и содержит важную информацию для диагностики причин возникновения deadlock-а.
Если используется JDK до 7 версии, то для генерации дампа можно воспользоваться утилитой Linux — kill с флагом -3. Вызов kill -3 отправит программе сигнал SIGQUIT.
В нашем случае вызов будет такой:
Анализ простого Thread Dump
Открыв файл thread_dump.txt, мы увидим примерно следующее содержание:
Дампим LSASS по OPSEC-овски
Итак, перейдем к самому интересному: как же можно «угодить» антивирусным средствам защиты и сделать дамп памяти процесса lsass.exe в стиле Operational Security?
Запреты AV на создание слепков памяти LSASS условно можно разделить на 3 части:
Запрет на получение дескриптора процесса lsass.exe.
Запрет на чтение виртуальной памяти процесса lsass.exe.
Запрет на сохранение результирующего дампа на диск.
Ниже мы рассмотрим 3 проекта, каждый из которых в свое время помогал мне извлечь чувствительную информацию из памяти сетевых узлов при активном средстве KES на внутренних пентестах или операциях Red Team.
Анализ memory dump в WinDbg
Чтобы перейти к процедуре, открываем объект в утилите (File – Open Crash Dump) или, если предварительно настраивались ассоциации файлов, открываем элемент щелчком мыши. Утилита начнёт анализировать файл, затем выдаст результат.
В окне предполагается ввод команд. Запрос «!analyze –v» позволит получить более детальные сведения о сбое (STOP-код, имя ошибки, стек вызовов команд, приведших к проблеме и другие данные), а также рекомендации по исправлению. Для остановки отладчика в меню программы жмём «Debug» – «Stop Debugging».
Out-Minidump.ps1
Еще один древний как мир способ — позаимствовать импорт P/Invoke функции MiniDumpWriteDump из класса NativeMethods сборки System.Management.Automation.WindowsErrorReporting , как это делается в скрипте Out-Minidump.ps1 из арсенала PowerSploit.
Анализ сборки System.Management.Automation.WindowsErrorReporting с помощью dnSpy
Результат работы скрипта аналогичен вызову функции MiniDump из предыдущего метода, поэтому оставлю это в качестве упражнения для читателя. Ну и, соответственно, антивирусы так же негативно к нему относятся.
Сбор дампов
Дампы можно собирать различными способами в зависимости от платформы, на которой выполняется приложение.
Для сбора дампа внутри контейнера требуется возможность PTRACE, которую можно добавить с помощью --cap-add=SYS_PTRACE или --privileged .
Дампы могут содержать конфиденциальные сведения, поскольку они могут содержать всю память выполняющегося процесса. При их обработке следует учитывать ограничения безопасности и рекомендации.
Создание файла дампа
При отладке процесса в Visual Studio можно сохранить дамп, когда отладчик останавливает выполнение при возникновении исключения или в точке останова.
Если включена JIT-отладка, можно подключить отладчик Visual Studio к аварийному процессу, который выполняется вне Visual Studio, а затем сохранить файл дампа из отладчика. См. раздел Подключение к выполняющимся процессам.
Сохранение файла дампа
Когда во время отладки происходит остановка (при возникновении ошибки или в точке останова), выберите Отладка > Сохранить дамп как.
В диалоговом окне Сохранить дамп как в разделе Тип файла можно выбрать Минидамп или Минидамп с кучей (значение по умолчанию).
Укажите путь сохранения и выберите имя файла дампа, а затем нажмите Сохранить.
Вы можете создавать файлы дампа с помощью любой программы, которая поддерживает формат минидампов Windows. Такой программой, например, может быть программа командной строки Procdump из Windows Sysinternals, которая может создавать файлы аварийного дампа процесса на основе триггеров или по требованию. Дополнительные сведения об использовании других средств для создания файлов дампа см. в разделе Требования и ограничения.
Сбор дампов в определенный момент времени
Вы можете собрать дамп, когда приложение еще не завершило работу. Например, если вы хотите проверить состояние приложения, в котором, на ваш взгляд, возникла взаимоблокировка, настройка переменных среды для сбора дампов при сбое не будет полезна, так как приложение все еще работает.
Для сбора дампа в собственном запросе можно использовать dotnet-dump , который является средством CLI для сбора и анализа дампов. Дополнительные сведения об использовании этого средства для сбора дампов с помощью dotnet-dump см. в разделе Служебная программа для сбора и анализа дампа.
Файлы дампа, с кучами или без куч
В файлах дампа могут содержаться сведения о куче, но могут и отсутствовать.
Файл дампа со сведениями о куче содержит снимок памяти приложения, включая значения переменных на момент создания дампа. Visual Studio также сохраняет в файле дампа с кучей двоичные файлы загруженных модулей машинного кода, что может значительно упростить отладку. Visual Studio может загружать символы из файла дампа с кучей, даже если не удается найти двоичный файл приложения.
Файлы дампа без сведений о куче намного меньше, чем дампы с кучами, но отладчику нужно будет загрузить двоичные файлы приложения, чтобы найти сведения о символах. Загруженные двоичные файлы должны точно соответствовать тем, которые выполнялись во время создания дампа. В файлах дампа без сведений о куче хранятся только значения переменных стека.
Открытие файла дампа
В Visual Studio последовательно выберите Файл > Открыть > Файл.
В окне Сводка файла минидампа отображается сводка и сведения о модулях для файла дампа, а также действия, которые можно выполнить.
В разделе Действия:
- чтобы задать расположения для загрузки символов, выберите Set symbol paths (Задать пути к символам);
- чтобы начать отладку, выберите Debug with Managed Only (Отладка только с управляемым кодом), Debug with Native Only (Отладка только с машинным кодом), Debug with Mixed (Отладка со смешанным кодом) или Debug with Managed Memory (Отладка с управляемой памятью).
NanoDump
Нашим следующим «спасителем» стал инструмент NanoDump от компании-разработчика Cobalt Strike, который я без преувеличений считаю просто произведением искусства.
Его ключевые особенности:
Использование системных вызовов (с их динамическим резолвом) с помощью SysWhispers2, что позволяет обходить userland-хуки Win32 API, которые вешает антивирусное ПО.
Собственная реализация MiniDumpWriteDump через чтение памяти lsass.exe с помощью ZwReadVirtualMemory, что избавляет оператора от необходимости дергать потенциально подозрительную ручку API.
Поддержка разных трюков и техник создания дампа (перечислены не все):
поиск уже открытых дескрипторов lsass.exe в других процессах [ссылка],
использование утекающего хэндла lsass.exe при вызове функции CreateProcessWithLogonW [ссылка],
загрузка NanoDump в виртуальную память lsass.exe в виде провайдера SSP [ссылка],
возможность снятия защиты PPL [ссылка].
Намеренное повреждение сигнатуры дампа памяти с целью избегания детекта от AV на этапе его записи на диск.
Компиляция в Beacon Object File (BOF) для выполнения NanoDump из памяти в случае, когда моделируемый злоумышленник обладает сессией «Кобальта» на скомпрометированном сетевом узле.
Для нас, как для пентестеров компаний преимущественно из ру-сегмента, наибольший интерес представляет техника загрузки NanoDump, скомпилированного в виде DLL, прямо в LSASS как SSP, то есть в виде псевдопровайдера аутентификации LSA. Исходя из нашего опыта, на данный момент это и есть слабое место «Касперского».
Для того, чтобы воспользоваться этой техникой без сессии Cobalt Strike, моделируемый злоумышленник должен принести на скомпрометированный узел 2 бинаря: загрузчик библиотеки SSP и, собственно, саму библиотеку SSP. Полагаю, что в скором времени оба они начнут детектиться по крайней мере на уровне сигнатурного анализа, поэтому воспользовавшись примером из этого ресерча от @ShitSecure мы напилили свой загрузчик NanoDump SSP из памяти с помощью кредла на PowerShell.
Дампим LSASS с помощью NanoDump SSP и восстанавливаем поврежденную сигнатуру
Намеренно не раскрываю исходник кредла (тем более, что в приведенной выше статье все есть), ибо надеюсь, что этот метод проживет хотя бы еще немного. Ну а в общем, смиренно ждем, когда и эта техника начнет «палиться» KES, чтобы начать искать новые ухищрения для дампа памяти LSASS.
Introductory Information
Хотя на первый взгляд этот файл может показаться слишком сложным и запутанным, в действительности он весьма прост, если разбирать его по частям шаг за шагом.
В первой строке указывается время, когда дамп был сформирован, во второй — диагностическая информация о JVM, на которой дамп был получен:
В этой секции нет информации о потоках. Тут задается общий контекст системы, в которой был собран дамп.
Общие сведенья о потоках
В следующей секции представлена информация о потоках, которые выполнялись в системе в момент сбора дампа:
В следующей секции приводится список:
В нем содержится информация о потоках за пределами JVM, т.е. это не потоки виртуальной машины и не потоки сборщика мусора. Если посмотреть на адреса этих потоков, то можно заметить, что они соответствуют значению tid — «естественному, железному» (native) адресу в операционной системе, а не Thread ID.
Троеточия используются для сокрытия излишней информации:
Читайте также: