Оглавление


В конце 2023 года команда Solar 4RAYS проводила расследование атаки на российскую телеком-компанию. Её сеть была скомпрометирована азиатской APT-группировкой, которую мы назвали Obstinate Mogwai (или «Упрямый Демон» в переводе с английского). «Упрямый», потому что в ходе работы над инцидентом мы видели, как хакеры вновь и вновь возвращались в атакованную организацию, пока мы окончательно не закрыли им все возможности для проникновения. Делали они это с помощью давно известной уязвимости десериализации ненадежных данных в параметре VIEWSTATE среды ASP.NET (далее будем называть это десериализацией VIEWSTATE).

Скоро мы напишем подробный отчет про Obstinate Mogwai в целом (и обязательно опубликуем его в нашем блоге). В этой же статье сфокусируемся только на десериализации VIEWSTATE. Вспомним историю исследования уязвимости, опишем, как обнаружить подобные атаки и как на них реагировать. В конце также приведем индикаторы компрометации. Надеемся, эта информация будет полезна как командам DFIR и Threat Hunting, так и сотрудникам ИБ-отделов, и системным администраторам.

Кстати, если вы увидели подозрительную активность в своей сети и считаете, что тоже стали жертвой хакерской группировки, пишите нам. Эксперты Solar 4RAYS проведут расследование, выявят проблемы и дадут необходимые рекомендации, чтобы защитить вашу инфраструктуру от хакеров.

Что такое VIEWSTATE

VIEWSTATE – это параметр в ASP.NET Framework, в котором сохраняется состояние ASPX-страницы при выполнении HTTP-запросов (например, значения элементов управления в форме и другая информация о странице). HTTP является протоколом без сохранения состояния (stateless), поэтому в ASP.NET эту задачу решают с помощью VIEWSTATE. В HTTP-запросах данные VIEWSTATE сериализуются и десериализуются на стороне получателя.

В ASP.NET для этого используется класс ObjectStateFormatter, который сам Microsoft назвал ненадежным и не рекомендовал в качестве провайдера сериализации объектов. Он же является одной из причин существования RCE-уязвимости.

Исследования уязвимости десериализации и атак, с ней связанных, – это отдельная большая тема, о которой выпущено большое количество публикаций и статей. Для оценки масштаба вспомним самые важные события, связанные с десериализацией в ASP.NET.

deser_timeline.png
История исследования десериализации в индустрии

Сентябрь 2014 – Microsoft выпускает патч KB 290524, который по умолчанию включает MAC-валидацию для всех версий ASP.NET (с 1.1 до 4.5.2).

Ноябрь 2014 – эксперт Александр Херцог (Alexandre Herzog) на конференции Application Security Forum – Western Switzerland (@appsec_forum) демонстрирует уязвимость десериализации VIEWSTATE в SharePoint. Представленный способ опирался на отсутствие MAC-валидации в ASP.NET.

Июль 2017 – эксперты Альваро Муноз (Alvaro Muñoz) и Олександр Мирош (Oleksandr Mirosh) выступают с докладом “Friday the 13th JSON Attacks” на Black Hat 2017 в США, где показывают гаджеты для эксплуатации десериализации VIEWSTATE.

Апрель 2019 – известный эксперт в области десериализации Соруш Далили выпускает своё исследование проблемы. Приводим его краткую выжимку: выпущенный Microsoft в 2014 году патч затрудняет использование уязвимости, но не закрывает её полностью: проверку MAC-валидации можно успешно проходить, если у злоумышленника есть доступ к веб-серверу, а именно – к ключам валидации VIEWSTATE (подробнее о них - далее в статье).

Ключи валидации VIEWSTATE в файле web.config веб-приложения
Ключи валидации VIEWSTATE в файле web.config веб-приложения

Исследование номинировали на Pwnie Awards как “Most under-hyped research” (самое недостаточно раскрученное исследование). Среди прочего в тексте исследования упоминается, что уязвимость относится к WON’T FIX (не будет исправляться). Одно предложение из описания заявки на номинацию красноречиво описывает суть уязвимости:

Описание статьи Соруш Далили в номинации “Most under-hyped research” на церемонии Pwnie awards 2019
Описание статьи Соруш Далили в номинации “Most under-hyped research” на церемонии Pwnie awards 2019

Другими словами, если злоумышленники каким-либо образом получают доступ к ключам валидации VIEWSTATE веб-приложения на сервере, то они могут, используя уязвимость десериализации, удаленно выполнять код на этом сервере.

Февраль 2020 – Microsoft выпускает патч для CVE-2020-0688 – уязвимости, смысл которой в том, что у всех версий Exchange после установки были одинаковые ключи валидации для backend ECP. Вскоре стали доступны публичные POC. В данном случае для успешной эксплуатации требовались валидные учетные данные, которые позволяли получить доступ до backend панели ECP. Эту уязвимость можно рассматривать как частный случай уязвимости десериализации VIEWSTATE, так как CVE-2020-0688 работала только на один путь – backend ECP. Тем не менее, эта важная уязвимость, которая могла напомнить злоумышленникам о десериализации и её возможностях, так как именно после этой CVE стали появляться публичные отчеты об эксплуатации уязвимости десериализации VIEWSTATE в общем формате (на произвольных путях) в дикой среде.

Атаки с помощью десериализации в дикой природе

Июнь 2020 – Австралийский центр кибербезопасности (ACSC) Управления радиотехнической обороны выпустил рекомендации 2020-008 об атаке Copy-paste compromises, в которых были описаны тактики и техники неизвестной прогосударственной группировки, которая эксплуатировала уязвимости десериализации:

  • Telerik UI CVE-2019-18935 – уязвимость десериализации в .NET JavaScriptSerializer. Использовали для запуска реверс шелла;
  • CVE-2020-0688 – злоумышленники использовали уязвимость для размещения различных веб-шеллов (кастомных и open source), в том числе фреймворка js_eval собственной разработки, который работает только в памяти и является многомодульным;
  • вредоносные dotm-шаблоны Word, которые также загружали Powershell Empire с помощью десериализации нагрузок “вручную” – через вызов десериализации уязвимым провайдером – BinaryFormatter.

В мае 2020 года были выпущены рекомендации:

  • 2020-004 по обнаружению эксплуатации уязвимости в CVE-2019-18935 в ПО Telerik, а также по реагированию на такую атаку.
  • 2020-006 по обнаружению эксплуатации десериализации VIEWSTATE и уязвимости в ПО Telerik, а также по реагированию на подобные атаки. В этих рекомендациях впервые упоминалось, что для обнаружения атак десериализации можно использовать событие Application Event ID 1316 (подробнее о нём – далее в тексте).

