запуск процесса с повышенными правами UAC из неинтерактивной службы (win32 / .net / powershell) - PullRequest
16 голосов
/ 11 марта 2010

Я использую стороннюю службу Windows, которая выполняет некоторые задачи автоматизации, выполняя скрипты и исполняемые файлы с помощью CreateProcessAsUser (). Я сталкиваюсь с проблемами в Windows Server 2008 из-за UAC и способа обработки повышения LUA через API.

Служба работает как LocalSystem и не имеет включенного «Взаимодействовать с рабочим столом». Процессы выполняются как пользователи в группе администраторов, но не в качестве учетной записи администратора (которая освобождена от многих ограничений UAC). Все настройки UAC по умолчанию на месте.

Я могу передавать в службу произвольные команды или код powershell, но я не могу «вырваться» из не повышенного уровня неинтерактивного процесса, который запускается службой.

Суть проблемы заключается в том, что единственной (общедоступной) опцией API для запуска процесса с повышенными правами является ShellExecute () с глаголом runas, но, насколько я могу судить, его нельзя вызвать из неинтерактивный сервис или вы получаете ошибки типа «Для этой операции требуется интерактивная оконная станция».

Единственный найденный мной обходной путь упомянут здесь: http://www.eggheadcafe.com/software/aspnet/29620442/how-to-proper-use-sendinp.aspx

В Vista официальным документированным способом чтобы поднять процесс только с помощью API оболочки ShellExecute (Ex) (не CreateProcess или CreateProcessAsUser). Так что ваше приложение должно позвонить ShellExecute (Ex) для запуска помощника повышен для вызова SendInput. Кроме того, из-за сессии 0 изоляция, сервис может использовать только CreateProcessAsUser или CreateProcessWithLogonW (не может использовать ShellExecute (Ex)), чтобы указать интерактивный рабочий стол.

.. Я думаю, что нет прямого пути к породить повышенный процесс от служба windows. Мы можем только сначала использовать CreateProcessAsUser или CreateProcessWithLogonW, чтобы порождать не повышенный процесс в пользователя сессия (интерактивный рабочий стол). Затем в не повышенный процесс, он может использовать ShellExecute (Ex) для создания повышенных процесс для реальной задачи.

Чтобы сделать это из кода .net / powershell, похоже, мне нужно было бы сделать некоторые сложные вещи P / Invoke для вызова CreateProcessAsUser или CreateProcessWithLogonW, поскольку .Net System.Diagnostics.ProcessStartInfo не имеет эквивалента lpDesktop что я мог бы установить на "winsta0 \ default". И мне не ясно, есть ли у LocalSystem даже права вызывать CreateProcessAsUser или CreateProcessWithLogonW.

Я тоже смотрел на http://blogs.msdn.com/alejacma/archive/2007/12/20/how-to-call-createprocesswithlogonw-createprocessasuser-in-net.aspx а также Process.Start с различными учетными данными с UAC на

Исходя из всего этого, я прихожу к выводу, что не существует простого способа сделать это. Я что-то пропустил? Это действительно не похоже, что это должно быть так сложно. Такое ощущение, что UAC просто никогда не был разработан для обработки неинтерактивных сценариев использования.

И если кто-нибудь из Microsoft в конце концов прочитает это, я заметил, что ShellExecute внутренне обрабатывает повышение, вызывая Информационную службу приложений (AIS). Почему тот же вызов AIS не доступен через Win32 или .NET API? http://msdn.microsoft.com/en-us/library/bb756945.aspx

Извините, что побежал немного долго. Спасибо за любые идеи.

Ответы [ 2 ]

17 голосов
/ 04 сентября 2010

