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

Содержание статьи

  • Особенности неограниченного делегирования
  • Особенности эксплуатации
  • Обнаружение нужной службы
  • Подключение к службе
  • Получение сессионных ключей
  • Расшифровка AP-REQ
  • Выводы

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

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

 

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

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

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

Внут­ри это­го тикета будет лежать так­же сес­сион­ный ключ, что поз­волит служ­бе без проб­лем оли­цет­ворять кли­ента. Далее у кли­ента будет 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, нуж­но, что­бы выпол­нялись сле­дующие тре­бова­ния:

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

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

  • Об­раща­емся к служ­бе с неог­раничен­ным делеги­рова­нием.
  • По­луча­ем сге­нери­рован­ный AP-REQ.
  • Из­вле­каем сес­сион­ный ключ для рас­шифров­ки аутен­тифика­тора.
  • Рас­шифро­выва­ем аутен­тифика­тор.
  • Из­вле­каем 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

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

    int wmain(char argc, wchar_t* argv[]) { 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 removeLeadingCharacters(LPCWSTR originalString) { 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 поз­волят нам сымити­ровать обра­щение к служ­бе с неог­раничен­ным делеги­рова­нием.

    Источник: xakep.ru

    Ответить

    Ваш адрес email не будет опубликован. Обязательные поля помечены *