В последнее время часто можно услышать, что атаки на ADCS стали чем-то тривиальным: после выхода информативной статьи Certified Pre-Owned от Specter Ops почти каждый пентестер знает, что такое ESC1 и ESC8, и, увидев в Cert Publishers компьютеры, сразу бежит туда. Однако Web Enrollment помимо атак может чейнить уязвимости и служить отличным вариантом для Initial Access. Разберемся, как его применять, на примере нашего проекта по инфраструктурному пентесту.
Иногда бывает, что работы надо проводить «серым ящиком». Так было и у нас, когда мы приехали в офис к заказчику. Но какой пентестер не проводит работы «черным ящиком», даже когда есть свеженькая УЗ, верно? После всех дефолтных приготовлений и проверок мы обнаруживаем в Responder, что некая компьютерная УЗ так и просится к нам в гости.
При попытке разрешения имени через сетевые протоколы Windows руководствуется следующим приоритетом:
DNS
LLMNR
NBNS
Через протокол DNS хосты по умолчанию отправляют запросы на поиск веб-сервера, где хранится конфигурационный файл с настройками прокси. Происходит это через протокол WPAD (Web Proxy Auto Discovery protocol) — он часто мелькает в отправленных запросах. В случае, если DNS-сервер ответит, что не знает такого, то хост спустится до LLMNR и NBNS, и тут Responder протянет руку помощи каждому запросу, представившись нужным сервером, а взамен попросит пройти аутентификацию. Можно усложнить жизнь пентестерам, создав так называемые wildcard-записи, которые будут удовлетворять все запросы через DNS, и они не опустятся до LLMNR и NBNS.
Поймали Net-NTLMv2-хешированное значение пароля машинной УЗ — банальная ситуация, запомним ее и пойдем дальше.
Одним из векторов получения УЗ является использование утилиты pre2k, механизм работы которой расписали ребята из TrustedSec. Она позволяет запросить TGT-билет для любой машинной УЗ в домене, и если она была создана с флагом Assign this computer account as a pre-Windows 2000 computer, то пароль в нижнем регистре у нее будет равен ее имени. Сканируем доступный нам скоуп, заносим найденные хосты в файлик и получаем валидную УЗ в домене.
Аутентификация такими УЗ проходит по протоколу Kerberos. И bloodhound, и Certipy его поддерживают. Собираем информацию о домене и обнаруживаем, что все УЗ администраторов домена находятся в группе Protected Users. Идем в центр сертификаций и обнаруживаем уязвимости ESC1 и ESC8. Если вы столкнетесь с огромным доменом, рекомендую использовать rusthound, который собирает информацию о домене гораздо быстрее bloodhound и иногда может порадовать интересными находками (кто решал наш стенд по внутрянке на OFFZONE 2024, знает, о чем я).
Полезно обращаться к AD CS не только от компьютерных УЗ, но и от пользовательских, поскольку права на чтение шаблонов могут быть разными и выводы могут не совпадать. Также, если рассматривать утилиту Certipy и ей подобные, я редко использую флаг vulnerable, поскольку:
можно пропустить важную информацию из-за прав УЗ, с которой идет запрос;
изучать шаблоны вручную не так сложно, как кажется.
Например, если искать ESC1, то нужно обращать внимание всего лишь на три поля: Client Authentication, Enrollee Supplies Subject и Enrollment Rights. Если первые два пункта стоят в значении true, а в третьем значении указан пользователь или группа, значит, эксплуатация возможна от их УЗ. А если искать ESC4, то смотреть на атрибуты Write Owner Principals, Write Dacl Principals, Write Property Principals и пр.
Выписав себе сертификат при стандартной атаке на ESC8 на этапе аутентификации, мы сталкиваемся с ошибкой.
На официальном сайт Microsoft в документации находим ее описание.
KDC_ERR_PADATA_TYPE_NOSUPP:
Smart card logon is being attempted and the proper certificate cannot be located. This problem can happen because the wrong certification authority (CA) is being queried or the proper CA cannot be contacted in order to get Domain Controller or Domain Controller Authentication certificates for the domain controller.
It can also happen when a domain controller doesn’t have a certificate installed for smart cards (Domain Controller or Domain Controller Authentication templates).
Становится понятно, что PKINIT не поддерживается на контроллере домена. Есть отличная статья, описывающая весь путь от получения ошибки при аутентификации по PKINIT и дальнейшего использования сертификата через Schannel и LDAP через TLS. Как вы уже догадались, речь идет про атаку PassTheCert. Изначально инструмент для ее реализации создали ребята из AlmondOffSec, но Ly4k быстро адаптировал ее функционал под Certipy.
Можно получить ldap-shell от УЗ контроллера домена, поменять самому себе пароль и сделать DCSync. Но давайте сделаем что-то новое и получим ldap-shell от администратора домена, чтобы счейнить ESC1 + ESC8. Это уместно, когда шаблоны с enroll-группой DomainControllers отключены. Для эксплуатации уязвимости ESC1 в этом примере нужна компьютерная УЗ, которая, кстати, у нас уже есть.
Вспоминаем компьютерные УЗ, что стучались в наш Responder. Стандартная эксплуатация ESC8 происходит при форсировании аутентификации машинной УЗ контроллера домена через relay на службу Web Enrollment центра сертификации с указанием нужного нам шаблона. Но ведь мы также знаем, что для эксплуатации ESC1 нужна компьютерная УЗ, знаем название шаблона, знаем, кто находится в группе администраторов домена. Что нам мешает «натравить» любую компьютерную УЗ на Web Enrollment с указанием нужного сертификата и выпуском его с нужным альтернативным именем? Правильно, ничего!
Запускаем ntlmrelayx, следом за ним — Responder, и моментально ловим успешную аутентификацию и выпуск нужного нам сертификата.
Об успешном выпуске сертификата с нужным нам альтернативным именем Certipy любезно подсказывает в конце вывода.
Можно, конечно, получить ldap-shell через Certipy, но давайте в знак уважения к создателю воспользуемся оригинальной питоновской версией passthecert, для которой Ly4k также добавил функционал парсинга сертификата из формата .pfx в .crt и .key. Да и для решения любых задач рекомендуется уметь пользоваться несколькими тулзами, так как любая может не сработать в самый ответственный момент (проверено).
Далее получаем сессию администратора домена и добавляем УЗ, предоставленную нам для работ в привилегированную группу. Ребята со стороны заказчика потрудились и даже написали нам в description ФИО, и, когда мы успешно выполнили команду, мой друг и коллега Вова расплылся в улыбке от вывода на экране.
После этого проекта мне пришла идея о том, что если Web Enrollment включен, когда у нас нет УЗ, то можно брать пользователей или компьютерные УЗ из Responder (IPv6, coerce и др.) и пробовать спреить шаблоны сертификатов прямо через него. Своеобразный TEMPLATE SPRAY. Это уместно, когда получилось собрать информацию об объектах в домене (relay на ldap, например), но получить УЗ не выходит.
Проверим данный вариант на практике на своем стенде. Создадим уязвимый центр сертификации с Web Enrollment и с уязвимым шаблоном к ESC1. Шаблон назовем Netrunner.
Что такое Web Enrollment и какова его роль?
В документации Microsoft написано:
The Certification Authority (CA) Web Enrollment role service provides a set of web pages that allow users to perform certificate tasks. For example, requesting and renewing certificates, retrieving certificate revocations lists (CRLs) and enrolling for smart card certificates.
Все очевидно: запрос и обновление сертификатов, регистрация сертификатов смарт-карт и т.д. Как раз это мы и делаем!
Далее отфильтруем json-файл с шаблонами.
cat 20240830202029_nightcity-corp_templates.json | grep -oP '"name":\s*"\K[^@]*' > templates.txt
Теперь все, что нам осталось — это написать коротенький скрипт на Python и откинуться на спинку кресла, горделиво наблюдая, как будут прилетать сертификаты и добавить логику, которая будет запускать ntlmrelayx.py на 15 секунд с определенным шаблоном. А дальше ждать аутентификацию, сделать попытку на выпуск сертификата, убивать свой процесс, потому что ntlmrelayx.py сам не закрывается, а через короткое время вновь начинать работать, но уже с другим шаблоном и т.д.
Простенький POC тут:
import time
import os
import signal
def run_command(command):
process = subprocess.Popen(command, shell=False)
try:
time.sleep(15)
finally:
try:
os.kill(process.pid, 0)
print(f"Killing process {process.pid}")
os.kill(process.pid, signal.SIGKILL)
except OSError:
print(f"Process {process.pid} does not exist or has already been terminated.")
time.sleep(3)
def main(filename):
with open(filename, 'r') as file:
for line in file:
command = [
"ntlmrelayx.py",
"-t", "http://WEB_ENROLLMENT_IP/certsrv/certfnsh.asp",
"-smb2support", "--adcs", "--template", line.strip(),
"--altname", "administrator"
]
if command:
print(f"Executing command: {' '.join(command)}")
run_command(command)
if __name__ == "__main__":
main('templates.txt')
Эмулируем лобное место пентестеров на выездах к заказчику рабочий бухгалтерский движ в Responder и наблюдаем, как помимо подавляющего большинства ошибок при выпуске сертификатов нам все-таки прилетает несколько удачных выводов с выпуском сертификата, в том числе и с ESC1.
При анализе тех немногих шаблонов, по которым пользователи могут получать в домене сертификаты, было выяснено, что есть дефолтные шаблоны. И использовав их, можно получить сертификат клиенту, если он находится в нужной для них группе. Среди них — User для пользователей и Machine для компьютерных УЗ. Оба представляют для нас большую ценность! Они включены по умолчанию, и на обоих стоит флаг True на Client Authentication. Это значит, что мы сможем пройти аутентификацию, если запросим сертификат по такому шаблону.
Получается, зная, что есть эти шаблоны, нам не нужно спреить и не нужна даже информация о домене, а достаточно просто «привести» нужного клиента на правильный шаблон в Web Enrollment — так мы получим сертификат. Если посмотреть на скрин ниже, где мы берем домен через спрей шаблонов и уязвимость ESC1, то самая важная часть находится сверху. На ней видно, как мы получаем валидную УЗ в домене через шаблон User, на котором нет никаких уязвимостей. По умолчанию ntlmrelayx и certipy используют эти шаблоны для relay-атаки:
adcsoptions.add_argument('--template', action='store', metavar="TEMPLATE", required=False,
help='AD CS template. Defaults to Machine or User whether relayed account name ends with $.
Relaying a DC should require specifying DomainController')
Проверим вариант для компьютерной УЗ на шаблоне Machine и стригерим один из хостов.
Далее мы делаем все то же, что и раньше, только сейчас, как и с шаблоном User, не претендуем на повышенные привилегии.
Все получилось! А валидная компьютерная УЗ — это потенциальный Silver Ticket и т.д. Плюс рассмотренного вектора в том, что он реализуется «черным ящиком» и тебе не нужна никакая информация о домене или сертификатах — достаточно найти сервер AD CS, а следом, если включен, то и Web Enrollment. Это будет полезно, если нет УЗ пользователей или компьютеров, но есть llmnr/nbns/mdns spoofing или другой способ заставить доменную УЗ пройти аутентификацию для атаки NTLM relay. Также можно собрать хосты по 445-му порту и прогнать по ним PetitPotam без аутентификации. Шанс, что получится стригерить хотя бы один хост, по моему опыту, высок.
Есть мнение, что внедрение HTTPS помогает, но я бы не стал относиться к этому как к панацее, а рекомендовал бы сделать Web Enrollment доступным с определенных хостов или для определенной группы пользователей, либо отключить его. Ниже скрин с одного из проектов, доказывающий, что https не всегда является решением.
В данной статье мы выяснили, что Web Enrollment может не только выдавать сертификаты контроллеров домена через стандартную эксплуатацию ESC8, но и чейнить уязвимости и помочь с первоначальным доступом. Надеюсь, ты узнал что-то новое! До новых встреч!
Виктор Зварыкин
Консультант по информационной безопасности «Инфосистемы Джет»
had0uk3n
Крутая статья! Спасибо большое!
Наглядно и понятно, с кучей скринов и хорошим объяснением)