В прошлый раз я показал, как выполнять команды и скрипты от имени системы при наличии прав администратора. Сегодня я объясню, как запускать административные задачи, не имея полных прав.
Это решение родилось у меня экспромтом в ответ на вопрос в чате инсайдеров. Я подкинул идею, а участник чата легко реализовал ее самостоятельно. Сегодня я подробно разберу подход и решение. И нет, оно не про утилиту AdmLink или runas /savecred
.
[+] Сегодня в программе
Зачем такое может понадобиться
Иногда в контролируемой среде нужно предоставить возможность обычным пользователям запускать что-то с правами администратора или от имени системы. Например, без полных прав невозможно управление состоянием устройств.
Возьмем реальный пример участника чата. Он единственный администратор на семейном ноутбуке с сенсорным экраном, а остальные члены семьи работают с обычными правами. Нужно дать им возможность включать и отключать сенсорный экран, потому что кого-то путают случайные касания дисплея.
С тем же успехом это могла быть камера ноутбука и разный уровень «паранойи» у членов семьи. Также, потребность в таком решении может возникнуть, если сбой устройства (например, сетевой карты или мыши) лечится его перезапуском, а перезагружать систему для этого нецелесообразно.
Предупредительные выстрелы
Когда вы даете пользователю возможность запускать задачи с полными правами, есть вероятность отдать ему контроль над системой вне зависимости от предпринятых мер предосторожности. Мне неизвестны уязвимости моего решения, но это не значит, что их нет. Вы действуете на свой страх и риск.
- Обычно, при постановке такой задачи сразу вспоминают AdmiLink и команду
runas /savecred
. AdmiLink – стороннее решение, а мой подход использует только встроенные возможности Windows. В свою очередь runas дает пользователю возможность запускать с сохраненными учетными данными любую задачу, нежели только предусмотренную администратором. - Мое решение задействует встроенные возможности Windows, но не является эксплуатацией уязвимостей. В качестве администратора вы своими руками создаете все необходимые условия для запуска, поэтому надо подстраховаться↓
- Я категорически не рекомендую этот подход для запуска интерактивных приложений, хотя в рамках инструкций статьи он и не должен работать. Как правило, у приложений есть диалог Открыть / Сохранить как, откуда легко запускается командная строка с правами администратора или системы, а дальше все что угодно. Запуск скрипта несет меньше риска.
- Скрипт для выполнения административной задачи должен находиться в расположении, на которое у пользователя нет прав на запись. Это может быть профиль администратора, системная папка или корень диска. В принципе, годится любое расположение при условии, что для скрипта установлен высокий уровень целостности.
- У пользователя не должно быть возможности изменять автономную систему. Это решается шифрованием диска, паролем на BIOS, запретом загрузки с внешних носителей, опечатыванием корпуса ПК и т.д.
Алгоритм решения
Задача решается в два хода с помощью планировщика заданий.
- Администратор создает в планировщике задание, которое запускается с полными правами при появлении в журнале заданного события и выполняет команду или скрипт.
- Пользователь выполняет команду, создающую событие в журнале. Для этого не нужны полные права.
В результате задание срабатывает по событию. Все счастливы!
Реализация решения
Плясать надо от события, с него и начну.
Шаг 1 — Создание события в журнале
Поскольку действие пользовательское, нужна максимально простая реализация.
Я выбираю ярлык на запуск команды – его можно запускать мышью и сочетанием клавиш.
Создать событие можно командлетом PowerShell Write-Eventlog, но проще задействовать встроенную утилиту eventcreate. Она создает в указанном журнале событие с нужным номером, источником и описанием.
eventcreate /L Application /T Information /ID 777 /SO outsidethebox /D "Think outside the box!"
Если источник новый, как в этом примере, сначала зарегистрируйте его в системе, однократно выполнив команду от имени администратора. Для пользователя создайте на рабочем столе новый ярлык и вставьте туда команду. Она создает в журнале «Приложения» информационное событие 777
от источника outsidethebox
с описанием Think outside the box!
Шаг 2 — Создание задания в планировщике
Задание, выполняющееся по событию, легко создается в графическом интерфейсе планировщика (примеры создания из консоли ниже). Поэтому здесь я сфокусируюсь на нюансах, относящихся к моему решению.
Команда в задании
Я не могу знать, какую команду вы захотите выполнять по событию. Допустим, из корня системного диска выполняется скрипт PowerShell RunAsAdmin.ps1
(изменять его смогут только пользователи с полными правами).
В PowerShell от имени администратора выполните:
$script = "C:\RunAsAdmin.ps1" New-Item -ItemType File -Path $script #выполнить если файл не в корне системного диска #icacls $script /setintegritylevel high #https://www.outsidethebox.ms/14318/#_Toc343199595
В запланированном задании на вкладке Действия укажите такие параметры:
- Команда/скрипт:
powershell
- Аргументы:
-ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -file C:\RunAsAdmin.ps1
Учетная запись для запуска задания
В зависимости от ваших целей, вам нужно настроить запуск задания от имени администратора или системной учетной записи.
- На вкладке Общие установите переключатель Выполнять для всех пользователей.
- Для запуска от имени:
- администратора установите флажок Выполнять с наивысшими правами, затем нажмите ОК и введите пароль своего аккаунта.
- системы нажмите Изменить и введите SYSTEM или СИСТЕМА в зависимости от локализации, затем дважды нажмите OK.
Примеры скриптов PowerShell
Конечно, скрипт может выполнять по событию что угодно. Но поскольку статья в контексте устройств, я приведу конкретные примеры, чтобы придать решению законченный вид.
Управление состоянием устройства
Я уже показывал работу с модулем PNPDevice. Здесь я для разнообразия разберу отключение и включение камеры.
Выполнив Get-PNPDevice в PowerShell от имени администратора, вы увидите список всех устройств. Ориентируясь на их классы и имена, вы легко можете вывести ИД устройства и его статус.
Get-PnpDevice | Where-Object Class -eq Camera | Format-List FriendlyName,InstanceId,Status FriendlyName : Integrated Camera InstanceId : USB\VID_5986&PID_02D2&MI_00\7&153E4771&0&0000 Status : OK
У отключенного устройства будет статус Error
. Теперь можно менять состояние устройства в зависимости от текущего статуса с помощью командлетов Enable-PNPDevice и Disable-PNPDevice. Если статус OK – выключаем, иначе – включаем.
$id = "USB\VID_5986&PID_02D2&MI_00\7&153E4771&0&0000" if ((Get-PNPDevice -InstanceId $id).Status -eq 'OK') { Disable-PNPDevice -InstanceId $id -Confirm:$False } else {Enable-PNPDevice -InstanceId $id -Confirm:$False}
Создание запланированного задания с триггером по событию
Пожалуй, самый простой способ — создать задание вручную, а для автоматизации — экспортировать его в XML и импортировать с schtasks или командлетом PowerShell Register-ScheduledTask.
Register-ScheduledTask -Xml "C:\temp\runasadmin.xml" -TaskName "RunAsAdmin"
Чтобы создать задачу из консоли, придется немного разобраться с синтаксисом XPath. Ниже пример команды schtasks, создающий в планировщике задание, которое будет выполняться от имени системы при появлении в журнале приложений события 777
от outsidethebox
.
SCHTASKS /Create /TN "RunAsAdmin" /TR "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -file C:\RunAsAdmin.ps1" /SC ONEVENT /RU SYSTEM /EC Application /MO "*[System[Provider[@Name=\"outsidethebox\"] and (EventID=777)]]"
Что же касается PowerShell, я уже показывал создание запланированных заданий. С триггерами там все очень мутно, и создание триггера по событию требует отдельного разбора.
#Переменные $system = "NT AUTHORITY\SYSTEM" #Создание задания $taskname = "RunAsAdmin" #Общие: выполнять от имени системы вне зависимости от входа $principal = New-ScheduledTaskPrincipal -UserId $system -LogonType ServiceAccount #Триггер по событию $trigger.Enabled = $true $trigger.Subscription = '<QueryList> <Query Id="0" Path="Application"> <Select Path="Application">*[System[Provider[@Name='outsidethebox'] and (EventID=777)]]</Select> </Query> </QueryList>' #Параметры: запускать при работе от батареи; запускать немедленно если пропущено $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -StartWhenAvailable #Команда... $execute = "powershell" #... и ее параметры командной строки $argument = "-ExecutionPolicy Bypass -WindowStyle Hidden -NoProfile -file 'C:\RunAsAdmin.ps1'" #Действие: "команда + параметры командной строки" $action = New-ScheduledTaskAction -Execute $execute -Argument $argument #Создать задание в планировщике Register-ScheduledTask -TaskName $taskname -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Force #Для запуска от имени администратора измените $principal, а в конце команды↑ добавьте: -RunLevel Highest
В случае с произвольными событиями и источниками, заменяйте источник и событие в моих примерах или действуйте так:
- Если вы используете свое событие, создайте хотя бы одно утилитой eventcreate и перезапустите eventvwr.msc.
- Перейдите в журнал, в котором создано событие, отфильтруйте его по источнику и событию (можете указать несколько источников/событий).
- В фильтре перейдите на вкладку XML и скопируйте запрос. Затем отредактируйте его, заменяя все одинарные кавычки на двойные (например, в названии провайдера).
- Подставьте исправленный запрос в переменную
$trigger.Subscription
(PowerShell) или в параметры/EC
и/MO
(schtasks) . Если при выполнении скрипта возникают ошибки триггера, попробуйте вытянуть запрос в одну строку, т.е. уберите переносы строк и лишние пробелы
Заключение
В блоге планировщик играет заметную роль почти в двух десятках статей! И в очередной раз с его помощью легко решается сложная на вид задача.
На мой взгляд, это – самый простой подход к решению в домашних условиях, но не единственный. Дома и в организации задачу можно решить на PowerShell, используя технологию Just Enough Administration (JEA). Ссылки для самостоятельного изучения:
- Just Enough Administration (документация)
- Secure Your Powershell Session with JEA and Constrained Endpoints
А у вас возникала необходимость запускать что-либо от имени администратора, не имея полных прав? Напишите в комментариях, как вы решали такую задачу.
Itigo Kurosaki
А зачем такие сложности? Nsudo и всё.
Vadim Sterkin
У меня к вам встречные вопросы тогда.
1. Где вы увидели сложности? Там две команды по сути.
2. Как будете действовать в случае, если стороннее ПО запрещено?
3. Не заглядывая в документацию, сможете объяснить, как работает Nsudo и какие риски она создает? А заглядывая?
Alex Kornev
Очень «скользкая тема». И хочется, и … потом разгребать, если накосячат? Надо очень и очень хорошо понимать и потом крепко подумать, что разрешить, а что нет. В домашних условиях и подавно.
В начале 90-х, во времена MS-DOS, некий Хижняк П,Л,, выпустил книжку «Пишем вирус и антивирус».
В предисловии было сказано- «Книга для ознакомления с методами программирования резидентов и т..п А не для того, чтобы научить писать компьютерные вирусы.»
Так вот. Потом Лозинский Д.Н., , автор Aidstest, который в то время был крупнейшим компьютерным «вирусологом», высказался в том духе, что — «За такие книжки, надо отрывать руки. Увидел бы — прибил» :-)
Почему? Очень просто. По стране пронёсся буквально шквал «вирусов с ошибками», потому что даже припечатывали исходник с опечатками. :-) Хорошо, что тогда из глобальных сетей были только FIDOnet и «зародышевый» Интернет. Не так сильно пострадали.
А книжка эта, до сих пор у меня на полке есть. Коллекционное, практически раритетное издание.
Vadim Sterkin
Хорошо понимать, крепо подумать — это все общие слова. А конкретно что вы можете сказать по данному решению?
Я вот подумал и предложил ряд технических предосторожностей в предупредительных выстрелах. Соответственно, ожидаю предложений по улучшению и т.п.
Сергеич
Зачастую, в случаях, когда некоторый «китайский» софт необоснованно запрашивает админские полномочия, помогает:
cmd /min /C "set __COMPAT_LAYER=RUNASINVOKER && start "" %ApplicationPath%"
ну естественно в батник рядом с исп.файлом и ярлык на рабочий стол ведущий на батник.
Vadim Sterkin
Это обратная задача — запуск с ограниченными правами, когда ПО хочет админские. Я разбирал это в 2012: Зачем программам нужны установщики
Lecron
Интересная концепция. При внешней простоте, просто в голову не приходила.
Но простота тут именно внешняя. В реализации это непросто. Или у нас разное понимание о простоте. Поясню. Да, сделать можно, как я когда-то сделал автоматизацию по изменению файла. Включить аудит, разобраться с его событиями, привязаться к нужному. Но когда появилась возможность вернуться к nnCron, где в роли события просто указываешь «изменение файла» и путь к нему, относительная оценка сложности встала на место. Просто забить гвоздь микроскопом, пока не познакомился с молотком.
Очень не хватает в виндовом планировщике концепции «Простое — просто, сложное — возможно». Нужно простое создания триггеров для других событий, отличных от времени. В нынешнем виде, инструмент для профессионала, а не конечного пользователя.
Vadim Sterkin
В планировщике есть мастер создания простой задачи — там как раз есть по событию :) Я действительно не вижу тут ничего сложного. Проведите отсечку перед примерами скриптов — с этим в GUI справится любой человек, умеющий следовать простым инструкциям.
Автоматизация создания задачи добавляет более сложный слой — XPath, а триггеры в PowerShell — это вообще тяжелый случай :)
А так, планировщик и не предназначался для конечных пользователей, это административный инструмент, оснастка MMC же.
Lecron
Подразумевал user frendly события — Старт/завершение системы, логин/логофф, изменение каталога, создание/изменение файла, установка/разрыв сетевого соединения, изменение буфера обмена, создание/активация/удаление окна, запуск/завершение процесса.
Vadim Sterkin
Понятно, но это не к планировщику претензия уже :) В ОС эти события отслеживаются, но получение возможно либо ценой массивного журналирования (аудит доступа к объектам), либо программными средствами (буфер обмена).
Павел Старченков
Спасибо за статью, как раз искал что-то подобное, чтобы дать возможность скрипту, запускаемому пользователями без прав админа на терминальном сервере, читать список открытых файлов на сетевой шаре файлового сервера. Заодно теперь по журналу событий видно кто когда этот скрипт запускает:)
Vadim Sterkin
Пожалуйста, рад что вы нашли применение ;)
IT Video PRO
Вадим, отличная статья. Я прямо проникся :-) И решение не стандартное, никогда на слышал про такое.
Павел Нагаев.
Vadim Sterkin
Сеошники в чате :) Кстати, с прошедшим ДР, Паша!
Nikolay Proskuryakov
Делал аналогичную штуку, но на серверной Windows. Был изолированный сервер с ролью центра сертификации. Безопасники выдавали на нем сертификаты ЭЦП клиентам компании и после каждой выдачи (обычно несколько раз в день) надо было бэкапить базу ЦС, чтобы в случае падения машины не потерять данные. Стороннее ПО было категорически запрещено.
Для бэкапа ЦС нужны права администратора, а выдавать их не хотелось. В итоге сделал через PS-скрипт в планировщике, который запускался с правами системы и бэкапил базу ЦС на флешку и выдавал короткое сообщение об успешном завершении или ошибке (например, если забыли вставить флешку). А на рабочий стол вывел ярлык для запуска задачи планировщика.
Vadim Sterkin
Николай,
вы что-то путаете, похоже. Пользователю для запуска заданий планировщика нужны права администратора.Владимир Педяш
Я чего-то не понимаю. То ли у меня какая-то другая версия Windows 10, то ли я живу в параллельной вселенной. Но у меня так не работает. ((
Мне нужно, при выходе пользователя из системы запускать CMD скрипт от имени администратора.
Со скриптом вопросов нет, событие стандартное, тоже всё понятно.
Не удаётся выполнить его от имени администратора.
Распишу, как я делал :
Захожу под администратором.
Создаю задачу в планировщике.
На вкладке Общие выставляю «Выполнять для всех пользователей».
Выставляю «запуск от имени администратора».
Выставляю «Выполнять с наивысшими правами»
Нажимаю ОК и на запрос ввожу пароль администратора.
Захожу в профиль пользователя, открываю планировщик задач, становлюсь на эту задачу и для проверки нажимаю «Выполнить».
Но получаю сообщение «У данной учётной записи нет разрешения на запуск этой задачи».
Ну как же так ? Каких разрешений ей не хватает ? Задача ведь создана от имени администратора, пароль введен. Вроде бы всё так же, как и у вас.
Но почему у меня так не работает ?
Vadim Sterkin
Да неужели? У меня-то задача выполняется по событию, а пользователь просто пишет в журнал. О чем и статья, собственно :)
Иванов Иван
добрый день. решил опробовать и на первом этапе споткнулся. Система не дает пользователю записать данные в журнал — ошибка «отказано в доступе». в Описании команды читаю «Позволяет !!!администратору!!! создать пользовательское событие в указанном журнале событий»…
Подскажете? Система windows 7
Иванов Иван
так, вроде помог однократный запуск команды от имени администратора. Похоже, это связано с тем, что нужно зарегистрировать новый (неизвестный системе) источник. Дальше команда отрабатывает нормально без прав администратора
Vadim Sterkin
Да, есть такой момент. Добавил примечание под командой, спасибо.