August 03, 2011

Windows 7 Remote Shell with custom delegation

This post will cover how to enable Powershell Remoting and WinRM remote shell using Group Policy and how to set remote session permissions to a custom group. Although focussed on Windows 7, it is equally applicable to Server 2008R2.

Step1 : Enable remoting with default permissions

The first step is to ensure that remote sessions are working using default permissions. In the enterprise, Group Policy can be used to configure the required settings as follows:

Set the WinRM service to Auto start
Computer Configuration/Policies/Windows Settings/Security Settings/System Services
Windows Remote Management (WS-Management)
Service startup mode = Automatic

Add Windows Firewall rule
Computer Configuration/Policies/Windows Settings/Security Settings/Windows Firewall with Advanced Security/Inbound Rules
New inbound Rule Wizard
Rule Type = Predefined: Windows Remote Management (HTTP-In)
Allow the connection
Profiles: Domain

Create WinRM IP Listeners
Computer Configuration/Policies/Administrative Templates/Windows Components/Windows Remote Management (WinRM)/WinRM Service
Allow automatic configuration of listeners = Enabled
IPv4 = *
IPv6 = *

Enforce security options
Computer Configuration/Policies/Administrative Templates/Windows Components/Windows Remote Management (WinRM)/WinRM Service
Allow basic authentication = disabled
Allow unencrypted traffic = disabled

Enable Remote Shell
Computer Configuration/Policies/Administrative Templates/Windows Components/Windows Remote Shell
Allow Remote Shell Access = enabled

Step 2: Test Default Permissions

a) Powershell interactive remote session
Test from ComputerB to ComputerA as follows:

Start a Powershell console on ComputerB, in the context of a user that has administrative access to ComputerA.
At the prompt, use the Enter-PSSession cmdlet to start an interactive Powershell session on ComputerA i.e.

PS C:\> Enter-PSSession -Computername ComputerA

The Prompt will change to show the remote session. Type $Env:ComputerName at the prompt to confirm the remote session

[ComputerA]: PS C:\> $ENV:COMPUTERNAME
COMPUTERA

b) Powershell Remote Command
Test from ComputerB to ComputerA as follows:

Start a Powershell console on ComputerB, in the context of a user that has administrative access to ComputerA.
At the prompt, use the Invoke-Command cmdlet to run a command on ComputerA and return the results on ComputerB

PS C:\> Invoke-Command -ComputerName ComputerA -ScriptBlock {$env:COMPUTERNAME}

c) WinRM Interactive Remote Shell
Test from ComputerB to ComputerA as follows:

Start a cmd prompt on ComputerB, in the context of a user that has administrative access to ComputerA. At the prompt, use the WinrRS command to start a WinRM remote shell on ComputerA

C:\> WinRS -r:COMPUTERA CMD.EXE

Note that by default, the prompt will not change. Use the HOSTNAME command to confirm the remote session.

Step 3: Read Default Permissions

By default, the local Administrators group are granted access to both Powershell and WinRM remote sessions. Security is managed separately for each but the default is the same. The default SDDL strings are shown below and can be useful if a reset is required. Excellent information on SDDL is available from Jorge’s blog.

a) Powershell

PS C:\> Get-PSSessionConfiguration -Name Microsoft.Powershell | Select SecurityDescriptorSddl

SecurityDescriptorSddl
----------------------
O:NSG:BAD:P(A;;GA;;;BA)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)

b) WinRM

PS C:\> Get-Item -Path "WSMan:\localhost\Service\RootSDDL" | Select Value

Value
-----
O:NSG:BAD:P(A;;GA;;;BA)S:P(AU;FA;GA;;;WD)(AU;SA;GWGX;;;WD)

Step 4: Change Permissions

Changing permissions is as simple as constructing a new SDDL and writing it to each of the properties shown above. Let’s break down the default SDDL to determine where to make the change:

