Pull to refresh

И ещё один Steam Windows Client Local Privilege Escalation 0day

Reading time 7 min
Views 21K

В предыдущей серии


Не так давно я опубликовал описание уязвимости для Steam. Я получил много отзывов от читателей. Valve не проронили ни слова, а HackerOne прислал огромное слезливое письмо и, в основном, молчал. В итоге меня забанили Valve на H1 — я не могу участвовать в их программе по отклонению уязвимостей (остальной H1 мне доступен).



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

А оно простое и грустное — Valve все так же терпят фиаско. Последнее обновление, которое было призвано устранить проблему, легко обходится и уязвимость все еще актуальна. Да, я это проверил — прекрасно работает.

Но эта статья не о том, что старая уязвимость все еще присутствует, а о новой. Поскольку Valve еще раз изъявили желание прочитать публичный отчет, вместо частного, не будем лишать их этого удовольствия.

Краткое описание уязвимости


Общее описание эксплуатации уязвимости довольно несложно и состоит из трех шагов:

  1. Подготавливаем окружение для эксплуатации (целых два способа на выбор, используя разные недочеты безопасности).
  2. Заставляем Стим скопировать и запустить нашу dll.
  3. Dll должна соответствовать небольшим требованиям.

Все эти действия может выполнить любой пользователь ОС, а еще точнее — любая программа на компьютере. В результате можно выполнить любой код с максимальными привилегиями, такой класс уязвимостей называется escalation of privileges (eop) или local privilege escalation (lpe). Несмотря на то, что любое приложение может само по себе нанести какой-то вред, получение максимальных прав приведет к гораздо более существенным последствиям. Выключение антивируса и файрвола, установка руткита, скрытие процесса-майнера, кража личных данных всех пользователей ПК — это только малая часть того, что можно придумать.

Теоретический минимум


Было очень забавно наблюдать комментарии к предыдущей статье, где люди писали «В HKLM ключи реестра пользователь писать не может» или «Чтобы создать симлинк нужны права администратора». Интересно, что проверка этих утверждений займёт едва ли дольше, чем написание такого комментария. И, да, на всякий случай: оба утверждения ложны. Поэтому в этой статье я решил сделать небольшой раздел, где описал ряд сложных моментов из эксплуатации.

«В раздел реестра HKLM нельзя писать пользователю»


Нет такого общего правила. Есть конкретные правила безопасности, для конкретных ключей реестра. Valve выставила права полного доступа для всех пользователей на ветку HKLM\SOFTWARE\Wow6432Node\Valve\steam, и поэтому в данной ветке любой пользователь может делать, что хочет.

«Нельзя запустить или остановить сервис без прав администратора»


Нет такого общего правила. Есть конкретные правила безопасности для конкретных сервисов. Valve выставила права так, что сервис Steam Client Service может быть запущен и остановлен любым пользователем.

«Для создания симлинка нужны права администратора»


Это сам по себе забавный вопрос, учитывая, что из 5 основных видов линков в Windows только полтора требуют эти права. Итак, встречайте: file symbolic link, object directory symbolic link, hard link, NTFS reparse point и reg_link. Права администратора нужны только для создания file symbolic link и для постоянного object directory symbolic link (временный живет ровно столько, сколько живет сессия, в которой он создан, в общем смысле до перезагрузки, и специальных прав не требует).

Симлинк с папки на папку


Это называется NTFS reparse point или NTFS mount point. Название особо не важно, суть в том, что эта штука позволяет использовать одну папку как указатель на другую. Может создаваться обычным пользователем из пустой папки, если у него есть права Write для нее. Для создания будем пользоваться утилитой CreateMountPoint.exe из набора утилит для тестирования работы с линками.

Уступающая блокировка


Уступающая блокировка (OpLock или Opportunistic Lock) — это специальный механизм, при котором одно приложение может временно заблокировать доступ всем к некоторому файловому ресурсу. Тут много всяких деталей можно написать, особенностей работы с папками и разными доступами. Суть вкратце: программа может «поймать» событие обращения к некоторому файлу и задержать его на время. Устанавливать оплоки можно утилитой SetOpLock.exe из того же набора тестирования работы с линками. Запуск утилиты устанавливает требуемый оплок; когда происходит доступ, то утилита пишет сообщение; нажатие enter снимает оплок.

