Comments 13
Вообще, в модели безопасности Windows имеется довольно много боковых веток и обходных путей. Но, справедливости ради, нужно отметить, что Microsoft предпринимает серьёзные усилия по их ограничению.
Например, если раньше можно было свободно вызывать функцию SetWindowsHookEx, то теперь в Windows 10 нужно не только для этого запуститься от лица администратора, но и обладать подписанным сертификатом в составе приложения.
Единственные, кому нужно помнить об этой особенности — разработчикам ядра, которые своим кодом создают новые процессы и назначают им 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) получает описатель клиентское приложение.
Локальное повышение привилегий (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), изменение контекста существующей нити.
А завтра появятся новые трюки миграции кода между процессами. Поэтому стоит давать минимально необходимый набор разрешений.
Как PROCESS_DUP_HANDLE превращается в PROCESS_ALL_ACCESS