Июль 2021 – эксперты SYGNIA выпустили отчет по атаке группировки Praying Mantis, которая использовала 4 разные уязвимости десериализации:

  • Checkbox Survey RCE Exploit (CVE-2021-27852) – десериализация кастомной реализации VIEWSTATE для reflective загрузки вредоноса NodeIISWeb – очень похож на фреймворк js_eval из рекомендаций Австралийского центра кибербезопасности;
  • Десериализация VIEWSTATE с помощью украденных ключей IIS – злоумышленники несколько раз возвращались в инфраструктуру этим методом, им же они пользовались для горизонтального перемещения;
  • Десериализация объекта из ASP.NET MSSQL базы – злоумышленники записывали в MSSQL базу вредоносный объект в сериализованном виде, который далее запрашивал и десериализовывал IIS-сервер когда получал запрос со специально сформированным cookies. В этом случае также загружали вредонос NodeIISWeb;
  • Telerik UI CVE-2019-18935 – уязвимость десериализации в .NET JavaScriptSerializer. Использовали для загрузки веб-шеллов.

Высока вероятность, что эксперты SYGNIA и ACSC столкнулись с одной и той же группировкой, так как было много пересечений по тактикам, техникам и процедурам.

Март 2022 – эксперты Mandiant поделились большим набором кейсов атак группировки APT41, в большинстве из которых эксплуатировались уязвимости десериализации VIEWSTATE:

  • Июнь 2020 – APT41, через уязвимость directory traversal, получили доступ к web.config файлу с ключами проприетарного веб-приложения на ASP.NET. Ключи использовали для десериализации вредоносных VIEWSTATE-нагрузок и загрузки сборок в память. Сборки записывали веб-шеллы по заранее закодированным путям на файловой системе. Также использовался гаджет DisableActivitySurrogateSelectorTypeCheck утилиты ysoserial.net.
  • Май 2021 – как и в июне 2020 злоумышленники эксплуатировали десериализацию VIEWSTATE в проприетарном веб-приложении. Откуда были получены ключи неизвестно (вероятно, также через directory traversal уязвимость).
  • Июнь-декабрь 2021 – использование уязвимости нулевого дня в ASP.NET веб-приложении USAHERDS (диагностическая система оповещения о чрезвычайных ситуациях в области охраны здоровья животных) для атаки на правительства нескольких штатов в США. Уязвимость аналогична CVE-2020-0688 (использование статических ключей). Предполагается, что первоначальный доступ к ключам был получен также через directory traversal уязвимость.

Март 2023 – Агентство по кибербезопасности и защите инфраструктуры (CISA), ФБР и MS-ISAC выпустили совместные рекомендации по противодействию атакам с помощью уязвимостей десериализации. Рекомендации создали на основе анализа атак нескольких группировок, произошедших в ноябре 2022 на множество правительственных IIS-серверов. В атаках использовалась уязвимость десериализации в ПО Progress Telerik.

Ноябрь 2023 – эксперты Solar 4RAYS реагируют на инцидент в инфраструктуре телеком-компании, где группировка Obstinate Mogwai эксплуатирует уязвимости десериализации VIEWSTATE для повторного возвращения.

Эксплуатация десериализации VIEWSTATE в исполнении Obstinate Mogwai

В конце 2023 года в процессе реагирования на инцидент мы стали замечать на Exchange-серверах выполнение powershell-команд от процесса w3wp – worker-процесса Exchange. Причем команды выполнялись от самого процесса w3wp без создания дочерних процессов powershell.exe.

Примеры выполняемых команд:

    
net user <REDACTED>
gci c:\programdata\
reg query HKEY_LOCAL_MACHINE\system\currentcontrolset\control\securityproviders\wdigest /v uselogoncredential
Get-ItemProperty -Path HKLM:\system\currentcontrolset\control\securityproviders\wdigest).uselogoncredential
Get-ChildItem -Path "c:\programdata"
Get-ChildItem -Path "c:\users"
Remove-Item -Path "c:\programdata\1.log"
Remove-Item -Path "c:\windows\system\l.txt"
Test-Connection -Count 1 -ComputerName <REDACTED>
Resolve-DnsName -Name <REDACTED>
Remove-Item -Path "C:\Program Files\Microsoft\Exchange Server\V15\FrontEnd\HttpProxy\owa\auth\Current\themes\resources\frown.aspx"
Remove-Item -Path "c:\windows\temp\user.log"            
    

С помощью powershell-команд на Exchange-сервер копировались и другие инструменты атакующих (например, powershell-скрипт Invoke-SMBClient из фреймворка Inveigh), а также выполнялась эксфильтрация данных и содержимого почтовых ящиков.

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

В IIS-логах в окрестности времени выполнения команд были обнаружены следующие POST-запросы с характерным UserAgent:

POST /owa/auth/RedirSuiteServiceProxy.aspx 443 77.223.109[.]164 python-requests/2.30.0
POST /owa/auth/RedirSuiteServiceProxy.aspx 443 45.12.67[.]18 python-requests/2.30.0
POST /owa/auth/RedirSuiteServiceProxy.aspx 443 77.223.109[.]163 python-requests/2.30.0

Причем все они выполнялись к легитимному aspx-файлу (C:\Program Files\Microsoft\Exchange Server\V15\FrontEnd\HttpProxy\owa\auth\RedirSuiteServiceProxy.aspx).

В ходе более тщательного поиска индикаторов по всему образу одного из Exchange-серверов в журнале Windows мы обнаружили событие с ID 1316:

Пример события Application Event 1316 с подозрительным VIEWSTATE
Пример события Application Event 1316 с подозрительным VIEWSTATE

В параметре PersistedState в событии 1316 отображается base64-закодированный сериализованный VIEWSTATE, который при десериализации (во время или после) как раз и вызвал Exception, из-за которого это событие и появилось в журнале событий. После декодирования из basе64 VIEWSTATE выглядел следующим образом:

Декодированный VIEWSTATE с гаджетом ActivitySurrogateDisableTypeCheck
Декодированный VIEWSTATE с гаджетом ActivitySurrogateDisableTypeCheck

