Пошаговый разбор процесса исследования и эксплуатации уязвимости в клиенте WinReg. Раскрываем механизм работы RPC, методы обнаружения уязвимых систем и способы защиты.
Исследователь Akamai Стив Купчик обнаружил новую уязвимость повышения привилегий (Elevation of Privilege, EoP) в клиенте Remote Registry от Microsoft CVE-2024-43532 (оценка CVSS: 8.8). Ошибка эксплуатирует механизм резервного режима в клиенте WinReg, который небезопасно использует устаревшие транспортные протоколы, если SMB недоступен.
Эксплуатируя уязвимость, злоумышленник может передать учетные данные NTLM клиента службе Active Directory Certificate Services (ADCS) и запросить пользовательский сертификат, который затем может использоваться для дальнейшей аутентификации в домене. Akamai представила доказательство концепции в репозитории на GitHub.
Уязвимость была раскрыта Microsoft Security Resource Center в феврале 2024 года и была исправлена в рамках октябрьского Patch Tuesday 2024. Недостаток затрагивает все неисправленные версии Windows.
MS-RPC — это реализация Microsoft протокола удаленного вызова процедур (RPC). RPC — это механизм межпроцессного взаимодействия, позволяющий процессам открывать функциональные возможности, к которым могут обращаться другие процессы. RPC является основным компонентом Windows, и многие службы зависят от него, включая диспетчер служб и функции выключения в wininit.
Akamai провела много исследований MS-RPC — как наступательных, в рамках которых обнаружили крупный вектор атак в форме атак кэширования, так и защитных, в которых анализировали механизмы безопасности.
В данном случае Akamai показывает RPC с другой стороны. Любой протокол, который обеспечивает взаимодействие и выполнение операций между разными компьютерами, должен поддерживать аутентификацию пользователей, и RPC действительно поддерживает передачу учетных данных и аутентификацию в рамках своего процесса привязки. Однако наличие аутентификации также влечет за собой возможность перехвата учетных данных, и специалисты начали искать такие уязвимости.
Сессии RPC обрабатываются через привязки. Клиентское приложение подключается к серверному приложению, связывается с нужным интерфейсом RPC и запрашивает выполнение конкретной функции.
Базовый процесс вызова RPC
Запрос привязки и ответ могут передавать несколько полей данных, необходимых для соединения. Обычно, если аутентификация не требуется, привязка RPC просто используется для выбора синтаксиса передачи, который будет использоваться для инкапсуляции параметров функций в последующих вызовах.
Чего не хватает во взаимодействии? Аутентификации! Как сервер может узнать личность клиента и проверить его право на выполнение запрошенного действия? Ответ таков: он не сможет этого сделать, если клиент не добавит контекст безопасности (аутентификацию) к запросу привязки. По умолчанию все соединения RPC не аутентифицированы, и не все серверы RPC требуют аутентификации.
Чтобы аутентифицироваться (или, как это называется в RPC, «добавить контекст безопасности»), клиент должен добавить дополнительные данные к запросу привязки, согласовать протокол аутентификации (например, NTLM или Kerberos) и вставить дополнительные метаданные, необходимые протоколом аутентификации (например, имя пользователя, домен и т.д.).
Метаданные аутентификации, добавленные в запрос привязки RPC
Первый шаг — понять, как должна выглядеть аутентификация с точки зрения API. Это достигается вызовом одной из функций RpcBindingSetAuthInfo* от клиента после создания дескриптора привязки. В документации по этим функциям можно заметить поле AuthenticationLevel, которое указывает уровень безопасности аутентификации.
Безопасность с аутентификацией предполагает не только проверку существования и авторизации пользователя, но также защиту от подделки. Уровни аутентификации варьируются от простой проверки успешности соединения (RPC_C_AUTHN_LEVEL_CONNECT) до полной шифровки и подписи всего трафика для предотвращения подделок (RPC_C_AUTHN_LEVEL_PKT_PRIVACY). Конечно, злоумышленники интересуются первым уровнем, так как он не защищает трафик от подделок.
Однако всё не так просто. Атаки с ретрансляцией RPC уже давно известны, и многие клиенты и серверы RPC в Windows были обновлены до использования высокого уровня аутентификации, что препятствует успешности ретрансляции. Поэтому необходимо искать более старый код, который по каким-то причинам остаётся небезопасным.
Запуск скрипта IDAPython в папке system32 сервера Windows для поиска случаев небезопасной аутентификации RPC (скрипт можно найти в репозитории Akamai)
Как и предполагалось, список потенциальных целей включал менее 5% от общего числа серверов и клиентов RPC; большинство из них больше не используют небезопасную аутентификацию. Однако в библиотеке advapi32.dll мы нашли интересного кандидата.
advapi32.dll — это основной компонент Windows API, который реализует множество функций, таких как ведение журналов событий, шифрование, WMI и другие.
Мы обнаружили, что функция BaseBindToMachine иногда вызывает RpcBindingSetAuthInfoA с уровнем аутентификации RPC_C_AUTHN_LEVEL_CONNECT, что нам и нужно. BaseBindToMachine вызывается функцией RegConnectRegistryExW, если в качестве имени машины передается путь UNC.
Документация Rust для RegConnectRegistryExW, так как она не документирована в MSDN
Просмотрев BaseBindToMachine, можно заметить, что она использует как RpcBindingSetAuthInfoW (безопасный уровень RPC_C_AUTHN_LEVEL_PKT_PRIVACY), так и RpcBindingSetAuthInfoA (с уровнем RPC_C_AUTHN_LEVEL_CONNECT, который позволяет ретрансляцию, так как не проверяет подлинность и целостность соединения).
Два вызова для установки информации аутентификации
Нам просто нужно понять, почему имеются два конфликтующих вызова. При изучении логики функции мы обнаружили, что в ней используется переменная-указатель на функцию и массив функций. Эти функции задают информацию привязки RPC для использования конкретного транспорта RPC; по умолчанию используется SMB и именованные каналы, но, если это не удаётся, происходит попытка привязки через SPX, TCP/IP и другие.
Почему используются два конфликтующих вызова? При изучении логики функции было обнаружено, что в ней имеется переменная-указатель на функцию и массив функций.
Указатели функций и массив функций внутри BaseBindToMachine
Откат на TCP/IP выглядит многообещающе, так как это позволяет использовать небезопасный метод аутентификации для ретрансляции трафика через атаку посредника, не привлекая внимания клиента. Хотя подобное возможно и с другими транспортными протоколами, они достаточно устарели, и их использование в современной сети может вызвать подозрения. Протокол TCP/IP гораздо чаще применяется в качестве транспорта RPC, и его можно безопасно использовать даже в условиях красной команды.
Важно знать, что как BaseBindToMachine, так и RegConnectRegistryExW принимают в качестве аргумента флаг, предотвращающий резервное переключение, но базовая функция RegConnectRegistryW вызывает RegConnectRegistryExW без этого флага.
Ретрансляция NTLM является распространённой техникой, и основная логика уже реализована в ntlmrelayx от Impacket, который мы будем использовать в основном.
ntlmrelayx не включает сервер RPC для TCP/IP, а только сервер SMB. Поэтому нам потребуется создать собственный сервер ретрансляции, который будет отклонять подключение через именованный канал winreg, чтобы вызвать резервное подключение к TCP/IP.
Для этого необходимо реализовать три ключевых момента:
Сопоставитель конечных точек RPC отвечает за преобразование UUID интерфейсов RPC в соответствующие конечные точки, которые в случае TCP/IP-транспорта представляют собой номера портов. В отличие от SMB, где конечные точки представляют собой именованные каналы с предсказуемыми именами, конечные точки TCP/IP используют временные порты, и поэтому требуется дополнительный слой преобразования.
Ключевой момент связан с NTLM. В процессе привязки RPC с NTLM сначала отправляется сообщение о начале NTLM-переговоров, затем сервер возвращает NTLM-вызов в ответ на запрос привязки. Наконец, клиент отправляет ещё одно сообщение, называемое AUTH3, с ответом на вызов.
Аутентификация NTLM через привязку RPC
Чтобы выполнить ретрансляцию, достаточно перехватить соответствующие сообщения на нашем RPC-сервере. Как только мы видим сообщение о согласовании NTLM в сообщении привязки, мы открываем собственное подключение к серверу аутентификации и также запрашиваем аутентификацию через NTLM. Затем мы просто захватываем вызов, отправленный сервером, передаём его жертве и обратно на сервер, чтобы получить нашу собственную аутентифицированную сессию.
Первой мыслью было бы передать данные другому RPC-серверу на другой машине, например, диспетчеру служб или планировщику задач, чтобы выполнить удалённый код. Однако обе эти службы требуют уровня аутентификации RPC_C_AUTHN_LEVEL_PKT_PRIVACY, который шифрует весь трафик с использованием NTLMv2-хеша клиента, к которому у нас нет доступа даже с ретрансляцией.
Ретрансляция NTLM на ADCS реализована в Impacket по умолчанию, поэтому нам остаётся лишь передать нашу аутентифицированную сессию модулю HTTPAttack и позволить процессу автоматизированно завершить атаку.
HTTP-сервер ADCS не требует особой безопасности и уязвим к ретрансляционным атакам. Используя это, после аутентификации мы можем запросить пользовательский сертификат, который можно затем использовать для аутентификации без повторной ретрансляции.
Запрос и получение сертификата от ADCS после атаки NTLM relay attack
С помощью этого сертификата мы аутентифицировались на службе LDAP на контроллере домена и создали постоянную учётную запись администратора домена в скомпрометированном домене.
Создание нового администратора домена с использованием оболочки LDAP
Функция в advapi сама по себе не представляет интереса, если только её не использует что-то ещё. Быстрый поиск по импортам RegConnectRegistryExW или RegConnectRegistryExA ничего не показал на актуальном контроллере домена, однако поиск RegConnectRegistryW выявил несколько возможных кандидатов, таких как certutil и certsrv (AD CS), EFS, DFS и другие.
Служба Remote Registry по умолчанию не включена на всех устройствах Windows. Статус её активности можно определить с помощью следующего запроса osquery:
SELECT display_name, status, start_type, pid FROM services WHERE name='RemoteRegistry'
Однако это не защищает от CVE-2024-43532, так как это проблема клиента. Результаты запроса помогут выявить реальные случаи использования Remote Registry в организации, которые могут понадобиться учесть при усилении защиты.
Для обнаружения клиентов, использующих уязвимые функции WinAPI, можно применить следующее правило YARA:
import "pe" rule winreg_client_import { meta: description = "Detect binaries that rely on RegConnectRegistry" author = "Стив Купчик из Akamai Technologies" condition: pe.is_pe and ( pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryA") or pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryW") or pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryExA") or pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryExW") ) }
Пользователи Akamai Guardicore Segmentation могут также создавать правила политики для трафика, направленного к службе RemoteRegistry.
Правило политики сегментации для оповещения о трафике в службу RemoteRegistry
Также можно использовать Event Tracing for Windows (ETW) для мониторинга трафика RPC на стороне клиента и сервера.
Хотя протокол RPC и MS-RPC разрабатывались с учетом безопасности, анализ различных реализаций интерфейсов RPC демонстрирует эволюцию принципов безопасности с течением времени. Несмотря на то, что большинство серверов и клиентов RPC сейчас защищены, время от времени всё же можно обнаружить реликты небезопасной реализации в той или иной степени.
В данном случае нам удалось выполнить атаку ретрансляции NTLM, что является классом атак, которые должны были бы остаться в прошлом. Это показывает, что защита сети должна быть как можно более всесторонней, так как невозможно предсказать, какой древний интерфейс ещё может быть открыт или использоваться.
Одно найти легче, чем другое. Спойлер: это не темная материя