SDDL portion Description
O:NS Owner = Network Service
G:BA Owner Group = BUILTIN\Administrators
D:P
(A;;GA;;;BA)
DACL : Protected
(Allow;;Generic All;;; BUILTIN\Administrators)
S:P
(AU;FA;GA;;;WD)
(AU;SA;GXGW;;;WD)
SACL : Protected
(Audit;Failed Access;Generic All;;;Everyone)
(Audit;Success;Execute,Write;;;Everyone)

To overwrite the default, the update required is as simple as changing the DACL to specify the SID of the new group i.e.

SDDL portion Description
D:P
(A;;GA;;;S-1-5-21-824518204-1975331169-839522115-6179)
DACL : Protected
(Allow;;Generic All;;; DOMAIN\GROUP)

The following code will change the permissions for both Powershell and WinRS:

Powershell Script

function Set-RemoteShellAccess {
<#
.SYNOPSIS
Sets the DACL for Powershell and WinRM remote shell access

.DESCRIPTION
By default, BUILTIN\Administrators have remote shell access.
This function appends or over-writes the DACL on the local computer to modify permissions.

.PARAMETER SID
A SID string representing the user or group to grant access

.PARAMETER APPEND
A switch controlling whether to append to or overwrite the existing DACL

.PARAMETER RESET
A stand-alone switch used to control a reset to the default DACL

.EXAMPLE
PS C:\> Set-RemoteShellAccess -Append -SID "S-1-5-21-824518204-1975331169-839522115-6179" | fl *

Confirm
Are you sure you want to perform this action?
Performing operation "Append to existing DACL to grant DOMAIN\GROUP access" on Target "Powershell Remoting".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): y

Confirm
Are you sure you want to perform this action?
Performing operation "Append to existing DACL to grant DOMAIN\GROUP access" on Target "WinRM Remote Shell".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): y

WinRMUpdate : Success
PSUpdate : Success
WinRMPermission : O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;S-1-5-21-824518204-1975331169-839522115-6179)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
PSPermission : O:NSG:BAD:P(A;;GA;;;BA)(A;;GA;;;S-1-5-21-824518204-1975331169-839522115-6179)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)

This example adds a new group to the existing DACL for both Powershell and WinRS.

.EXAMPLE
PS C:\> Set-RemoteShellAccess -Append:$False -SID "S-1-5-21-824518204-1975331169-839522115-6179" | fl *

This example over-writes the existing DACL for both Powershell and WinRS, granting a new group as the only permission.

.EXAMPLE
PS C:\> Set-RemoteShellAccess -Reset

This example over-writes the existing DACL for both Powershell and WinRS, to reset the DACL to the Windows 7 default.
Default correct for Win7 RTM and SP1 at time of writing.
.NOTES
Version 1.0
Date 2011-08-04

#>
[CmdletBinding(
SupportsShouldProcess=$True,
SupportsTransactions=$False,
ConfirmImpact="High",
DefaultParameterSetName="UPDATE")]

param(
[Parameter(Position=0,Mandatory=$true,ParameterSetName="UPDATE")]
[string]$SID
,
[Parameter(Position=1,Mandatory=$false,ParameterSetName="UPDATE")]
[switch]$Append=$True
,
[Parameter(Position=0,Mandatory=$true,ParameterSetName="RESET")]
[switch]$Reset
)