Первые два байта представляют собой заголовок сериализованного VIEWSTATE – FF 01, вот его общая структура:

      
struct SERIALIZED_VIEWSTATE {
    byte Marker_Format = 0xFF; // https://referencesource.microsoft.com/#System.Web/UI/ObjectStateFormatter.cs,116 
    byte Marker_Version_1 = 0x01; // https://referencesource.microsoft.com/#System.Web/UI/ObjectStateFormatter.cs,117
    byte token = 0x32; // https://referencesource.microsoft.com/#System.Web/UI/ObjectStateFormatter.cs,90
    variable_size length_7BitEncodedInt; // size may vary: [1-4] bytes, https://referencesource.microsoft.com/#mscorlib/system/io/binaryreader.cs,582
    SERIALIZED_DATA d; // has size of length_7BitEncodedInt
    _HashSize MAC_bytes; //_HashSize depends on validation algorithm (16, 20 or 32 bytes), https://referencesource.microsoft.com/#System.Web/Configuration/MachineKeySection.cs,88
}         
struct SERIALIZED_DATA {
    struct SerializationHeaderRecord { // https://referencesource.microsoft.com/#mscorlib/system/runtime/serialization/formatters/binary/binarycommonclasses.cs,455 
        byte binaryHeaderEnum = 0x0 (SerializedStreamHeader); // https://referencesource.microsoft.com/#mscorlib/system/runtime/serialization/formatters/binary/binaryenums.cs,27
        dword topId = 0x1;
        dword headerId = 0xFFFFFFFF;
        dword majorVersion = 0x1; // https://referencesource.microsoft.com/#mscorlib/system/runtime/serialization/formatters/binary/binarycommonclasses.cs,417
        dword minorVersion = 0x0; //https://referencesource.microsoft.com/#mscorlib/system/runtime/serialization/formatters/binary/binarycommonclasses.cs,418
    }
    data; // serialized data itself
}            
    

Приведенных выше структур в исходном коде нет — это наша интерпретация для лучшего понимания. По ссылкам можно найти исходный код и ознакомиться с подробностями реализации.

В сериализованных данных наше внимание привлек гаджет (сериализованный код для выполнения заданных команд) ActivitySurrogateDisableTypeCheck, а именно строчка “xaml_payload”, по которой мы вышли на утилиту ysoserial.net. Этот инструмент с открытым исходным кодом позволяет генерировать нагрузки для эксплуатации различных уязвимостей десериализации. Его предназначение – отключение проверок, введенных в .NET 4.8+ для другого гаджета ActivitySurrogateSelector. Он, в свою очередь, позволяет загружать произвольные .NET сборки (далее – сборки) в память. Судя по структуре наблюдаемых нами VIEWSTATE-нагрузок, злоумышленники генерировали их с помощью ysoserial.net, и все они были гаджетами ActivitySurrogateDisableTypeCheck.

Больше никаких VIEWSTATE-нагрузок мы не нашли. Следов применения гаджета ActivitySurrogateSelector также не обнаружили. Однако спустя некоторое время мы все-таки поймали настоящий payload – благодаря ошибке самих злоумышленников. А как именно они ошиблись, расскажем далее.

Злодеи тоже ошибаются

У гаджета ActivitySurrogateDisableTypeCheck есть важная особенность. Он отключает проверки .NET 4.8+ на время жизни соответствующего w3wp-процесса. Как известно, w3wp-процессы могут перезапускаться:

  • по расписанию;
  • при зависании (например, если процесс не ответит на heart beat запрос IIS);
  • при ручном перезапуске;
  • по другим причинам.

Но если бы они не перезапускались, то можно было бы использовать этот гаджет один раз и больше не применять до тех пор, пока “жив” атакуемый w3wp-процесс.

Расписание перезапуска отображается в расширенных настройках пулов приложений в IIS Manager в разделе Recycling:

Настройки перезапуска worker-процессов по умолчанию
Настройки перезапуска worker-процессов по умолчанию

У заказчика время перезапуска worker-процесса, который отвечал за приложение OWA, составляло 14 дней, а не установленные по умолчанию 29 часов. В результате мы наблюдали следующий timeline событий:

11:00 Event ID 1316 Viewstate was invalid – отправка гаджета ActivitySurrogateDisableTypeCheck
XX:YY перезапуск w3wp процесса и потеря эффекта гаджета ActivitySurrogateDisableTypeCheck
16:00 Event ID 1316 Viewstate was invalid – неудачная попытка выполнения powershell-команды из-за перезапущенного w3wp процесса и отсутствующего эффекта гаджета ActivitySurrogateDisableTypeCheck.

Именно в этом событии в PersistedState находилась VIEWSTATE-нагрузка, которая позволяла атакующим выполнять powershell-команды. То есть, судя по всему, злоумышленники допустили ошибку и забыли “активировать” гаджет ActivitySurrogateDisableTypeCheck, так как, вероятно, думали, что сохранился эффект от прошлого гаджета, использованного в 11:00.

После декодирования из basе64 VIEWSTATE выглядел следующим образом:

Декодированный VIEWSTATE с гаджетом ActivitySurrogateSelector и нагрузкой
Декодированный VIEWSTATE с гаджетом ActivitySurrogateSelector и нагрузкой

Это был гаджет ActivitySurrogateSelector. Он загружал в память сборку в виде dll-файла с помощью Assembly.Load(byte[]) и запускал её вызовом конструктора сборки. В результате анализа выяснилось, что dll позволяла выполнять powershell-команды (подробности далее).

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

Для замены ключей мы пробовали перезапускать пулы и даже перезагружать почтовые серверы, но это не принесло желаемого результата. Во многих публичных статьях, которые связаны с десериализацией VIEWSTATE, приводят расплывчатые рекомендации на этот случай:

1) Danger of stealing auto generated net machine keys (Soroush Dalili, 2019)

“Recommendation:
Create a new Application Pool for your ASP.NET application when you need to reset all the credentials for any reasons. This will ensure that a new ASP.NET key will be generated for the application.”

То есть для “сброса” ключей рекомендуется создать пул заново. В случае с Exchange-сервером сделать это не так уж просто. Сложнее, наверное, только полная переустановка Exchange. Впрочем, как оказалось, даже переустановка Exchange не поможет изменить auto generated-ключи, так как мастер-ключ не удаляется из реестра, поэтому при повторной установке будут использованы те же самые ключи.

2) Praying Mantis An Advanced Memory Resident Attack (Sygnia, 2021)

“In addition to these security measures, encryption and validation keys should be handled with care as sensitive credentials. If possible, use auto-generated keys, otherwise routinely rotate the machine keys on your IIS servers to make sure you would not be susceptible to attacks where keys were stolen or leaked.”

Рекомендуется по возможности использовать автоматически сгенерированные ключи, но нет никаких рекомендаций, что делать в случае их компрометации.

