Стаття описує використання протоколу аутентифікації Kerberos та його розширення PKINIT, яке використовує криптографію з відкритим ключем для покращення безпеки. Детально пояснюємо, як цей метод допомагає захистити систему від атак на основі слабких паролів у середовищах, таких як Active Directory.
Дисклеймер: Ми усвідомлюємо, що на деяких фото або в матеріалах можуть зустрічатися написи російською мовою. Проте хіба це не чудово — використовувати їхню ж мову проти них?
У статті розглядається принцип роботи одного з розширень протоколу Kerberos – PKINIT. Після теоретичного вступу будуть детально проаналізовані техніки атак, пов’язані з цим розширенням, а саме: Shadow Credentials та UnPAC the hash.
Коротко про суть цих технік: перша дозволяє зловмиснику забезпечити стійку присутність у домені, навіть у разі зміни паролів, тоді як друга — отримати NT-хеш пароля облікового запису за умови наявності дозволу на зміну атрибута msDS-KeyCredentialLink для відповідного облікового запису.
PKINIT (Public Key Cryptography for Initial Authentication in Kerberos) — це розширення протоколу Kerberos, яке дозволяє застосовувати криптографію з відкритим ключем на етапі початкової автентифікації.
Чому виникла необхідність у цьому розширенні? Які його основні переваги?
Щоб надати коректну відповідь, необхідні ґрунтовні знання в криптографії, оскільки подібні питання досить складно пояснити простими словами. Якщо говорити надто спрощено, ті, хто знайомий із темою, вважатимуть відповідь поверхневою, а новачки можуть її не зрозуміти. Проте цей матеріал не претендує на статус суворої наукової роботи, тому я спробую дати вступне пояснення, яке допоможе краще зрозуміти суть питання.
Оцінюючи безпеку інформаційних ресурсів організації, одним із класичних ризиків залишається використання паролів, вразливих до словникових атак або атак методом перебору. Сам протокол Kerberos не забезпечує ефективного захисту від таких атак (докладніше про це згадувалося раніше в контексті розпилення паролів). Звісно, в Active Directory можна запровадити сувору парольну політику, а також проводити моніторинг журналів подій для виявлення та блокування атак. Проте як усунути цю проблему на більш глибокому рівні?
Почнемо з основ. Що таке пароль? У ГОСТ Р 58833-2020 «Захист інформації. Ідентифікація та автентифікація. Загальні положення» надано таке визначення:
Пароль — це конфіденційна інформація для автентифікації, зазвичай у вигляді рядка символів.
Ключовим у цьому визначенні є слово «зазвичай». Адже паролем може виступати не лише рядок символів, а й апаратний токен, відбиток пальця, зображення сітківки ока, голос тощо.
Варто також відзначити різницю між двофакторною та двоетапною автентифікацією, про що більш детально можна прочитати в окремих матеріалах.
Паролі у вигляді рядка символів мають низку вразливостей, зокрема:
можливість використання словникових паролів;
необхідність запам’ятовувати паролі;
ризик випадкового розголошення паролів користувачами під час фішингових атак, у листуванні або в загальнодоступних файлах;
у разі компрометації сервера зловмисник може отримати паролі користувачів та використати їх для атак методом повторного відтворення.
PKINIT покликаний усунути ці недоліки, оскільки дозволяє використовувати криптографію з відкритим ключем. Завдяки цьому розширенню підтримується автентифікація за сертифікатами, а також стає можливою реалізація двофакторної автентифікації, що унеможливлює застосування словникових атак.
Далі розглянемо, що таке «відкритий ключ» у контексті криптографії.
Спершу слід зрозуміти різницю між симетричним та асиметричним шифруванням.
Симетричне шифрування передбачає використання одного і того ж ключа як для шифрування, так і для розшифрування даних. Це означає, що і клієнт, і сервер повинні володіти однаковим секретом.
Асиметричне шифрування базується на парі взаємопов’язаних ключів: відкритому та закритому. Закритий ключ зберігається в таємниці, а відкритий може бути загальнодоступним. Дані, зашифровані одним із ключів, можуть бути розшифровані лише за допомогою іншого.
Для кращого розуміння наведемо побутовий приклад:
При симетричному шифруванні інформацію передають у скриньці, закритій на звичайний замок. Ключ від цього замка мають як відправник, так і одержувач. Щоб зашифрувати повідомлення, відправник кладе його в скриньку та замикає на замок своїм ключем. Одержувач, маючи той самий ключ, відкриває замок і отримує повідомлення.
При асиметричному шифруванні відправник використовує замок, який може бути відкритий лише спеціальним ключем одержувача. Відповідно, кожен учасник зберігає свій ключ у таємниці, і навіть якщо хтось перехопить скриньку з повідомленням, він не зможе його відкрити без закритого ключа.
При асиметричному шифруванні повідомлення передається у ящику, закритому на незвичайний замок . Особливість замку полягає в тому, що для його закриття нічого не потрібно, замок достатньо лише заклацнути, а для відкриття вже необхідний ключ. Одержувач створює багато копій замку і роздає їх усім охочим, а ключ зберігає в секреті. Кожен, хто хоче відправити повідомлення, кладе його в ящик і закриває його на замок, що защіпається, відкрити який може тільки одержувач.
Симетричне шифрування відрізняється швидкою роботою, однак його слабким місцем є управління ключами, зокрема проблема передачі загального секретного ключа через відкриті канали зв’язку. Це є критично важливим для встановлення захищених з’єднань, таких як в Інтернеті.
Асиметрична криптографія з відкритим ключем вирішує зазначену проблему, хоча й поступається за швидкодією.
Для кращого розуміння наведемо умовний, але реалістичний приклад. Уявімо мережу, де кожен користувач має власний сервер з розгорнутою поштою та блогом, що містить контактну інформацію.
Розглянемо можливий процес обміну повідомленнями у вказаній мережі:
Користувач Аліса знаходить сайт користувача Боб і в розділі з контактною інформацією дізнається відкритий ключ Боба
Аліса генерує секретний сесійний ключ, зашифровує його з використанням відкритого ключа Боба і відправляє зашифроване повідомлення Бобу.
Боб, отримавши повідомлення, розшифровує його за допомогою свого закритого ключа та отримує сесійний ключ
Боб відправляє Алісі повідомлення, зашифроване з використанням отриманого сесійного ключа. Подальше спілкування шифрується на встановленому сесійному ключі.
Важливо відзначити, що вказаний приклад схильний до атаки “людина по середині”. Атакуючий може нав’язати відправнику свій сервер замість сервера Боба, наприклад, за допомогою ARP-spoofing. Підроблений сервер може містити копію сайту Боба, але вже з іншим значенням відкритого ключа, секретний ключ якого є у атакуючого. В результаті відправник направить атакуючому повідомлення зашифроване на підставному відкритому ключі.
Виникає питання, як Аліса може переконатися, що відкритий ключ Боба дійсно належить Бобу?
Криптографія з відкритим ключем працює також у зворотному напрямку: дані, зашифровані за допомогою закритого ключа, можуть бути розшифровані відповідним відкритим ключем.
У чому сенс такого підходу? Припустимо, у нас є справжній відкритий ключ Боба, і Боб хоче зробити публічне повідомлення від свого імені. У цьому випадку:
Боб створює текст повідомлення та шифрує його власним закритим ключем.
Боб розміщує зашифроване повідомлення у себе на сайті.
Будь-хто може розшифрувати повідомлення відкритим ключем Боба та прочитати його.
Якщо розшифрування було успішним, це підтверджує, що повідомлення створено власником закритого ключа, тобто Бобом. Таким чином, інформація може бути «підписана» цифровим підписом.
Іншими словами, цифровий підпис дозволяє перевірити автентичність джерела повідомлення та переконатися, що інформація не була змінена під час передачі. Це забезпечує автентифікацію та контроль цілісності даних.
Однак шифрувати все повідомлення є ресурсозатратним процесом, тому зазвичай шифрується лише хеш повідомлення, отриманий за допомогою криптографічної хеш-функції. До оригінального повідомлення додається цей зашифрований хеш, який називається підписом. Той, хто бажає перевірити справжність, обчислює хеш повідомлення самостійно та порівнює його з розшифрованим підписом.
Проте проблема аутентифікації відкритих ключів поки що залишається невирішеною. Одним із рішень є запровадження сертифікації відкритих ключів за допомогою довіреної третьої сторони – центру сертифікації. У цьому випадку перед публікацією свого відкритого ключа користувач повинен звернутися до центру сертифікації, щоб отримати сертифікат.
Сертифікат відкритого ключа може містити багато полів, наприклад:
Ім’я користувача
Відкритий ключ користувача
Термін дії сертифіката
Найменування центру сертифікації
Але найголовнішим полем є цифровий підпис , що є хеш від усіх полів сертифіката, зашифрований на закритому ключі центру сертифікації.
Розглянемо приклад процедури обміну повідомленнями за наявності центру сертифікації:
Аліса генерує пару відкритий/закритий ключ.
Аліса аутентифікується у центрі сертифікації та передає туди свої дані, зокрема відкритий ключ.
Центр сертифікації перевіряє отримані дані та підписує їх із використанням свого закритого ключа. В результаті виходить сертифікат відкритого ключа, який передається Алісі.
Кроки 1-3 вважаються стандартними всім користувачів мережі. Можна вважати, що під час реєстрації кожен користувач отримує сертифікат свого відкритого ключа та відкритий ключ Центру сертифікації. Таким чином Боб також має сертифікат для свого відкритого ключа і відкритий ключ Центру сертифікації.
Бажаючи надіслати повідомлення, Боб із сайту Аліси отримує відкритий ключ і відповідний йому сертифікат. За допомогою відкритого ключа центру сертифікації Боб перевіряє справжність отриманого сертифіката. Боб довіряє Центру сертифікації, а успішна перевірка цифрового підпису сертифіката означає, що Центр сертифікації, у свою чергу, перевірив належність відкритого ключа Алісі. Тому Боб може бути впевнений, що витягнутий із сайту відкритий ключ справді належить Алісі.
Боб шифрує повідомлення з використанням відкритого ключа Аліси та підписує його з використанням свого закритого ключа. Дані передаються Алісі.
Отримавши повідомлення, Аліса розшифровує його за допомогою свого закритого ключа. Крім того, завдяки електронному підпису Аліса може переконатися, що повідомлення надіслано саме Бобом. Таким чином, забезпечується взаємна автентифікація користувачів.
Цікаво відзначити, що в розглянутій схемі Центр сертифікації необхідний тільки при реєстрації користувача.
Наведений приклад ілюструє загальну ідею, що з допомогою запровадження додаткової довіреної сутності можна вирішити проблему управління відкритими ключами.
Важливо зауважити, що з впровадженням процедури сертифікації в домені з’являється ще один центр довіри.
PKINIT дозволяє впровадити криптографію з відкритим ключем на етапі попередньої автентифікації в протоколі Kerberos. У попередній частині вже було детально розглянуто етап попередньої автентифікації. Коротко нагадаємо: у «звичайному» сценарії для автентифікації клієнта і сервера необхідно, щоб обидві сторони мали спільний симетричний ключ.
Тепер розглянемо в першому наближенні обмін повідомленнями за допомогою PKINIT:
Клієнт відправляє серверу автентифікації поточну мітку часу, підписану своїм закритим ключем, а також сертифікат призначений для перевірки справжності відкритого ключа, що передається.
Сервер автентифікації перевіряє за допомогою відкритого ключа центру сертифікації, перевіряє справжність отриманого сертифіката відкритого ключа клієнта.
З використанням перевіреного відкритого ключа клієнта сервер автентифікації перевіряє підпис мітки часу та порівнює його значення з поточним.
Сервер автентифікації надсилає відповідь Клієнту.
Клієнт із використанням свого закритого ключа розшифровує одну з частин прийнятої відповіді.
У розшифрованій частині клієнт перевіряє сертифікат відкритого ключа сервера автентифікації.
За допомогою отриманого відкритого ключа сервера автентифікації клієнт перевіряє підпис ключа AS-REP.
За допомогою AS-REP ключа Клієнт розшифровує сесійний ключ для спілкування з контролером домену.
Клієнт отримує TGT та сесійний ключ для KDC.
Після отримання TGT і сесійного ключа подальші етапи автентифікації у Kerberos виконуються за стандартною схемою, як і в класичному сценарії.
У допитливих читачів може виникнути питання: чому використовують проміжний симетричний ключ (див. крок №3) замість того, щоб одразу передавати сесійний ключ? Основна причина полягає в тому, що існують різні способи генерації симетричного ключа, кожен із яких має свої переваги й недоліки. Занурюватися у всі можливі варіанти немає необхідності, тому ми зосередилися на найпоширенішому з них для прикладу. Варто також зазначити, що розглянутий сценарій є спрощеним, і в реальних умовах передані повідомлення містять додаткові поля, такі як випадкові мітки та ідентифікатори.
Далі клієнт надсилає контролеру домену запит на отримання TGS квитка для доступу до робочої станції. Зверніть увагу, що як принцип сервісу вказується host/hostname.domain .
Контролер домену копіює PAC із TGT, формує TGS-квиток і надсилає його клієнту. Важливо врахувати, що це сценарій інтерактивного входу, тому «секрет хоста» вважається відомим для робочої станції.
Система надасть клієнту доступ, проте ключовий момент у тому, що NT-хеш клієнта буде розшифрований за допомогою ключа AS-REP і збережений у кеші LSA. Якщо в майбутньому якийсь сервіс не зможе використовувати Kerberos, система автоматично застосує NT-хеш із кешу LSA для автентифікації через NTLM.
Таким чином використання смарт-карт не виключає застосування протоколу NTLM.
Раніше ми розглядали модель довіри на основі сертифікатів із використанням центрів сертифікації та смарт-карток. Це одна з перших моделей, впроваджених в Active Directory, але пізніше Microsoft додала нову модель під назвою Key Trust, яка з’явилася разом із Windows Server 2016 і технологією Windows Hello for Business.
Windows Hello — це технологія, що забезпечує двофакторну автентифікацію за допомогою біометричних даних (відбиток пальця, обличчя, райдужка ока) та інтегрована в операційні системи Windows. Вона підтримує як автономні пристрої, так і доменні середовища.
Детальніше ознайомитися з Windows Hello можна за такими посиланнями:
“Exploiting Windows Hello for Business” , BlackHat, 2019 рік.
У Key Trust замість сертифікатів для автентифікації клієнта контролер домену використовує атрибут msDS-KeyCredentialLink, який містить відкриті ключі, прив’язані до облікового запису. Цей атрибут підтримує кілька значень, що дозволяє одному користувачу входити до системи з різних пристроїв, кожен із яких може мати свій закритий ключ. Наприклад, це актуально, коли користувач використовує дві робочі станції із різними модулями TPM.
Хоча може здатися, що у цій моделі центр сертифікації не потрібен, це не зовсім так. Сертифікати все одно необхідні для перевірки справжності відкритого ключа контролера домену. Отже, клієнт підтверджує автентичність контролера за сертифікатом, а контролер перевіряє клієнта, розшифровуючи його повідомлення відкритим ключем із атрибуту msDS-KeyCredentialLink.
Як ця інформація може допомогти під час тестування на проникнення? Окрім атак Shadow Credentials та UnPAC the Hash, про які згадувалося раніше, існують інші техніки, зокрема PassTheCertificate.
PassTheCertificate
Умови: наявність ключової пари та відповідного сертифіката користувача.
Результат: отримання доступу до доменних ресурсів із правами користувача, чиї ключі було викрадено.
PassTheCertificate є базовою технікою, що дозволяє автентифікуватися в домені без введення пароля, використовуючи ключі та сертифікат користувача. Найчастіше ключі зустрічаються у форматах PEM або PFX:
PEM:
.pem— відкритий ключ і сертифікат.key— закритий ключ
PFX: Включає відкритий і закритий ключі разом із сертифікатом у зашифрованому вигляді.
Після отримання цих файлів можна провести автентифікацію та отримати TGT для відповідного облікового запису.
Команди для виконання атаки в Linux:
Для роботи з PKINIT в Linux існує набір скриптів PKINITtools. Для автентифікації сертифіката скористаємося утилітою gettgtpkinit.py
Аутентифікація за сертифікатом у форматі PFX:
gettgtpkinit.py -cert-pfx "PATH_TO_PFX_CERT" -pfx-pass "CERT_PASSWORD" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"
Аутентифікація за сертифікатом у форматі PFX, кодованому в Base64:
gettgtpkinit.py -pfx-base64 $(cat "PATH_TO_B64_PFX_CERT") "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"
Аутентифікація по сертифікату у форматі PEM:
gettgtpkinit.py -cert-pem "PATH_TO_PEM_CERT" -key-pem "PATH_TO_PEM_KEY" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"
Rubeus підтримує роботу з PFX-сертифікатами, кодованими в Base64:
Rubeus.exe asktgt /user:"TARGET_SAMNAME" /certificate:"BASE64_CERTIFICATE" /password:"CERTIFICATE_PASSWORD" /domain:"FQDN_DOMAIN" /dc:"DOMAIN_CONTROLLER" /show
У Windows конвертувати PEM у PFX можна за допомогою openssl:
openssl pkcs12 -in "cert.pem" -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out "cert.pfx"
Протягом PassTheCertificate логічно використовувати PassTheTicket (PassTheTicket), але є й інші атаки, які будуть розглянуті далі.
Умови: наявність ключової пари та відповідного сертифіката для облікового запису.
Результат: отримання NT-хешу пароля облікового запису.
Хоча отримання TGT корисне, NT-хеш відкриває значно більше можливостей. Щоб зрозуміти суть атаки UnPAC the Hash, розглянемо принцип роботи автентифікації User-to-User (U2U) у протоколі Kerberos.
U2U-автентифікація була створена для ситуацій, коли користувачі можуть надавати сервіси один одному, наприклад, через NFS або FTP. Оскільки робоча станція користувача вважається ненадійною для зберігання довгострокових ключів, як секрет сервісу використовується тимчасовий сесійний «ключ користувача для KDC».
Ключовий момент у тому, що KDC не зберігає ці сесійні ключі, а витягує їх із TGT. Тоді виникає питання — як KDC шифрує TGS-квиток? Відповідь: сесійний ключ витягується з TGT, а потім використовується для шифрування TGS-квитка, що забезпечує безпеку процесу.
Для кращого розуміння того, що відбувається покроково, розберемо етапи U2U-аутентифікація.
Аліса проходить автентифікацію до сервісу, що надається Бобом, пред’являючи TGS-квиток, зашифрований довгостроковим ключем Боба (наприклад, NT-хешем). Однак Сервіс Боба не може розшифрувати цей квиток, оскільки працює на звичайній робочій станції, яка не має доступу до довготривалого ключа Боба.
Щоб вирішити цю проблему, Сервіс Боба надсилає відповідь із вимогою використання механізму U2U та додає свій TGT. Передача TGT у відкритому вигляді вважається безпечною, навіть якщо мережевий трафік доступний потенційним зловмисникам, оскільки вся критична інформація в ньому зашифрована.
Аліса запитує у контролера домену TGS-квиток з використанням U2U-автентифікації, про що свідчить спеціальний прапор. Крім того, як додатковий квиток Аліса додає TGT Боба. Контролер домену в свою чергу витягує з отриманих TGT сесійні ключі Аліси та Боба.
Контролер домену відправляє Алісі TGS-квиток зашифрований з використанням вилученого на попередньому кроці сесійного ключа Боба.
Тепер Сервіс Боба може розшифрувати отриманий TGS-квиток та надати доступ для Аліси.
Використання короткочасного ключа як секрет також не дозволяє провести Kerberoasting щодо простих користувачів. У результаті, навіщо були потрібні непрості пояснення всіх цих механізмів?
Справа в тому, що користувач може здійснити PKINIT + U2U запит на автентифікацію до себе і таким чином отримати свій NT-хеш. Для початку уявімо, що Аліса вже отримала TGT з використанням PKINIT, як це було розглянуто раніше.
Аліса звертається до KDC із запитом U2U-аутентифікації до свого ж сервісу і надає як додатковий квиток свій TGT, отриманий в результаті PKINIT і при цьому містить PAC зі своїм NT-хешем.
Контролер домену надсилає Алісі TGS-квиток, зашифрований її сесійним ключем для KDC. Аліса використовує свій сесійний ключ, щоб розшифрувати TGS-квиток, а потім отримує свій NT-хеш, розшифровуючи його за допомогою AS-REP ключа.
Таким чином, якщо у зловмисника є ключова пара та відповідний сертифікат облікового запису, він може отримати NT-хеш пароля цього облікового запису. На практиці техніка PassTheCert у поєднанні з UnPAC the Hash широко застосовується для подальшої експлуатації результатів попередніх атак.
Команди для виконання атаки в Linux:
Спочатку запитуємо TGT за допомогою PKINIT:
gettgtpkinit.py -cert-pfx "PATH_TO_CERTIFICATE" -pfx-pass "CERTIFICATE_PASSWORD" "FQDN_DOMAIN/TARGET_SAMNAME" "TGT_CCACHE_FILE"
З використанням отриманого TGT та AS-REP ключа (відобразиться в консолі в результаті виконання gettgtpkinit) отримуємо NT-хеш:
export KRB5CCNAME="TGT_CCACHE_FILE" getnthash.py -key 'AS-REP key' 'FQDN_DOMAIN'/'TARGET_SAMNAME'
Команди для виконання атаки у Windows:
Rubeus.exe asktgt /getcredentials /user:"TARGET_SAMNAME" /certificate:"BASE64_CERTIFICATE" /password:"CERTIFICATE_PASSWORD" /domain:"FQDN_DOMAIN" /dc:"DOMAIN_CONTROLLER" /show
У цьому матеріалі було детально розглянуто основні принципи функціонування розширення PKINIT та роботу сертифікатів у середовищі Active Directory. Також було проаналізовано типові техніки атак, що базуються на описаних механізмах, і наведено практичні приклади з командами для їх реалізації.