Pull to refresh

Comments 13

Статья хорошая, спасибо.

Вообще, в модели безопасности Windows имеется довольно много боковых веток и обходных путей. Но, справедливости ради, нужно отметить, что Microsoft предпринимает серьёзные усилия по их ограничению.
Например, если раньше можно было свободно вызывать функцию SetWindowsHookEx, то теперь в Windows 10 нужно не только для этого запуститься от лица администратора, но и обладать подписанным сертификатом в составе приложения.
Вряд ли это является проблемой, потому что Windows не позволяет открыть системные процессы или процессы других пользователей с доступом PROCESS_DUP_HANDLE.

Единственные, кому нужно помнить об этой особенности — разработчикам ядра, которые своим кодом создают новые процессы и назначают им ACL.

Более привелегированный процесс может дублировать описатель на себя (или другой привелегированный процесс) в процесс с урезанными правами, как результат какого-нибудь IPC, например.

Сложно представить такой сценарий. Привилегированный процесс даёт свой хендл для чего?

Произвольное чтение/запись адресного пространства, создание потоков — уже нарушает безопасность.

Разрешение чтения статистики — так можно отдать статистику вместо хендла.

Назначение квот — это не должен делать менее привилегированный.

Убиение процесса по запросу другого процесса — допускаю, но это плохой сценарий, т.к. у останавливаемого процесса нет шансов выполнить подчистку (удалить временные файлы, например). В этом случае лучше дать ссылку на Event, который сигналить снаружи.
Сложно представить такой сценарий. Привилегированный процесс даёт свой хендл для чего?

В этом случае лучше дать ссылку на Event, который сигналить снаружи.

Сложно, но, к сожалению, не невозможно. Никто не утверждал, что это best practices, но я неоднократно встречал решения, которые специально обходят механизмы безопасности Windows, например инсталляцией драйвера, что бы открывать описатели объектов с ExGetPreviousMode() == KernelMode и возвращать их в User Mode процесс.


Пример навскидку: есть процесс, сканирующий (а возможно и закрывающий, например для безопасного извлечения флешки) открытые описатели системы. Для детализированной информации нужно получить описатель этого самого объекта. Как его можно получить, учитывая что есть процессы в соседних сессиях и более привилегированные процессы? Более привилегированным кодом, например драйвером. Но авторам кода можно не дергать драйвер на каждый описатель целевого процесса, достаточно из драйвера вернуть описатель на этот процесс с правом PROCESS_DUP_HANDLE. А затем уже процесс собственноручно сможет дублировать интересующие описатели из целевого процесса себе.


Если рассматривать такой дырявый драйвер (а с учетом политики подписей, таскать с собой эксплуатируемый чужой подписанный драйвер уже давно не ново для вредоносного ПО), то может показаться, что поверхность атаки не велика: можно попробовать вызвать падение более привилегированного процесса, просто позакрывав ему описатели (DUPLICATE_CLOSE_SOURCE). Но, учитывая легкость и стабильность получения PROCESS_ALL_ACCESS можно запустить произвольный код в более привилегированном процессе, что (IMHO) много более опаснее.


Менее детализированный пример навскидку #2: процесс редактирует описатель безопасности (ACL'и) у некоторого объекта процесса. С учетом представленной информации нужно осознавать, что разрешение маски доступа PROCESS_DUP_HANDLE дает полный доступ к процессу, что не очевидно.

Спасибо, как раз то, что искал!
Вот вам ещё пример. Есть некое приложение, которое должно запускаться от имени администратора по своей природе. Это приложение торчит наружу COM-интерфейсом, т. е. по идее для управления им права администратора не требуются. Однако, для удобства использования и для того, чтобы иметь, например, возможность завершить процесс или сделать с ним ещё что-то полезное, нам нужно иметь его handle.
Таким образом, у нас есть служба, которая запускает процесс (от имени администратора) и дублирует его хэндл через WCF в клиентское приложение, которое уже делает с ним всё, что необходимо, не требуя при этом повышения прав.
Что можете сказать про такой расклад?

С WCF не знаком, поэтому тонкостей не знаю.
Но вопрос в том, с какими правами (access mask) получает описатель клиентское приложение.

Пока что открываю процесс со всеми возможными правами (ProcessAccessFlags.All) и использую DuplicateOptions.DuplicateSameAccess — надо полагать access mask будет выглядеть соответствующим образом (что-то вроде 0x001F0FFF). Чем это может быть чревато?

Локальное повышение привилегий (Local Privilege Escalation), включая возможный выход из песочницы (из песочницы того же хрома, например). Как пример — шифровальщики: получая повышение привилегий могут добраться до данных всех пользователей текущей машины (а не только текущего пользователя). А вообще любая малварь может таскать с собой уязвимые бинари (особенно так любят делать, если уязвимый продукт имеет цифровую подпись), что бы обходить разные защиты (или даже атаковать-отключать работающие защиты). Для производителя продукта это, помимо репутационных потерь, черевато детектами со стороны антивирусов.

Хм, спасибо, значит буду урезать права.
Можете в двух словах описать механизм повышения привилегий, учитывая что:
1. Запускаемый бинарник лежит в Program Files — т. е. предполагается что с обычными правами его просто так не подменишь
2. Хэндл дублируется для конкретного процесса — т. е. за его пределами не юзабелен.

Так сказать, врага нужно знать в лицо.

Вредоносный процесс запускается под непривилегированным пользователем в той же сессии, что и процесс с целевым описателем. Открывает процесс с целевым описателем (на привилегированный процесс) для дублирования описателей из него к себе. Перебором дублирует описатели, получая описатель на привилегированный процесс. А дальше — миграция кода, например:


  • пишем вредоносный код в виде DLL (полезная нагрузка в DllMain) во временную директорию
  • записываем полый путь к DLL в привилегированный процесс (например — WriteProcessMemory)
  • создаем новую нить (CreateRemoteThread) в привилегированном процессе, точкой входа указывая LoadLibrary, а аргументом для LoadLibrary — записанный полный путь к вредоносной DLL

В результате в привилегированном процессе появляется новая нить, которая выполнит DllMain вредоносной DLL.


Это простейший сценарий, который имеет огромное количество вариаций, из того что прямо сейчас пришло в голову:


  • WriteProcessMemory может быть заменен на Atom Bombing, Windows Notification Facility (с которым будет взаимодействовать не ваш непосредственный кода, в какой-нибудь kernel32.dll) и т.п.
  • CreateRemoteThread может быть заменен на APC (Asynchronous Procedure Calls), изменение контекста существующей нити.

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

Понял, большое спасибо за такое подробное описание!
Sign up to leave a comment.

Articles