3) Does This Look Infected? A Summary of APT41 Targeting U.S. State Governments (Mandiant, 2022)

“Generating unique machineKey values is critical to the security of an ASP.NET web application because the values are used to secure the integrity of the ViewState.”

Конкретных рекомендаций нет. В статье обращают внимание на то, чтобы значения ключей были уникальными. В случае автоматически сгенерированных ключей они будут уникальными, но снова нет информации, что делать при компрометации этих уникальных ключей.

Конкретные меры были описаны только в рекомендациях 2020-006 от ACSC в июне 2020, но там не раскрывалось, как менять автоматически сгенерированные ключи, которые по умолчанию используются в Exchange-серверах.

Несколько слов об атрибуции

Отметим несколько фактов касательно атрибуции расследованных нами инцидентов. При реагировании на атаку группировки Obstinate Mogwai мы наблюдали следующую технику загрузки в память .NET бэкдора:

powershell exec bypass -f c:\programdata\microsoft\WDF\ctfmon.ps1

Загружаемый в память бэкдор имел некоторое сходство с C++ версией бэкдора KingOfHearts группировки IAmTheKing, про которую писали в 2020 г.

В результате выполнения Powershell команды расшифровывался файл C:\ProgramData\Microsoft\WDF\WDF.log, и в память powershell.exe загружалась .NET сборка, представляющая собой бэкдор. Файл ctfmon.ps1 при этом весил 13MB, так как был обфусцирован следующим образом:

image008.png

image009.png

Именно такую уникальную обфускацию мы уже наблюдали в инцидентах 2020-2021 годов, про которые рассказывали в 2021 году в докладе “Operation LongChain. История одного расследования”.

По данным Mandiant, APT41 в июне 2020 года использовала при эксплуатации десериализации VIEWSTATE гаджет ActivitySurrogateDisableTypeCheck, который мы также наблюдали при реагировании в нашем инциденте. Именно этот гаджет подвел злоумышленников и помог раскрыть одну из сборок фреймворка.

В исследованном инциденте Obstinate Mogwai для проведения атак десериализации использовали свой собственный фреймворк, который отличался от js_eval, описанного в кейсе ACSC в 2020 году.

Другими словами, надёжная атрибуция атак, которые мы расследовали, к какой-либо из известных APT-группировок – дело непростое, так как некоторые азиатские группы регулярно заимствуют друг у друга вредоносный инструментарий. В будущем мы посвятим этому вопросу отдельную публикацию.

VIEWSTATE exploitation framework

VIEWSTATE exploitation framework – наше имя для набора скриптов/библиотек, которыми оперировали злоумышленники в описываемой атаке. На основании проделанного исследования мы пришли к выводу, что оператор при проведении подобных атак работает на более высоком уровне – у него имеется нечто вроде оболочки, куда он вводит команды и получает результат в удобном формате.

На это указывает одна из особенностей работы фреймворка, а именно использование под каждое конкретное действие отдельной скомпилированной на ходу сборки. Нам удалось достать из памяти атакованного worker-процесса следующие файлы:

Assembly name Compilation timestamp Main class name POST query param name to fetch data from Number of extracted assemblies Action
Microsoft.Exchange.Management.Powershell.Support.dll 2023-01-04 09:11:36 Ps cadata 8 Выполнить powershell команду, зашифрованную в параметре cadata POST-запроса и отправить вывод в ответе
Microsoft.Exchange.MessageSecurity.dll FileDown cadataIV 2 Выполнить эксфильтрацию файла по пути, указанном в параметре cadataIV
Microsoft.Exchange.UM.UMCommon.dll 2023-01-04 09:11:35 FileView cadataSig 1 Отобразить метаданные файла или каталога, указанных в параметре cadataSig

Сборки, извлеченные из памяти атакованного worker-процесса w3wp.exe

Даты компиляции разных сборок практически совпадают. Конечно, в нашем распоряжении довольно небольшая выборка, но, скорее всего, она свидетельствует о том, что все доступные сборки компилировались каким-либо скриптом для дальнейшего использования во фреймворке. В утилите ysoserial.net “из коробки” гаджет ActivitySurrogateSelectorFromFile требует на вход cs-файл и компилирует его при каждом запуске. Но ничто не мешает изменить это поведение: заранее скомпилировать все используемые сборки и генерировать разные VIEWSTATE нагрузки в зависимости от ключей валидации. Также отметим, что у всех сборок отсутствует TypeLib ID. Либо он был удалён специально, либо сборки компилировались в режиме командной строки (как, например, в ysoserial.net).

Каждая сборка получала данные в одном параметре POST-запроса в XOR-зашифрованном и в base64-закодированном виде. Отдельно отметим, что названия параметров не случайны. Именно такие параметры используются в cookies в механизме FBA (Form-based Authentication – специальный модуль IIS, который отвечает за конвертацию пары имени и пароля пользователя в cookies и обратно):

Параметры cookies в механизме FBA
Параметры cookies в механизме FBA (источник)

Этих cookies в памяти атакованного worker-процесса Exchange (пул OWA) очень много, что значительно усложняет восстановление POST-запросов злоумышленников. Нам не удалось получить ни один запрос, так как, скорее всего, garbage collector их удалил из-за большой активности самого worker-процесса.

После выполнения основного действия сборки (Ps, FileView и FileDown) отправляли зашифрованный с помощью XOR ответ злоумышленникам. Ключ шифрования во всех сборках использовался один и тот же:

45 AF 33 27 56 DF CA BB 12 67 9A 52 63 B8 2B C7 62 1F 10 8D 15

В таблице выше мы привели столбец Number of extracted assemblies, чтобы подчеркнуть вышеупомянутую особенность работы VIEWSTATE Exploitation Framework – одно действие – одна сборка.

Это означает, например, что для выполнения одной команды powershell будет отправлен отдельный POST-запрос с Ps-сборкой. Если злоумышленник снова захочет выполнить powershell-команду, то будет отправлен тот же POST-запрос, содержащий VIEWSTATE с Ps-сборкой, но с другим параметром cadata. В памяти атакованного worker-процесса это будет выглядеть так:

Вредоносные .NET Ps-сборки в worker-процессе w3wp (пул OWA) в Process Hacker
Вредоносные .NET Ps-сборки в worker-процессе w3wp (пул OWA) в Process Hacker

Видно, что в адресное пространство атакованного w3wp-процесса загружены одни и те же сборки Microsoft.Exchange.Management.Powershell.Support.dll, которые выполняют powershell-команду.

Рассмотрим сборки, используемые Obstinate Mogwai, которые нам удалось извлечь.

