Public/Get-WeakServicePermission.ps1

function Get-WeakServicePermission {
    <#
    .SYNOPSIS
        Finds services whose binary has write-or-higher access granted to unprivileged identities.
    .DESCRIPTION
        Enumerates all Win32 services, resolves each service binary path, and checks the file
        ACL for Allow entries that grant Write, Modify, or FullControl to Everyone,
        BUILTIN\Users, or NT AUTHORITY\Authenticated Users. Any such entry means a
        non-administrator could replace the binary and achieve privilege escalation.
        Returns one object per vulnerable ACE found. Returns nothing if no issues are found.
    .INPUTS
        None. Parameters must be supplied directly.
    .OUTPUTS
        System.Management.Automation.PSCustomObject
    .PARAMETER ComputerName
        The target computer. Defaults to the local machine.
    .EXAMPLE
        Get-WeakServicePermission

        Scans all service binaries on the local machine for weak permissions.
    .EXAMPLE
        Get-WeakServicePermission -ComputerName 'Server01'

        Scans service binaries on Server01 for weak permissions.
    .NOTES
        Read-only. Does not modify any system state.
        Requires read access to the file system ACLs of service binaries.
        Remote operations require WinRM to be configured on the target machine.
        Checks the binary file only; does not evaluate parent directory permissions.
    #>


    [CmdletBinding()]
    [OutputType([System.Management.Automation.PSCustomObject])]

    param (
        [Parameter(Mandatory = $false)]
        [string]$ComputerName = $env:COMPUTERNAME
    )

    $isLocal = ($ComputerName -ieq $env:COMPUTERNAME) -or
               ($ComputerName -ieq 'localhost') -or
               ($ComputerName -eq '127.0.0.1')

    $scan = {
        # Well-known SIDs for unprivileged identities
        $weakSids = @(
            'S-1-1-0',     # Everyone
            'S-1-5-32-545', # BUILTIN\Users
            'S-1-5-11'     # NT AUTHORITY\Authenticated Users
        )

        $writeRights = [System.Security.AccessControl.FileSystemRights]'Write' -bor
                       [System.Security.AccessControl.FileSystemRights]'Modify' -bor
                       [System.Security.AccessControl.FileSystemRights]'FullControl'

        Get-CimInstance -ClassName Win32_Service | ForEach-Object {
            $svc      = $_
            $pathName = $svc.PathName
            if ([string]::IsNullOrWhiteSpace($pathName)) { return }

            # Strip surrounding quotes and extract just the executable path
            $pathName = $pathName.TrimStart('"')
            if ($pathName -imatch '^([^"]*?\.exe)') {
                $binaryPath = $Matches[1].Trim()
            } else {
                return
            }

            if (-not (Test-Path -LiteralPath $binaryPath -PathType Leaf)) { return }

            try {
                $acl = Get-Acl -LiteralPath $binaryPath -ErrorAction Stop
            } catch {
                Write-Verbose "Could not read ACL for '$binaryPath': $($_.Exception.Message)"
                return
            }

            foreach ($ace in $acl.Access) {
                if ($ace.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow) { continue }
                if (($ace.FileSystemRights -band $writeRights) -eq 0) { continue }

                try {
                    $sid = $ace.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value
                } catch {
                    continue
                }

                if ($sid -notin $weakSids) { continue }

                [PSCustomObject]@{
                    ServiceName       = $svc.Name
                    DisplayName       = $svc.DisplayName
                    BinaryPath        = $binaryPath
                    IdentityReference = $ace.IdentityReference.Value
                    FileSystemRights  = $ace.FileSystemRights.ToString()
                }
            }
        }
    }

    if ($isLocal) {
        & $scan | ForEach-Object {
            [PSCustomObject]@{
                ComputerName      = $ComputerName
                ServiceName       = $_.ServiceName
                DisplayName       = $_.DisplayName
                BinaryPath        = $_.BinaryPath
                IdentityReference = $_.IdentityReference
                FileSystemRights  = $_.FileSystemRights
            }
        }
    } else {
        Invoke-Command -ComputerName $ComputerName -ScriptBlock $scan | ForEach-Object {
            [PSCustomObject]@{
                ComputerName      = $ComputerName
                ServiceName       = $_.ServiceName
                DisplayName       = $_.DisplayName
                BinaryPath        = $_.BinaryPath
                IdentityReference = $_.IdentityReference
                FileSystemRights  = $_.FileSystemRights
            }
        }
    }
}