BEGIN{

# Even Get-PSSessionConfiguration requires administrative rights
$IsAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")

# Powershell and WinRM default SDDL
New-Variable -Option Constant -Name DefaultSDDL -Value "O:NSG:BAD:P(A;;GA;;;BA)S:P(AU;FA;GA;;;WD)(AU;SA;GWGX;;;WD)" -Force -Verbose:$false -Confirm:$false

# Path to WinRM RootSDDL settting
New-Variable -Option Constant -Name WinRMRootSDDL -Value "WSMan:\localhost\Service\RootSDDL" -Force -Verbose:$false -Confirm:$false

# Used for visual confirmation when running interactively
Function Get-PricipalFromSid([string]$SID)
{
$objSID = New-Object System.Security.Principal.SecurityIdentifier($SID)
$objPrincipal = $objSID.Translate( [System.Security.Principal.NTAccount])
return $objPrincipal.Value
}
}
PROCESS{

if(-not $IsAdmin){
Throw "ERROR: Please re-run this script as an Administrator."

}

switch($PSCmdlet.ParameterSetName) {
"UPDATE" {
Write-Verbose "Processing update"
# check SID is valid
$Principal = Get-PricipalFromSid -SID $SID
If(-not $Principal){
# Fatal Error
Write-Error "`nERROR: Failed to resolve SID ($SID) to a name on this computer `n`n"
Return
}
Write-Verbose "--$SID resolved to $Principal"

switch($Append) {
$true {
Write-Verbose "--Append mode"

# Build an SD based on existing DACL
$existingSDDL = (Get-PSSessionConfiguration -Name "Microsoft.PowerShell" -Verbose:$false).SecurityDescriptorSDDL
$isContainer = $false
$isDS = $false
$SecurityDescriptor = New-Object -TypeName Security.AccessControl.CommonSecurityDescriptor -ArgumentList $isContainer,$isDS, $existingSDDL

# Add the new SID
$accessType = "Allow"
$accessMask = 268435456
$inheritanceFlags = "none"
$propagationFlags = "none"
$SecurityDescriptor.DiscretionaryAcl.AddAccess($accessType,$SID,$accessMask,$inheritanceFlags,$propagationFlags) | Out-Null

# Combined SDDL
$newSDDL = $SecurityDescriptor.GetSddlForm("All")
$Message = "Append to existing DACL to grant $Principal access"

}

$false {
Write-Verbose "--Overwrite mode"
$newSDDL = "O:NSG:BAD:P(A;;GA;;;$SID)S:P(AU;FA;GA;;;WD)(AU;SA;GWGX;;;WD)"
$Message = "Overwrite existing DACL to grant $Principal access"
}
}#switch

}#update

"RESET" {
Write-Verbose "Processing reset"
$newSDDL = $DefaultSDDL
$Message = "Reset to default DACL"

}#reset
}#switch

Write-Verbose "--`NewSDDL = $NewSDDL"

# Powershell update
If ($psCmdlet.shouldProcess("Powershell Remoting" ,$Message)){
Get-PSSessionConfiguration -Verbose:$false |
ForEach-Object {
$Name = $_.Name
try{
# Turn off confirm as already wrapped in confirm block
Set-PSSessionConfiguration -name $Name -SecurityDescriptorSddl $newSDDL -force -Confirm:$false -Verbose:$false | Out-Null
$PoshResult = "Success"
}catch{
Write-Error "`nERROR: failed to update DACL on $Name `n`n$($_)"
$PoshResult = "Failed"
}
}#foreach
}else{
$PoshResult = "Cancelled"
}#endif

# WimRM update
If ($psCmdlet.shouldProcess("WinRM Remote Shell" ,$Message)){

try{
Set-Item -Path $WinRMRootSDDL -Value $newSDDL -Confirm:$false -Verbose:$false -Force | Out-Null
$WinRMResult = "Success"
}catch{
Write-Error "`nERROR: failed to update $WinRMRootSDDL `n`n$($_)"
$WinRMResult = "Failed"
}
}else{
$WinRMResult = "Cancelled"
}#endif

# Get updated SDDL string
$PSSDDL= (Get-PSSessionConfiguration -Name "Microsoft.PowerShell" -Verbose:$false).SecurityDescriptorSDDL
$WinRMSDDL = Get-Item -Path $WinRMRootSDDL | %{"$($_.Value)"}

# Output results to pipeline
New-Object -TypeName PSObject -Property @{
PSUpdate = $PoshResult
WinRMUpdate = $WinRMResult
PSPermission = $PSSDDL
WinRMPermission = $WinRMSDDL
}

}#PROCESS

}

No comments:

Post a Comment