Ps-сборка

Сборка предназначена для выполнения зашифрованной с помощью XOR Powershell-команды, которая передается в POST-запросе в параметре cadata. Отметим, что дочерний процесс powershell.exe при этом не создается из-за использования класса PowerShell из пространства имён System.Management.Automation.

Основной функционал Ps-сборки
Основной функционал Ps-сборки

FileDown-сборка

Сборка предназначена для эксфильтрации файла, путь до которого указывается в XOR-зашифрованном и base64-закодированном виде в параметре cadataIV.

FileView-сборка

Сборка предназначена для получения метаданных указанного файла или каталога, путь до которого указывается в XOR-зашифрованном и base64-закодированном виде в параметре cadataSig.

Основной функционал FileView-сборки"
Основной функционал FileView-сборки

Примечательно, что выходные данные сборки формируются в бинарном виде. В качестве наглядного примера этой логики можно рассмотреть следующий каталог и то, как будет выглядеть собранная о нем информация до XOR-шифрования:

image014.png

Значение в attribs складывается из стандартных констант атрибутов файла или каталога по именам, но злоумышленники изменили их стандартные значения. По-видимому, это было сделано для того, чтобы атрибуты умещались в один байт.

Константа Значение в сборке Значение Windows
FILE_ATTRIBUTE_REPARSE_POINT 0x01 0x400
FILE_ATTRIBUTE_SYSTEM 0x02 0x004
FILE_ATTRIBUTE_HIDDEN 0x04 0x002
FILE_ATTRIBUTE_READONLY 0x08 0x001
FILE_ATTRIBUTE_ARCHIVE 0x10 0x020
FILE_ATTRIBUTE_DIRECTORY 0x20 0x010

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

Важно отметить, что в отличие от того же js_eval, в рамках существующей архитектуры фреймворка мы могли наблюдать только те сборки, которые были непосредственно использованы в атаке. Учитывая опыт исследования js_eval, где механизм выполнения модулей разворачивался на стороне жертвы, можно предположить, что существуют другие сборки, используемые в рамках нового фреймворка, такие как FileUpload (по аналогии с FileDown), загрузка shellcode и тд. Это делает VIEWSTATE Exploitation Framework серьезным инструментом с широким модульным функционалом, который можно легко расширять.

Обнаружение десериализации VIEWSTATE

В этом разделе мы делимся практическими советами по детектированию атак десериализации VIEWSTATE. В зависимости от конкретного случая, для обнаружения могут быть использованы разные техники, описанные ниже.

Обнаружение Memory-only сборок в памяти

Про обнаружение описанных выше .NET-сборок в памяти была написана не одна статья. Регион памяти процесса w3wp, в который была загружена Memory-only сборка с помощью метода Assembly.Load(byte[]), имеет отличительные параметры по которым его можно задетектить:

  • Тип MEM_MAPPED, хотя отсутствует объект отображенного в память файла (пустой столбец Use);
  • Параметр защиты области памяти PAGE_READWRITE, но без EXECUTE.
Особенности поиска .NET сборок в памяти worker-процесса w3wp
Особенности поиска .NET сборок в памяти worker-процесса w3wp

В упомянутой статье от Elastic Security Labs в том числе предлагается powershell-скрипт для поиска и дампа подобных сборок.

Обнаружение по событию Application Event ID 1316

Другой способ детектирования основывается на анализе журнала Windows Application, а именно, на двух разных типах одного события ID 1316:

  • Viewstate was invalid (Event detail code 50204).
  • The viewstate supplied failed integrity check (Event detail code 50203).

Данные события возникают, если произошло какое-либо исключение при десериализации VIEWSTATE.

Для понимания механизма возникновения этих исключений, нужно проследить цепочку вызовов в исходном коде, ведущей к десериализации VIEWSTATE. Она начинается в методе HiddenFieldPageStatePersister::Load, далее вызывается Util::DeserializeWithAssert, после чего выполнение переходит в метод ObjectStateFormatter::Deserialize, где как раз и происходит MAC-валидация (The viewstate supplied failed integrity check), а после –  десериализация VIEWSTATE. Эта последовательность лучше всего представлена на стеке вызовов:

Стек вызовов во время десериализации VIEWSTATE
Стек вызовов во время десериализации VIEWSTATE

Примечательно, что в методе LoadPageStateFromPersistenceMedium присутствует вызов метода ShouldSuppressMacValidationException, который по умолчанию скрывает (не записывает в журнал событий Windows) событие ID 1316 The viewstate supplied failed integrity check (Event detail code 50203). При параметрах по умолчанию оно может появиться в журнале только в том случае, если в POST-запросе имеется корректный параметр __VIEWSTATEGENERATOR. Возможно, это было сделано разработчиками Microsoft в том числе для того, чтобы злоумышленник не получал дополнительной информации о возникшей ошибке валидации VIEWSTATE.

Теперь, когда мы знаем, где выбрасываются эти исключения, подробнее рассмотрим указанные в начале этого раздела два типа события ID 1316.

Событие The viewstate supplied failed integrity check (Event detail code 50203) связано с ошибкой валидации MAC, которая выполняется до десериализации, поэтому если мы видим в журнале это событие, то:

  • VIEWSTATE не был десериализован, то есть пока сервер “в безопасности”;
  • мы видим это событие в журнале, так как в POST-запросе использовался корректный параметр __VIEWSTATEGENERATOR.
image018.png
Событие ID 1316 The viewstate supplied failed integrity check (Event detail code 50203).

Событие Viewstate was invalid (Event detail code 50204) возникает во время или после десериализации. Это означает, что у отправителя VIEWSTATE имеются корректные ключи валидации и необходимо уделить особое внимание содержимому VIEWSTATE (значение PersistedState из события).

Ранее мы уже приводили структуру заголовка сериализованного VIEWSTATE. Его первые два байта (Marker_Format и Marker_Version_1) во всех случаях одинаковые, так как представляют собой заголовок VIEWSTATE. В третьем байте указывается тип сериализованных данных. Мы проанализировали гаджеты утилиты ysoserial.net, которые подходят в качестве нагрузок для VIEWSTATE, и заметили, что все они “под капотом” представляют собой данные, сериализованные BinaryFormatter, то есть имеют тип Token_BinarySerialized (0x32). Эти данные можно использовать для детектирующего правила. В итоге первые три байта вредоносного VIEWSTATE в hex выглядят так:

FF 01 32 или “/wEy” в base64.