«Официальный» способ разорвать нулевую изоляцию сеанса - использовать комбинацию API служб терминалов и CreateProcessAsUser() для запуска процесса в сеансе пользователя. На моей старой работе мы сделали именно это, поскольку нам нужно было отобразить диалоговое окно для пользователя из службы перед установкой загруженного обновления. Итак, я знаю, что это работает, по крайней мере, на WinXP, Win2K3, Vista и Win7, но я не ожидайте, что Win 2K8 будет слишком другим. В основном, процесс идет следующим образом:

  1. Вызовите WTSGetActiveConsoleSessionId(), чтобы получить идентификатор активного сеанса консоли (ОЧЕНЬ важно, поскольку интерактивный сеанс НЕ всегда сеанс 1, даже в клиентских системах). Этот API также возвратит -1 , если в интерактивном сеансе нет активных пользователей (то есть локально вошедших в систему на физическом компьютере, в отличие от использования RDP).
  2. Передайте идентификатор сеанса из предыдущего вызова API на WTSQueryUserToken(), чтобы получить открытый токен, представляющий пользователя, вошедшего в консоль.
  3. Позвоните DuplicateTokenEx(), чтобы преобразовать токен олицетворения (из WTSQueryUserToken) в основной токен.
  4. Позвоните CreateEnvironmentBlock(), чтобы создать новую среду для процесса (необязательно, но если у вас ее нет, у процесса ее не будет).
  5. Передайте основной токен из шага # 3 в вызов CreateProccessAsUser() вместе с командной строкой для исполняемого файла. Если вы создали блок среды из шага # 4, вы также должны пройти флаг CREATE_UNICODE_ENVIRONMENT (всегда). Это может показаться глупым, но API ужасно терпит неудачу, если вы этого не сделаете (с ERROR_INVALID_PARAMTER).
  6. Если вы создали блок среды, вам нужно вызвать DestroyEnvironmentBlock, иначе вы вызовете утечку памяти. При запуске процессу предоставляется отдельная копия блока среды, поэтому вы уничтожаете только локальные данные.

И вуаля! Windows делает некоторую внутреннюю магию, и вы видите запуск приложения. Однако, хотя это и запустит интерактивный процесс из службы, я не уверен, что это обойдет UAC (но не цитируйте меня по этому поводу). Другими словами, он может не запускаться как процесс с повышенными правами, если в реестре или во внутреннем манифесте не указано иное, и даже в этом случае вы все равно можете получить приглашение UAC. Если токен, полученный на шаге 3, является токеном с ограниченным доступом, вы можете использовать AdjustTokenPrivileges() для восстановления повышенного (полного) токена, но также не указывайте мне это. Однако, как указано в документации MSDN, обратите внимание, что невозможно «добавить» привилегии для токена, у которого их еще нет (например, вы не можете превратить токен ограниченного пользователя в администратора с помощью AdjustTokenPrivileges; основной пользователь должен быть администратором для начала).

Технически возможно сделать все это из Win2K вперед. Однако, это действительно возможно только начиная с WinXP, так как в Win2K отсутствуют API WTSGetActiveConsoleSessionId() и WTSQueryUserToken() (наряду с WTSEnumerateProcesses() для Win2K Pro). Вы можете жестко закодировать 0 в качестве идентификатора сеанса (так как это всегда имеет место в Win2K), и я полагаю, что вы могли бы получить токен пользователя, перечислив запущенные процессы и продублировав один из их токенов (это должен быть тот, который имеет интерактивный SID присутствует). В любом случае, CreateProcessAsUser() будет вести себя одинаково при передаче интерактивного пользовательского токена, даже если вы не выберете «Взаимодействовать с рабочим столом» в настройках службы. Это также более безопасно, чем запуск непосредственно из службы, так как процесс не унаследует божественный LocalSystem токен доступа.

Теперь, я не знаю, делает ли ваше стороннее приложение что-либо из этого, когда запускает скрипт / процесс, но если вы хотите сделать это из службы, вот как (а в Vista или Win7 это единственный способ преодолеть изоляцию сеанса 0).

1 голос
/ 03 июля 2013

В зависимости от вашего варианта использования вы можете делать то, что я делаю. Я выслеживаю процесс winlogon для активной сессии и краду его токен. Если нет активного сеанса (API вернул -1), используйте 1, если WINVER> = 6, иначе 0. Это приводит к SYSTEM в активном сеансе.

...