Powershell : Restricting Access to Windows Services

If you need to restrict access to Windows services you need to be careful how you handle this as you can hide the service and stop is from starting, so be careful which services you choose, however if you have a requirement for this then continue with this guide.

This approach is already being used for many services, including for example Advanced Threat Protection (or ATP) as you can see below as an administrator I have no service controls they are all "greyed out"


Right, so lets take the Windows Update service as an example, this is a service that can be edited by an administrator as you can see below, the options to Start, Stop are available to the user in this example.


WARNING : Ensure you know what you are doing before you change ACL's on services as this can break the service irreversibly, also ensure you have a back out plan before randomly settings ACL security permissions! 

What if we only wanted a "certain" user of the domain to have this access and leave the system service with read access and everyone else with read - well that is simple to complete as the script below will complete:

Script : RestrictService.ps1

# PowerShell script to modify Windows Update service permissions
# This script restricts control of the Windows Update service to specific user only
# while maintaining visibility for all users

#Requires -RunAsAdministrator

# Service name to modify
$serviceName = "wuauserv"

# User who will have control permissions
$controlUser = "bear\windowsupdate.admin"

# Check if service exists
$service = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($null -eq $service) {
    Write-Error "Windows Update service (wuauserv) was not found."
    exit 1
}
Write-Host "Modifying permissions for Windows Update service ($($service.DisplayName))..."
# Get the original SDDL string
$originalSDDL = $null
try {
    $cmd = "sc.exe sdshow $serviceName"
    $originalSDDL = Invoke-Expression $cmd   
    if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrEmpty($originalSDDL)) {
        Write-Error "Failed to retrieve current security descriptor. Exit code: $LASTEXITCODE"
        exit 1
    }   
    Write-Host "Retrieved current security descriptor: $originalSDDL"
}
catch {
    Write-Error "An error occurred: $_"
    exit 1
}

# Split the SDDL into DACL and SACL parts
$DACL = ""
$SACL = ""
if ($originalSDDL -match "^(D:[^S]*)S:(.*)") {
    $DACL = $matches[1]
    $SACL = "S:" + $matches[2]
}
elseif ($originalSDDL -match "^(D:.*)") {
    $DACL = $matches[1]
    $SACL = "S:"
}
else {
    Write-Error "Could not parse the security descriptor."
    exit 1
}
Write-Host "Parsed DACL: $DACL"
Write-Host "Parsed SACL: $SACL"
# Convert the username to SID
try {
    $ntAccount = New-Object System.Security.Principal.NTAccount($controlUser)
    $userSID = $ntAccount.Translate([System.Security.Principal.SecurityIdentifier]).Value
    Write-Host "User $controlUser has SID: $userSID"
}
catch {
    Write-Error "Could not find user $controlUser. Please ensure the username is correct."
    exit 1
}

# Create a new DACL with the right permissions
# Preserving format but changing permissions:
# CCLCSWRPWPDTLOCRRC = Full control
# CCLCSWLOCRRC = Read-only with display
$newDACL = "D:(A;;CCLCSWLOCRRC;;;SY)(A;;CCLCSWLOCRRC;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$userSID)"

# Combine the new DACL with the original SACL
$newSDDL = "$newDACL$SACL"
Write-Host "Created new security descriptor: $newSDDL"

# Apply the new security descriptor
try {
    $cmd = "sc.exe sdset $serviceName `"$newSDDL`""
    Write-Host "Executing: $cmd"
    $result = Invoke-Expression $cmd   
    if ($LASTEXITCODE -ne 0) {
        Write-Error "Failed to set security descriptor. Exit code: $LASTEXITCODE"
        exit 1
    }   
    Write-Host "Security descriptor applied successfully: $result"
}
catch {
    Write-Error "Failed to set security descriptor: $_"
    exit 1
}

# Verify the service permissions were set correctly
try {
    $verifySDDL = Invoke-Expression "sc.exe sdshow $serviceName"   
    if ($LASTEXITCODE -ne 0) {
        Write-Error "Failed to verify security descriptor. Exit code: $LASTEXITCODE"
        exit 1
    }   
    Write-Host "New security descriptor verified: $verifySDDL"   

    # Verify the service is still accessible
    $visibleService = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
    if ($null -eq $visibleService) {
        Write-Warning "Service may not be visible in management console. Please verify manually."
    }
    else {
        Write-Host "Service visibility confirmed: $($visibleService.DisplayName) is visible."
    }    

    Write-Host "`nPermissions for Windows Update service have been modified successfully."
    Write-Host "- Only $controlUser can start, stop, or edit the service."
    Write-Host "- SYSTEM and other users have read-only access."
    Write-Host "- The service should remain visible to all users."
}
catch {
    Write-Error "An error occurred during verification: $_"
    exit 1
}

Now when we check the service, unless you are the specified user then the options to manage the service have vanished as if my magic.


This means only that one user can control the status of that service, which is handy to know.

How do I reset the service back to "default" permissions?

That is also very simple you can use this default permission marker to set everything back to "normal" operations:

# Set default security for the Windows Update service using the full path to sc.exe
& "$env:SystemRoot\System32\sc.exe" sdset wuauserv "D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWLOCRRC;;;SU)"

However remember that not "any user" can perform these actions, they will get Access Denied as below as you have restricted the service earlier, remember!

Previous Post Next Post

نموذج الاتصال