Таким образом, если в событии ID 1316 Viewstate was invalid (Event detail code 50204) в первых четырех символах поля PersistedState мы видим /wEy, то обязательно стоит обратить на это внимание. Если подтвердится, что отправитель (поля User host address или Client IP) не является легитимным, то стоит “бить тревогу”, так как у него есть возможность RCE на данном IIS-сервере.

Также отметим другие полезные поля событий ID 1316:

Process ID – можно сразу знать, из какого PID дампить вредоносные сборки;

User-Agent – если является уникальным, то можно отследить другие запросы. Обычно используется в паре с полями Client IP и User host address;

Account name – позволяет оценить права, с которыми может происходить RCE в случае вредоносного VIEWSTATE;

Machine name – имя атакованного хоста.

Особые случаи

Первая особенность, которая существенно усложняет детектирование атаки, – это использование зашифрованного VIEWSTATE, что исключает возможность оперативно анализировать его содержимое.

Пример события Application Event ID 1316 с зашифрованным VIEWSTATE
Пример события Application Event ID 1316 с зашифрованным VIEWSTATE

В версиях .NET 4.5+ он шифруется по умолчанию, даже если параметр конфигурации viewStateEncryptionMode установлен в Never.

В legacy версии <4.5 VIEWSTATE не шифруется по умолчанию, даже при параметре конфигурации viewStateEncryptionMode установленном в Always. Зашифрован VIEWSTATE или нет, определяется по наличию параметра __VIEWSTATEENCRYPTED в запросе.

Отметим, что использование legacy версии .NET (<4.5) или новой (>=4.5) зависит от параметра конфигурации MachineKeyCompatibilityMode и, например, не задается через IIS Manager:

<machineKey validationKey="<REDACTED>" decryptionKey="<REDACTED>" validation="SHA1" decryption="AES" compatibilityMode="Framework45" />

Параметр имеет следующие значения:

    
public enum MachineKeyCompatibilityMode { 
  // 2.0 SP1 mode encryption doesn't use IVs when encrypting data and is included only for legacy reasons.
  Framework20SP1 = 0,
  // 2.0 SP2 mode encryption uses IVs when encrypting data.
  // See: DevDiv Bugs #137864 (http://bugcheck/bugs/DevDivBugs/137864)
  Framework20SP2 = 1,
  // 4.5 mode encryption uses IVs, signing, and a purpose (subkey derivation) to encrypt data.
  // See: DevDiv #48244 (http://vstfdevdiv:8080/DevDiv2/web/wi.aspx?id=48244), which the overall Crypto DCR is a reaction to
  // See: DevDiv #87406 (http://vstfdevdiv:8080/DevDiv2/web/wi.aspx?id=87406)
  Framework45 = 2,
}

По умолчанию, используется значение Framework20SP1, если в файле конфигурации в элементе httpRuntime в атрибуте targetFramework не указано значение "4.5":

<httpRuntime targetFramework="4.5" />

В этом случае в качестве MachineKeyCompatibilityMode будет использоваться Framework45 и VIEWSTATE будут шифроваться по умолчанию.

IIS, который используется в Microsoft Exchange в качестве веб-движка, по умолчанию использует legacy версию .NET-фреймворка.

Вторая особенность: возможны случаи, когда после десериализации вредоносного VIEWSTATE событие Event ID 1316 не будет записано в журнал. Такое поведение определяется специфическим содержимым полезной нагрузки VIEWSTATE. В этом случае для детектирования можно использовать события механизма Event Tracing for Windows (ETW). В этом может помочь информация из двух событий сборки: AssemblyLoad_V1 (наступает, когда сборка загружается) и ModuleLoad_V2 (вызывается при загрузке модуля), примеры таких событий при загрузке вредоносной сборки находятся в Приложении II.

Для детекта можно использовать следующую логику: у вредоносной сборки в событии AssemblyLoad_V1 в поле FullyQualifiedAssemblyName значение PublicKeyToken обычно равно null, так как чаще всего вредоносные сборки не подписаны, а в событии ModuleLoad_V2 в поле ModuleILPath будет только имя сборки, а не полный путь до файла.

Сборка AssemblyLoad_V1 FullyQualifiedAssemblyName PublicKey ModuleLoad_V2
ModuleILPath
Легитимная PublicKeyToken=31bf3856ad364e35 C:\\Windows\\Microsoft.Net\\assembly\\GAC_MSIL\\System.Runtime.InteropServices.RuntimeInformation\\v4.0_4.0.0.0__b03f5f7f11d50a3a\\System.Runtime.InteropServices.RuntimeInformation.dll
Вредоносная null Microsoft.Exchange.Management.Powershell.Support

Стоит уделять особое внимание анализу имени сборки, так как у злоумышленников есть возможность изменить его вручную с целью сделать его похожим на путь.

Мы подготовили Sigma правило, детектирующее загрузку вредоносных in-memory сборок, его можно найти в разделе IOCs.

Митигация. Machine Key

После обнаружения атаки с помощью десериализации VIEWSTATE, необходимо заменить Machine Key, так как сам факт успешной атаки означает, что ключи валидации и расшифровки были скомпрометированы.

Замена вручную созданных ключей

В случае использования обычных (не автоматически сгенерированных) ключей процедуру замены можно выполнить прямо в IIS Manager на вкладке Machine Key:

Смена ключей в IIS Manager
Смена ключей в IIS Manager

Для этого необходимо выполнить следующие действия:

1) Generate Keys -> Apply для атакованных приложений (рекомендуется сделать это для всех приложений веб-сервера).

2) Обязательно перезапустить службу IIS (или весь сервер), так как worker-процесс будет использовать “старые” ключи из памяти:

net stop w3svc
net start w3svc

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

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

Замена автоматически сгенерированных ключей

В случае использования автоматически сгенерированных ключей процедура их замены выглядит нетривиально хотя бы потому, что в публичном пространстве практически нет инструкций на эту тему. В то же время во многих статьях по настройке IIS рекомендуют использовать именно такие ключи, а значит, проблема их замены может иметь массовый характер. Также auto generated-ключи используются по умолчанию во всех приложениях Exchange после установки.

В случае использования автоматически сгенерированных ключей вкладка Machine Key в IIS Manager выглядит так:

Auto generated ключи в IIS Manager
Auto generated ключи в IIS Manager

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

В результате глубокого исследования исходного кода, мы выяснили, что ключи генерируются при каждом новом запуске приложения (при старте домена) на основе мастер-ключа из реестра, который после самой первой генерации (скорее всего, после установки IIS/Exchange или первого использования генерации) не меняется. Происходит это в два этапа:

1) генерация мастер-ключа, который хранится в реестре;