BaitAndSwitch


Это название приема, который комбинирует создание линков и установку оплоков, чтобы выиграть TOCTOU (time of check\time of use). Суть проще объяснить на примере.

Представьте, что есть некоторая программа, которая подряд делает что-то типа такого:

ReadContentFromFile(“C:\test\myfile.txt”);
ReadContentFromFile(“C:\test\myfile.txt”);

Это просто чтение одного и того же файла два раза подряд. Всегда ли будет прочитано одно и то же? Нет, не обязательно.

Сначала создадим две папки с файлами C:\test1\myfile.txt и C:\test2\myfile.txt. А папку C:\test вообще очистим и создадим reparse point на C:\test1. Поставим оплок на файл из первой директории и запускаем программу. Как только она откроет файл, сработает оплок. Мы поменяем reparse point и C:\test будет указывать на C:\test2. Теперь, после того, как оплок будет снят, программа прочитает файл во второй раз уже из другого файла.

Зачем это нужно? Очень просто — довольно типичная ситуация, где файл сначала проверяют (первое чтение), а потом уже запускают (второе чтение). Вот так мы на проверку отправим один файл, а на исполнение — другой.

Теперь все готово к эксплуатации.

Эксплуатация 1. Подготовка окружения


Надо немного подготовить рабочее окружение. Начнем с того, что необходимо взять исполняемые файлы CreateMountPoint.exe и SetOpLock.exe.

Теперь надо провести небольшие изменения в файловой структуре Стима. Наша задача — получить папку с двумя файлами Steam.exe и steamclient.dll и обязательным отсутствием папки bin. Это можно сделать двумя способами.

Способ 1


Переименовать\удалить папку bin из основной папки Steam. Все, вы великолепны (Стим при установке дает любому пользователю права на все в его папке).

Способ 2


В ключе реестра HKLM\SOFTWARE\Wow6432Node\Valve\steam изменить параметр InstallPath на какую-нибудь нашу папку. В эту папку закинуть Steam.exe и steamclient.dll из основной папки Стима.

Пусть любым из способов мы подготовили папку C:\Steam (путь может быть любой, но в примерах я буду использовать этот). Теперь создадим в ней еще папки b1, b2, b3 и b4. В первые три закинем файл steamservice.dll (из комплекта Стима, в оригинале он лежал в папке bin), а в папку b4 закинем специально сформированную библиотеку с тем же именем — steamservice.dll. Подробно о подготовке библиотеки будет в 3 пункте.

Открываем два окошка консоли. На этом подготовка окружения завершена.

Эксплуатация 2. Подменяем файл


Я думаю, что из приготовлений уже стало понятно, что будет что-то типа описанного выше BaitAndSwitch.

Скриншот из ProcMon:



Это часть лога типичного старта сервиса Steam Client Service. Обратите внимание на часть, где dll сначала копируется в C:\Program Files (x86)\Common Files\Steam, а затем загружается. Мы сделаем так, чтобы скопировалась наша библиотека из C:\Steam\b4. К сожалению, сначала идут проверки, в том числе проверяется подпись библиотеки, чтобы ее нельзя было подменить (о, ирония).

