В данной статье, рассмотрим 3 способа:
- Предоставление права SeRemoteShutdownPrivilege
- Делегирование прав WMI
- Использование Windows Powershell Remoting / JEA
Наш стенд:
- 2 клиентских ОС – Windows 10
- 1 DC – Windows Server 2012 R2
- Администратор: Contoso\Administrator
- Пользователь: Contoso\User1
I. Первый способ – это предоставление права SeRemoteShutdownPrivilege
Force shutdown from a remote system — Этот параметр безопасности определяет, каким пользователям разрешено завершать работу компьютера из удаленного расположения в сети. Это позволяет членам группы администраторов или конкретных пользователей для управления компьютерами (для задач, таких как перезапуск) из удаленного расположения.
Для предоставления SeRemoteShutdownPrivilege мы можем использовать: GPO, ntrights, secedit и т.д.
Утилита ntrights входит в комплект Windows Server 2003 Resource Kit Tools . После установки комплекта , выполняем:
ntrights -r SeRemoteShutdownPrivilege -u Contoso\User1
Другая утилита, которая поставляется по умолчанию является — secedit.
При использовании secedit, потребуется подготовить шаблон.
SECEDIT /configure /db secedit.sdb /cfg "c:\SEC_TEMPLATE.inf"
[Unicode]
Unicode=yes
[Registry Values]
[Privilege Rights]
SeRemoteShutdownPrivilege = "Administrators","Contoso\User1"
[Version]
signature="$CHICAGO$"
Revision=1
[Profile Description]
Description=Local Security Template
SECEDIT /configure /db secedit.sdb /cfg "c:\SEC_TEMPLATE.inf"
Для предоставления права вручную:
Предоставим право SeRemoteShutdownPrivilege через PowerShell. Для этого нам потребуется отличный модуль — UserRights PowerShell Module .
1. Устанавливаем UserRights PowerShell Module
$fname = "UserRights.zip"
$dpath = "$env:UserProfile\Documents\WindowsPowerShell\Modules\UserRights"
$url = "https://gallery.technet.microsoft.com/scriptcenter/UserRights-PowerShell-1ff45589/file/124612/1/UserRights.zip"
[Net.WebClient]::new().DownloadFile($url, $fname)
Unblock-File $fname
Expand-Archive -LiteralPath $fname -DestinationPath $dpath
2. Проверим у кого есть право SeRemoteShutdownPrivilege по умолчанию
PS > Get-UserRight -Privileges SeRemoteShutdownPrivilege
Right ComputerName Accounts
----- ------------ --------
SeRemoteShutdownPrivilege W10-CL2 BUILTIN\Administrators
3. Добавим право SeRemoteShutdownPrivilege для пользователя Contoso\User1
PS > Grant-UserRight -Account Contoso\User1 -Privileges SeRemoteShutdownPrivilege -PassThru
Right ComputerName Accounts
----- ------------ --------
SeRemoteShutdownPrivilege W10-CL2 {BUILTIN\Administrators, CONTOSO\User1}
4. Добавим правило исключения в firewall
Get-NetFirewallRule -DisplayName "File and Printer Sharing (SMB-In)" | Enable-NetFirewallRule
5. Выключаем удаленную машину
shutdown /m \\W10-CL2.Contoso.Com /s /t 0
Какие WinApi использует утилита shutdown:
- Win2003/WinXP — InitiateSystemShutdownEx function
- WinVista+ — InitiateShutdown function
Определим сигнатуру и выполним функцию.
$Signature = @"
[DllImport("advapi32.dll")]
public static extern bool InitiateSystemShutdown(
[MarshalAs(UnmanagedType.LPStr)] string lpMachinename,
[MarshalAs(UnmanagedType.LPStr)] string lpMessage,
Int32 dwTimeout,
bool bForceAppsClosed,
bool bRebootAfterShutdown);
"@
$ShutDown = Add-Type -MemberDefinition $Signature -Name "Win32" -Namespace Win32Functions -PassThru
$ShutDown::InitiateSystemShutdown("W10-CL2.Contoso.Com","",0,$true,$false)
Можно использовать другой метод, который описан в статье — Weekend Scripter: Give Your PowerShell Console a Glassy Theme .
#region Module Builder
$Domain = [AppDomain]::CurrentDomain
$DynAssembly = New-Object System.Reflection.AssemblyName('PInvokeAssembly')
# Only run in memory
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('PInvokeModule', $False)
#endregion Module Builder
$TypeBuilder = $ModuleBuilder.DefineType('PInvoke', 'Public, Class')
$PInvokeMethod = $TypeBuilder.DefineMethod(
'InitiateSystemShutdown', #Method Name
[Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
[bool], #Method Return Type
[Type[]] @([string],[string],[Int32],[bool],[bool]) #Method Parameters
)
$DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
$FieldArray = [Reflection.FieldInfo[]] @(
[Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
[Runtime.InteropServices.DllImportAttribute].GetField('PreserveSig')
)
$FieldValueArray = [Object[]] @(
'InitiateSystemShutdown', #CASE SENSITIVE!!
$False
)
$CustomAttributeBuilder = New-Object Reflection.Emit.CustomAttributeBuilder(
$DllImportConstructor,
@('Advapi32.dll'),
$FieldArray,
$FieldValueArray
)
$PInvokeMethod.SetCustomAttribute($CustomAttributeBuilder)
[void]$TypeBuilder.CreateType()
[PInvoke]::InitiateSystemShutdown("W10-CL2.Contoso.Com","",0,$true,$false)
Для удаления права:
PS > Revoke-UserRight -Account Contoso\User1 -Privileges SeRemoteShutdownPrivilege -PassThru
Right ComputerName Accounts
----- ------------ --------
SeRemoteShutdownPrivilege W10-CL2 BUILTIN\Administrators
II. Второй способ — Делегирование прав WMI
Удаленный доступ к WMI по умолчанию разрешен только группе Администраторы. Т.к. право Access this computer from network по умолчанию назначено Domain Users для клиентских ос, то мы его трогать не будем.
Т.к. группе Distributed COM Users выданны нужные права, то нам будет достаточно только добавить туда пользователя или группу. Если Вам потребуется ограничить права более детально:
- Создайте группу и добавьте заданных пользователей
- Проверить, что группа имеет право — Access this computer from network user right
- Component Services (dcomcnfg.exe) -> Computers -> My Computer –> Properties
- Проверить галочку Default Properties -> Enable Distributed COM
- COM Security tab -> Edit Limits секция Launch and Activation Permissions и добавить группу, указав Allow для Local Launch, Remote Launch, Local Activation, Remote Activation
- Открываем My Computer -> переходим к разделу DCOM Config находим Windows Management and Instrumentation -> Properties –> Security. В разделе Launch and Activation Permissions назначаем Allow для Local Launch, Remote Launch, Local Activation, Remote Activation
- Закрываем
- WMI Control(WmiMgmt.msc) –> Properties –> Security –> Root\Cimv2 –> Security. Добавляем права Allow для Remote Enable
Как в начале было сказано, мы пойдет более простым путем.
1. Добавим пользователя «Contoso\User1» в группу «Remote Management Users»
Add-LocalGroupMember "Distributed COM Users" -Member "Contoso\User1"
2. Добавим правила в FireWall
Get-NetFirewallRule -DisplayName "Windows Management Instrumentation (WMI-IN)" | Enable-NetFirewallRule
3. Добавим право SeRemoteShutdownPrivilege для пользователя Contoso\User1 ,т.к. выключение происходит удаленно то требуется данное право. Для локального выключения – SeShutdownPrivilege.
PS > Grant-UserRight -Account Contoso\User1 -Privileges SeRemoteShutdownPrivilege -PassThru
Right ComputerName Accounts
----- ------------ --------
SeRemoteShutdownPrivilege W10-CL2 {BUILTIN\Administrators, CONTOSO\User1}
4. Добавим разрешения для namespace = root\cimv2, где расположен класс Win32_OperatingSystem
$Namespace = "root\cimv2"
$systemSecurity = Get-CimInstance -Namespace $Namespace -ClassName __SystemSecurity
$oDacl = Invoke-CimMethod -InputObject $systemSecurity -MethodName GetSecurityDescriptor
$sd = $oDacl.Descriptor
$domain = "Contoso"
$user = "User1"
$ntuser = New-Object System.Security.Principal.NTAccount($domain,$user)
$trustee = New-CimInstance -Namespace root/cimv2 -ClassName Win32_Trustee -ClientOnly -Property @{
Domain = $domain
Name = $user
SidString = $ntuser.Translate([Security.Principal.SecurityIdentifier]).Value
}
# AceType Allow = 0 Deny = 1
# AccessMask 35 = Execute Methods,Enable Account,Remote Enable
$ace = New-CimInstance -Namespace root/cimv2 -ClassName Win32_Ace -ClientOnly -Property @{
AceType=[uint32]0
Trustee=$trustee
AccessMask=[uint32]35
AceFlags=[uint32]0
}
[CIMInstance[]] $nDacl = $null
foreach ($iAce in $sd.DACL) {
$nDacl += $iAce
}
$newDacl += $ace
$sd.DACL = $newDacl
Invoke-CimMethod -InputObject $systemSecurity -MethodName SetSecurityDescriptor -Arguments @{
Descriptor = $sd
}
5. Выключаем
Stop-Computer -ComputerName w10-cl2.contoso.com -Force
С переводом проекта PowerShell — https://github.com/PowerShell/PowerShell в разряд OpenSource, у нас появилась возможность посмотреть работу командлета Stop-Computer.
PS > Get-Command Stop-Computer | Format-List Module,ImplementingType
Module : Microsoft.PowerShell.Management
ImplementingType : Microsoft.PowerShell.Commands.StopComputerCommand
Зная тип переходим на сайте github и в поиске указываем StopComputerCommad.
Данный командлет начинается в строке 3185 — #region Stop-Computer и заканчивается 3606 — #endregion .
Командлет работает,как с DCOM, так и WSMAN.
PS > Get-Help Stop-Computer -Parameter Authentication
-Authentication <AuthenticationLevel>
Указывает уровень проверки подлинности, используемый для подключения к WMI. (Stop-Computer использует WMI.)
Значение по умолчанию — Packet.
Packet: проверка подлинности COM на уровне пакетов.
Значение по умолчанию Packet (4)
/// Packet = 4,
[Alias("Authentication")]
public AuthenticationLevel DcomAuthentication { get; set; } = AuthenticationLevel.Packet;
На платформе Windows Desktop , используется по умолчанию DcomProtocol. Для поддержки других платформ(например Linux,MacOs) — WsmanProtocol.
Подробнее :
https://blogs.msdn.microsoft.com/dotnet/2014/12/04/introducing-net-core/
https://blogs.msdn.microsoft.com/dotnet/2015/02/03/coreclr-is-now-open-source/
[ValidateSet(ComputerWMIHelper.DcomProtocol, ComputerWMIHelper.WsmanProtocol)]
public string Protocol { get; set; } =
#if CORECLR
//CoreClr does not support DCOM protocol
// This change makes sure that the the command works seamlessly if user did not explicitly entered the protocol
ComputerWMIHelper.WsmanProtocol;
#else
ComputerWMIHelper.DcomProtocol;
#endif
PS > Get-Help Stop-Computer -Parameter Impersonation
-Impersonation <ImpersonationLevel>
Задает уровень олицетворения, используемый при вызове WMI. (Stop-Computer использует WMI.) Значение по умолчанию —
"Impersonate".
Impersonate: позволяет объектам использовать учетные данные вызывающей стороны.
// Impersonate = 3,
public ImpersonationLevel Impersonation { get; set; } = ImpersonationLevel.Impersonate;
Рассматривать работу в Job не будем. Метод Win32Shutdown определен:
uint32 Win32Shutdown (
[in] sint32 Flags,
[in] sint32 Reserved =
);
Если параметр -Force не указан, то используется флаг — 0x1, иначе 0x5.
1 (0x1)
Shutdown — Shuts down the computer to a point where it is safe to turn off the power.
5 (0x5)
Forced Shutdown (1 + 4) — Shuts down the computer to a point where it is safe to turn off the power.
# Определяется флаг
object[] flags = new object[] { 1, 0 };
if (Force.IsPresent)
flags[0] = 5;
# Вызов метода Win32Shutdown класса Win32_OperatingSystem с заданными параметрами
ConnectionOptions options = ComputerWMIHelper.GetConnectionOptions(DcomAuthentication, this.Impersonation, this.Credential);
ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options);
ObjectQuery query = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem);
using (_searcher = new ManagementObjectSearcher(scope, query, enumOptions))
{
foreach (ManagementObject obj in _searcher.Get())
{
using (obj)
{
object result = obj.InvokeMethod("Win32shutdown", flags);
Для просмотра internal properties класса ComputerWMIHelper:
PS > $class = [Reflection.Assembly]::Load(
"Microsoft.PowerShell.Commands.Management").GetType(
"Microsoft.PowerShell.Commands.ComputerWMIHelper")
PS > $class.GetFields([Reflection.BindingFlags]"NonPublic,Static").Name
NetBIOSNameMaxLength
WMI_Class_SystemRestore
WMI_Class_OperatingSystem
WMI_Class_Service
WMI_Class_ComputerSystem
WMI_Class_PingStatus
WMI_Path_CIM
WMI_Path_Default
ErrorCode_Interface
ErrorCode_Service
SE_SHUTDOWN_NAME
SE_REMOTE_SHUTDOWN_NAME
DcomProtocol
WsmanProtocol
CimUriPrefix
CimOperatingSystemNamespace
CimOperatingSystemShutdownMethod
CimQueryDialect
localhostStr
PS > $class.GetField(
"WMI_Class_OperatingSystem",[Reflection.BindingFlags]"NonPublic,Static").GetValue($class)
Win32_OperatingSystem
PS > $class.GetFields([Reflection.BindingFlags]"NonPublic,Static").Foreach({
[pscustomobject]@{
Name = $_.Name
Value = $class.GetField($_.Name,[Reflection.BindingFlags]"NonPublic,Static").GetValue($class)
}
})
Name Value
---- -----
NetBIOSNameMaxLength 15
WMI_Class_SystemRestore SystemRestore
WMI_Class_OperatingSystem Win32_OperatingSystem
WMI_Class_Service Win32_Service
WMI_Class_ComputerSystem Win32_ComputerSystem
WMI_Class_PingStatus Win32_PingStatus
WMI_Path_CIM \root\cimv2
WMI_Path_Default \root\default
ErrorCode_Interface 1717
ErrorCode_Service 1056
SE_SHUTDOWN_NAME SeShutdownPrivilege
SE_REMOTE_SHUTDOWN_NAME SeRemoteShutdownPrivilege
DcomProtocol DCOM
WsmanProtocol WSMan
CimUriPrefix http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2
CimOperatingSystemNamespace root/cimv2
CimOperatingSystemShutdownMethod Win32shutdown
CimQueryDialect WQL
localhostStr localhost
III. Третий способ — Windows Powershell Remoting / JEA
Отличное описание Windows PowerShell endpoints в серии статей — https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/31/introduction-to-powershell-endpoints/
Перейдем сразу к реализации, т.к. вариантов много, рассмотрим один из них.
1. Включаем PowerShell Remoting
PS > Enable-PSSessionConfiguration -Force
WinRM has been updated to receive requests.
WinRM service type changed successfully.
WinRM service started.
WinRM has been updated for remote management.
WinRM firewall exception enabled.
2. Зарегистрируем endpoint с учетными данными администратора
New-PSSessionConfigurationFile –Path ShutDownEnd.pssc –SessionType RestrictedRemoteServer -LanguageMode NoLanguage -VisibleCmdlets Stop-Computer
Register-PSSessionConfiguration -Name ShutDown -Path ShutDownEnd.pssc –ShowSecurityDescriptorUI -RunAsCredential Contoso\Administrator
3. Выключаем
$session = New-PSSession -ConfigurationName ShutDown -ComputerName W10-CL2.contoso.com
Invoke-Command -Session $session -ScriptBlock {Stop-Computer -Force}
Параметр ShowSecurityDescriptorUI вызывает окно где мы указываем права доступа, достаточно Execute(Invoke).
Для автоматизации процесса, скачайте скрипт Add-PoShEndpointAccess.
Add-PoShEndpointAccess -SamAccountName "contoso\User1" -EndpointName ShutDown
И в качестве примера, приведу использование JEA (Just Enough Administration).
Just Enough Administration (JEA) — это технология безопасности, позволяющая делегировать администрирование в отношении всего, чем можно управлять через PowerShell. JEA позволяет сделать следующее:
- Уменьшить число администраторов на компьютерах, используя виртуальные учетные записи, которые допускают выполнение привилегированных действий от имени обычных пользователей.
- Ограничить доступные пользователям действия, указав, какие командлеты, функции и внешние команды могут выполнять пользователи.
- Лучше понять, что делают ваши пользователи, используя детализированные записи с «подсматриванием» для команд, выполняемых пользователем за время сеанса.
Подробнее:
https://msdn.microsoft.com/en-us/powershell/jea/readme
https://github.com/PowerShell/JEA
# Fields in the role capability
$MaintenanceRoleCapabilityCreationParams = @{
Author = 'Contoso Admin'
CompanyName = 'Contoso'
VisibleCmdlets = @{ Name = 'Stop-Computer' ; Parameters = @{ Name = 'Force' } }
}
# Create the demo module, which will contain the maintenance Role Capability File
New-Item -Path "$env:ProgramFiles\WindowsPowerShell\Modules\Demo_Module" -ItemType Directory
New-ModuleManifest -Path "$env:ProgramFiles\WindowsPowerShell\Modules\Demo_Module\Demo_Module.psd1"
New-Item -Path "$env:ProgramFiles\WindowsPowerShell\Modules\Demo_Module\RoleCapabilities" -ItemType Directory
# Create the Role Capability file
New-PSRoleCapabilityFile -Path "$env:ProgramFiles\WindowsPowerShell\Modules\Demo_Module\RoleCapabilities\Maintenance.psrc" @MaintenanceRoleCapabilityCreationParams
# Determine domain
$domain = (Get-CimInstance -ClassName Win32_ComputerSystem).Domain
# Replace with your non-admin group name
$NonAdministrator = "$domain\User1"
# Specify the settings for this JEA endpoint
# Note: You will not be able to use a virtual account if you are using WMF 5.0 on Windows 7 or Windows Server 2008 R2
$JEAConfigParams = @{
SessionType = 'RestrictedRemoteServer'
RoleDefinitions = @{
$NonAdministrator = @{ RoleCapabilities = 'Maintenance' }
}
TranscriptDirectory = "$env:ProgramData\JEAConfiguration\Transcripts"
}
# Set up a folder for the Session Configuration files
if (-not (Test-Path "$env:ProgramData\JEAConfiguration"))
{
New-Item -Path "$env:ProgramData\JEAConfiguration" -ItemType Directory
}
# Specify the name of the JEA endpoint
$sessionName = 'JEA_ShutDown'
if (Get-PSSessionConfiguration -Name $sessionName -ErrorAction SilentlyContinue)
{
Unregister-PSSessionConfiguration -Name $sessionName -ErrorAction Stop
}
New-PSSessionConfigurationFile -Path "$env:ProgramData\JEAConfiguration\JEADemo.pssc" @JEAConfigParams
# Register the session configuration
Register-PSSessionConfiguration -Name $sessionName -Path "$env:ProgramData\JEAConfiguration\JEADemo.pssc" -RunAsCredential Contoso\Administrator
}
$session = New-PSSession -ConfigurationName ShutDown -ComputerName W10-CL2.contoso.com
Invoke-Command -Session $session -ScriptBlock {Stop-Computer -Force}
Дополнительная информация:
1) Setting up a Remote WMI Connection — https://msdn.microsoft.com/en-us/library/aa822854(v=vs.85).aspx
2) DSC Resource to manage WMI Namespace Security — https://github.com/PowerShell/WmiNamespaceSecurity/blob/master/WmiNamespaceSecurity.psm1
3) Privilege Constants — https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx
4) Force shutdown from a remote system — https://technet.microsoft.com/en-us/library/dn221951(v=ws.11).aspx
5) Access to WMI Namespaces — https://msdn.microsoft.com/en-us/library/aa822575(v=vs.85).aspx
6) Windows PowerShell endpoints — https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/31/introduction-to-powershell-endpoints/
7) Just Enough Administration — https://msdn.microsoft.com/en-us/powershell/jea/readme
8) Constrained PowerShell endpoints – Visible cmdlets, session types, and language modes — https://4sysops.com/archives/constrained-powershell-endpoints-visible-cmdlets-session-types-and-language-modes/
9) Using Powershell and Reflection API to invoke methods from .NET Assemblies — https://blog.netspi.com/using-powershell-and-reflection-api-to-invoke-methods-from-net-assemblies/