2) runtime-генерация Validation и Decryption ключей.

Мастер-ключ хранится в реестре и в дальнейшем используется при генерации ключей, которые уже непосредственно участвуют в валидации и расшифровке. Сами они хранятся в памяти worker-процесса. Результаты нашего исследования местоположения ключей представлены в таблице:

Identity Master key registry path Encrypted
LocalSystem HKLM\SECURITY\Policy\Secrets\L$ASP.NETAutoGenKeysV44.0.30319.0\ Yes
ApplicationPoolIdentity HKU\S-1-5-82-<Virt_Acc_SID>\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0:AutoGenKeyV4 No
LocalService HKU\S-1-5-19\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0:AutoGenKeyV4 No
NetworkService HKU\S-1-5-20\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0:AutoGenKeyV4 No
Custom Account HKU\S-1-5-21-<USER_SID>\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0:AutoGenKeyV4 No
With Load User Profile False
or
if errors occurred
(fallback like mechanism)
HKLM\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\<USER_SID>:AutoGenKeyV4 No

Identity – пользователь, от которого запускается worker-процесс, он задается в разделе Process Model в настройках пула на вкладке Application Pools в IIS Manager:

Задание пользователя в настройках пула, от которого будет запускаться worker-процесс
Задание пользователя в настройках пула, от которого будет запускаться worker-процесс

Как видно из таблицы, при работе worker-процесса с правами LocalSystem, мастер-ключ хранится в реестре в зашифрованном виде.

Таким образом, алгоритм замены ключей выглядит следующим образом:

1) Удалить все значения из соответствующего ключа реестра (см. таблицу выше). Это позволит при следующем запуске worker-процесса записать новые сгенерированные значения, тем самым обновив мастер-ключ.

2) Выполнить перезапуск worker-процесса или всего сервера, аналогично алгоритму для вручную созданных ключей (net stop w3svc). Пока он не будет перезапущен, worker-процесс будет использовать старые значения ключей.

На рисунке показано, что нужно удалять для разных типов Identity.

reg_autogen_keys.png

Отметим, что для защиты от атак десериализации можно также завести все IIS-серверы за VPN, чтобы у злоумышленников не было возможности отправить запрос с вредоносным VIEWSTATE.  Поля, в которых ранее были указаны значения ключей, не редактируются и не содержат самих значений. Чтобы понять, как их заменить, нужно исследовать механизм их генерации и хранения.

Заключение

Несмотря на то, что уязвимость десериализации VIEWSTATE существует уже более 10 лет, мы видим, что с 2020 года началась и продолжается её активная эксплуатация в дикой среде различными группировками, в том числе Obstinate Mogwai. Обнаружить эксплуатацию этой уязвимости – задача нетривиальная. Детектировать инцидент приходится буквально по одному событию журнала Windows, которое в определенных обстоятельствах может и не записываться. Кроме того, атакуемые процессы на серверах Exchange работают с высочайшими привилегиями (worker-процесс Exchange работает с правами LocalSystem), что делает эту уязвимость ещё более опасной.

Надеемся, что в нашей статье вы нашли много полезной информации об уязвимости десериализации VIEWSTATE и способах детектирования подобных атак. Ниже представлены индикаторы компрометации.

А если вы увидели подозрительную активность в своей сети и считаете, что тоже стали жертвой хакерской группировки, пишите нам. Эксперты Solar 4RAYS помогут выявить инцидент и ограничить его распространение, а также защитить вашу инфраструктуру от подобных атак в будущем.

P.S. Кстати, Obstinate Mogwai – не единственная группировка, использующая уязвимость десериализации VIEWSTATE. В последние месяцы мы видели как минимум ещё одну. Подробнее о ней расскажем на грядущей конференции Positive Hack Days и в нашем блоге. Следите за обновлениями!

IOCs

File hashes

MD5
41a15b8d3d8c840be37690f8353e8934
6c63601e9c115c0e7ff3220e023b33bb
817c8c15040261490b75d5476f8ba5d6

SHA1
41b543f397e77461dee196b830c30024dc20605d
b4d3db052fa682abd38218620c27351766275911
348dbaa262410684153228a904c60e0d9cc17014

SHA256
503275fbf9bcd6575a6f8a014c903727eb28f2d77f067082fcf4f60c2ca630f5
06240b9dfb75b8a430c7c34cbb13cd066acf7f0e1d889891f576d7f4bc999c15
4608df9207e6612bcc548d0db39a2d03ed74c9c0f30c696a3a6ef2cc792c250a

IP

IP-адреса, с которых наблюдалась эксплуатация уязвимости десериализации VIEWSTATE.

45.12.67[.]18
77.223.109[.]162
77.223.109[.]163
77.223.109[.]164
77.223.109[.]165
193.47.34[.]229

Sigma

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

    
title: Detect Malicious Assembly and Module Load Events 
id: 2617e7ed-adb7-40ba-b0f3-8f9945fe6c09
status:
description: Detects suspicious Assembly and Module Load events indicative of potentially malicious activity.
references:
author: SOLAR 4RAYS
date:
modified:
logsource:
  product: SilkETW
detection:
  assemblywithoutsignature:
    EventName: 'Loader/AssemblyLoad'
    XmlEventData.FullyQualifiedAssemblyName|contains: 'PublicKeyToken=null'
  modulewithoutpath:
    EventName: 'Loader/ModuleLoad'
    XmlEventData.ModuleILPath|re: '\s"[^\\]+"'
  condition: assemblywithoutsignature and modulewithoutpath
falsepositives:
  - Legitimate changes to assemblies and modules.
level: high

Yara

    
rule mem_apt_obstinate_mogway_viewstate_assemblies {
      meta:
        description = "detects malicious assemblies loaded in memory via viewstate deserialization using leaked keys"
        author = "SOLAR 4RAYS"
        date = "20240423"
        hash = "41a15b8d3d8c840be37690f8353e8934" // execute powershell cmd
        hash = "6c63601e9c115c0e7ff3220e023b33bb" // upload file to C2
        hash = "817c8c15040261490b75d5476f8ba5d6" // fileview
      strings:
            // xor key used in viewstate payloads
            $ = { 45 AF 33 27 56 DF CA BB 12 67 9A 52 63 B8 2B C7 62 1F 10 8D 15 }
      condition:
            any of them
}          
    

MITRE