Итак, распишу по шагам. Шаги объединены в группы из однотипных действий. Для каждого шага будет указано, где что запускать и что происходит (разные окошки консоли я назвал cmd1 и cmd2).

  1. Создаем папку C:\Steam\bin и в cmd1 выполняем:
    CreateMountPoint.exe С:\Steam\bin C:\Steam\b1
  2. В cmd1 ставим оплок:
    SetOpLock.exe C:\Steam\b1\steamservice.dll
  3. Запускаем сервис Steam Client Service, видим в cmd1, что поймали обращение к файлу.

    ***
  4. Удаляем C:\Steam\bin, создаем на его месте папку C:\Steam\bin и в cmd2 выполняем:
    CreateMountPoint.exe С:\Steam\bin C:\Steam\b2
  5. В cmd2 ставим оплок:
    SetOpLock.exe C:\Steam\b2\steamservice.dll
  6. В cmd1 отпускаем оплок, видим, что в cmd2 поймали обращение к файлу.

    ***
  7. Удаляем C:\Steam\bin, создаем на его месте папку C:\Steam\bin и cmd1 выполняем:
    CreateMountPoint.exe С:\Steam\bin C:\Steam\b3
  8. В cmd1 ставим оплок:
    SetOpLock.exe C:\Steam\b3\steamservice.dll
  9. В cmd2 отпускаем оплок, видим, что в cmd1 поймали обращение к файлу.

    ***
  10. Удаляем C:\Steam\bin, создаем на его месте папку C:\Steam\bin и cmd2 выполняем:
    CreateMountPoint.exe С:\Steam\bin C:\Steam\b2
  11. В cmd2 ставим оплок:
    SetOpLock.exe C:\Steam\b2\steamservice.dll
  12. В cmd1 отпускаем оплок, видим, что в cmd2 поймали обращение к файлу.

    ***
  13. Удаляем C:\Steam\bin, создаем на его месте папку C:\Steam\bin и cmd1 выполняем:
    CreateMountPoint.exe С:\Steam\bin C:\Steam\b3
  14. В cmd1 ставим оплок:
    SetOpLock.exe C:\Steam\b3\steamservice.dll
  15. В cmd2 отпускаем оплок, видим, что в cmd1 поймали обращение к файлу.

    ***
  16. Удаляем C:\Steam\bin, создаем на его месте папку C:\Steam\bin и cmd2 выполняем:
    CreateMountPoint.exe С:\Steam\bin C:\Steam\b4
  17. В cmd1 отпускаем оплок

Хоть и выглядит сложно, на самом деле идея простая: из 6 обращений к файлу C:\Steam\bin\steamservice.dll первые 5 раз были отданы файлы-оригиналы из разных папок (в порядке обращения: b1, b2, b3, b2, b3), а в шестой раз для копирования был отдан файл с полезной нагрузкой.

Схематично я изобразил это так:


Слева — нормальное поведение, справа — поведение с эксплоитом.

Эксплуатация 3. Внедряемая библиотека


Для полезной нагрузки я сначала воспользовался самой типовой своей dll, которая в DllEntry создает интерактивную консоль. Поскольку код из dll будет выполнен в контексте Steam Client Service, он будет выполнен с теми же правами, что и сам сервис – NT AUTHORITY\SYSTEM. Но в результате эксплуатации консоль не появилась.

После загрузки сервис Стима все же понимает, что ему подсунули липу, и завершает работу, поэтому полезная нагрузка из моей dll не успела выполниться.

Пришлось немного пореверсить, и оказалось, что сервис после загрузки dll проверяет существование функций

int WINAPI SteamService_RunMainLoop()
void WINAPI SteamService_Stop()

в библиотеке. Более того, сервис вызывает первую функцию, где я и решил поместить полезную нагрузку (запуск интерактивной консоли с правами сервиса – NT AUTHORITY\SYSTEM). Вот теперь совсем все — повторяем все действия и получаем консоль с максимальными правами.

Заключение


Можно все это завернуть в exe-файл, но, честно говоря, мне не очень хочется заморачиваться. Я думаю, что видео с демонстрацией будет достаточно (вариант с реестром, вариант с файловой системой).
Я не буду копировать сюда раздел «Спекуляции» из прошлой статьи. Просто факты: старая уязвимость актуальная, о новой вы только что прочитали, Valve все так же не хотят слышать о проблемах.

Update (22.08.2019)


На данный момент есть две новости:

  1. Клиент беты получил обновление с исправлениями. Смотреть буду когда обновление дойдет до основного клиента.
  2. Valve поменяло политику в отношении LPE. И это отличная новость!


Update (27.08.2019)


Хорошие новости.

  1. Основной клиент получил обновление с исправлениями.
  2. Меня разбанили на H1 и выплатили награду


This article in english.
Tags:
Hubs:
+82
Comments 92
Comments Comments 92

Articles

Information

Website
amonitoring.ru
Registered
Founded
Employees
101–200 employees
Location
Россия