Техника, тактика Процедура
Initial Access
T1190 – Exploit Public-Facing Application
APT-группировка Obstinate Mogway (далее - злоумышленник) эксплуатировала уязвимость десериализации VIEWSTATE для удаленного выполнения кода на Exchange-серверах жертвы
Execution
T1059.001 – Command and Scripting Interpreter: PowerShell
Злоумышленник выполнял powershell-команды путем загрузки .NET-сборок в память процесса w3wp.exe в результате эксплуатации уязвимости десериализации VIEWSTATE, а также запускал .NET RAT в памяти путем запуска powershell скрипта
Persistence
T1505.003 Server Software Component: Web Shell
Злоумышленник в начале инцидента использовал различные вебшеллы для выполнения команд и закрепления
Defense Evasion
T1055.001 Process Injection: Dynamic-link Library Injection
Злоумышленник загружал вредоносные .NET-сборки в память процесса w3wp.exe
Defense Evasion
T1055.003 Process Injection: Thread Execution Hijacking
Злоумышленник загружал .NET RAT в процесс powershell.exe
Defense Evasion
T1036.005 – Masquerading: Match Legitimate Name or Location
Злоумышленник назвал .NET-сборки именами, мимикрирующими под легитимные .NET-сборки для дополнительной скрытности
Defense Evasion
T1070.004 – Indicator Removal on Host: File Deletion
Злоумышленник удалял свои файлы и архивы с данными
Credential Access
T1003.001 OS Credential Dumping: LSASS Memory
Злоумышленник включал настройку в реестре для упрощения дампа аутентификационного материала в открытом виде
Discovery
T1012 Query Registry
Злоумышленник проверял настройки реестра через утилиту reg.exe
Discovery
T1083 File and Directory Discovery
Злоумышленник получал содержимое интересующих его каталогов с помощью Get-ChildItem команд
Discovery
T1087.001 Account Discovery: Local Account
Злоумышленник выполнял команды net user
Command and Control
T1071.001 – Application Layer Protocol: Web Protocols
Злоумышленник отправлял POST-запросы с вредоносными нагрузками VIEWSTATE
Command and Control
T1132.001 Data Encoding: Standard Encoding
Злоумышленник в POST-запросах отправлял зашифрованные команды (xor + base64) и получал результаты их выполнения в xor-зашифрованном виде
Command and Control
T1001.003 Data Obfuscation: Protocol Impersonation
Злоумышленник отправлял POST-запросы с параметрами, имена которых мимикрируют под легитимные названия cookies в POST-запросах механизма Form-based Authentication.

Приложение I – .NET-сборки из памяти


name md5 comment
Microsoft.Exchange.Management.Powershell.Support.dll 41a15b8d3d8c840be37690f8353e8934 Ps-сборка из памяти w3wp.exe
Microsoft.Exchange.UM.UMCommon.dll 817c8c15040261490b75d5476f8ba5d6 FileView-сборка из памяти w3wp.exe
Microsoft.Exchange.MessageSecurity.dll 6c63601e9c115c0e7ff3220e023b33bb FileDown-сборка из памяти w3wp.exe

Приложение II – Примеры событий при загрузке вредоносной сборки

AssemblyLoad_V1

    
{ 
  "ProviderGuid": "e13c0d23-ccbc-4e12-931b-d9cc2eee27e4",
  "YaraMatch": [],
  "ProviderName": "Microsoft-Windows-DotNETRuntime",
  "EventName": "Loader/AssemblyLoad",
  "Opcode": 37,
  "OpcodeName": "AssemblyLoad",
  "TimeStamp": <REDACTED>,
  "ThreadID": 12268,
  "ProcessID": 15308,
  "ProcessName": "w3wp",
  "PointerSize": 8,
  "EventDataLength": 238,
  "XmlEventData": {
    "FormattedMessage": "AssemblyID=2,209,623,677,488;\r\nAppDomainID=2,209,468,624,928;\r\nAssemblyFlags=0;\r\nFullyQualifiedAssemblyName=0;\r\nClrInstanceID=Microsoft.Exchange.Management.Powershell.Support, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null ",
    "ProviderName": "Microsoft-Windows-DotNETRuntime",
    "ClrInstanceID": "29",
    "AppDomainID": "2,209,468,624,928",
    "BindingID": "0",
    "MSec": "7581.5038",
    "AssemblyID": "2,209,623,677,488",
    "PID": "15308",
    "TID": "12268",
    "AssemblyFlags": "0",
    "PName": "",
    "FullyQualifiedAssemblyName": "Microsoft.Exchange.Management.Powershell.Support, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
    "EventName": "Loader/AssemblyLoad"
  }
}

ModuleLoad_V2

    
{
      "ProviderGuid": "e13c0d23-ccbc-4e12-931b-d9cc2eee27e4", 
  "YaraMatch": [],
  "ProviderName": "Microsoft-Windows-DotNETRuntime",
  "EventName": "Loader/ModuleLoad",
  "Opcode": 33,
  "OpcodeName": "ModuleLoad",
  "TimeStamp": <REDACTED>,
  "ThreadID": 12268,
  "ProcessID": 15308,
  "ProcessName": "w3wp",
  "PointerSize": 8,
  "EventDataLength": 170,
  "XmlEventData": {
    "ModuleID": "140,735,524,732,376",
    "ManagedPdbSignature": "00000000-0000-0000-0000-000000000000",
    "Reserved1": "0",
    "ManagedPdbBuildPath": "",
    "ModuleNativePath": "",
    "NativePdbBuildPath": "",
    "FormattedMessage": "ModuleID=140,735,524,732,376;\r\nAssemblyID=2,209,625,905,760;\r\nModuleFlags=Manifest;\r\nModuleILPath=0;\r\nModuleNativePath=Microsoft.Exchange.Management.Powershell.Support;\r\nClrInstanceID=;\r\nManagedPdbSignature=29;\r\nManagedPdbAge=00000000-0000-0000-0000-000000000000;\r\nManagedPdbBuildPath=0;\r\nNativePdbSignature=;\r\nNativePdbAge=00000000-0000-0000-0000-000000000000;\r\nNativePdbBuildPath=0 ",
    "MSec": "9847.4270",
    "NativePdbAge": "0",
    "ModuleFlags": "Manifest",
    "AssemblyID": "2,209,625,905,760",
    "PID": "15308",
    "NativePdbSignature": "00000000-0000-0000-0000-000000000000",
    "ModuleILPath": "Microsoft.Exchange.Management.Powershell.Support",
    "TID": "12268",
    "ManagedPdbAge": "0",
    "ProviderName": "Microsoft-Windows-DotNETRuntime",
    "PName": "",
    "ClrInstanceID": "29",
    "EventName": "Loader/ModuleLoad"
  }
}