Using powershell, how do I grant “Log on as service” to an account?

后端 未结 8 856
感情败类
感情败类 2020-12-15 17:40

I\'m trying to use powershell to configure the account credentials, but I need to grant the account \"Log on as a service\" right in order for it to work. How can I do this

相关标签:
8条回答
  • 2020-12-15 18:16

    There is a Windows API that provides access to the Local Security Policy: AdvAPI32.dll and the Lsa* methods.

    You can take advantage of PowerShell's ability to add .NET types to the current session, and .NET's P/Invoke to call Windows APIs, in order to grant a user the 'Log on as a service' permission from PowerShell.

    As a minimal example*, you first need to add some new .NET types to the current PowerShell session to work with the Windows API:

    Add-Type @'
    using System;
    using System.Runtime.InteropServices;
    
    public enum LSA_AccessPolicy : long
    {
        // Other values omitted for clarity
        POLICY_ALL_ACCESS = 0x00001FFFL
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct LSA_UNICODE_STRING
    {
        public UInt16 Length;
        public UInt16 MaximumLength;
        public IntPtr Buffer;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public struct LSA_OBJECT_ATTRIBUTES
    {
        public UInt32 Length;
        public IntPtr RootDirectory;
        public LSA_UNICODE_STRING ObjectName;
        public UInt32 Attributes;
        public IntPtr SecurityDescriptor;
        public IntPtr SecurityQualityOfService;
    }
    
    public static partial class AdvAPI32 {
        [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
        public static extern uint LsaOpenPolicy(
            ref LSA_UNICODE_STRING SystemName,
            ref LSA_OBJECT_ATTRIBUTES ObjectAttributes,
            uint DesiredAccess,
            out IntPtr PolicyHandle);
    
        [DllImport("advapi32.dll")]
        public static extern Int32 LsaClose(IntPtr ObjectHandle);
    
        [DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
        public static extern uint LsaAddAccountRights(
            IntPtr PolicyHandle,
            byte[] AccountSid,
            LSA_UNICODE_STRING[] UserRights,
            uint CountOfRights);
    }
    '@
    

    Then, you need to open a handle to the local security policy:

    function Get-LsaPolicyHandle() {
        $system = New-Object LSA_UNICODE_STRING
        $attrib = New-Object LSA_OBJECT_ATTRIBUTES -Property @{
            Length = 0
            RootDirectory = [System.IntPtr]::Zero
            Attributes = 0
            SecurityDescriptor = [System.IntPtr]::Zero
            SecurityQualityOfService = [System.IntPtr]::Zero
        };
    
        $handle = [System.IntPtr]::Zero
    
        $hr = [AdvAPI32]::LsaOpenPolicy([ref] $system, [ref]$attrib, [LSA_AccessPolicy]::POLICY_ALL_ACCESS, [ref]$handle)
    
        if (($hr -ne 0) -or ($handle -eq [System.IntPtr]::Zero)) {
            Write-Error "Failed to open Local Security Authority policy. Error code: $hr"
        } else {
            $handle
        }
    }
    

    To add a new right, you first need to create a new one:

    function New-Right([string]$rightName){
        $unicodeCharSize = 2
        New-Object LSA_UNICODE_STRING -Property @{
            Buffer = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni($rightName)
            Length = $rightName.Length * $unicodeCharSize
            MaximumLength = ($rightName.Length + 1) * $unicodeCharSize
        }
    }
    

    And then you can add that right to the security policy:

    function Grant-Rights([System.IntPtr]$policyHandle, [byte[]]$sid, [LSA_UNICODE_STRING[]]$rights) {
        $result = [AdvAPI32]::LsaAddAccountRights($policyHandle, $sid, $rights, 1)
        if ($result -ne 0) {
            Write-Error "Failed to grant right. Error code $result"
        } 
    }
    

    So putting it all together, you can then consume those functions:

    function Grant-LogonAsServiceRight([byte[]]$sid) { 
        $logonAsServiceRightName = "SeServiceLogonRight"
    
        try {
            $policy = Get-LsaPolicyHandle
            $right = New-Right $logonAsServiceRightName
            Grant-Rights $policy $sid @($right)
        }
        finally {
            if($null -ne $policy){
                [AdvAPI32]::LsaClose($policy) | Out-Null
            }
        }
    }
    

    There are other methods in AdvAPI32 to enumerate a user's account rights, remove a right etc.

    --

    *I followed the code in the Azure DevOps Pipeline Agent repo to figure out how to work with the Local Security Policy methods in AdvAPI32.

    0 讨论(0)
  • 2020-12-15 18:17

    This is not pure PowerShell but at least you do not need a third party tool.
    Everything is already on your computer and works from the command line.

    #Requires -RunAsAdministrator
    
    #The SID you want to add
    $AccountSid = 'S-1-5-21-1234567890-1234567890-123456789-500'
    
    $ExportFile = 'c:\temp\CurrentConfig.inf'
    $SecDb = 'c:\temp\secedt.sdb'
    $ImportFile = 'c:\temp\NewConfig.inf'
    
    #Export the current configuration
    secedit /export /cfg $ExportFile
    
    #Find the current list of SIDs having already this right
    $CurrentServiceLogonRight = Get-Content -Path $ExportFile |
        Where-Object -FilterScript {$PSItem -match 'SeServiceLogonRight'}
    
    #Create a new configuration file and add the new SID
    $FileContent = @'
    [Unicode]
    Unicode=yes
    [System Access]
    [Event Audit]
    [Registry Values]
    [Version]
    signature="$CHICAGO$"
    Revision=1
    [Profile Description]
    Description=GrantLogOnAsAService security template
    [Privilege Rights]
    {0}*{1}
    '@ -f $(
            if($CurrentServiceLogonRight){"$CurrentServiceLogonRight,"}
            else{'SeServiceLogonRight = '}
        ), $AccountSid
    
    Set-Content -Path $ImportFile -Value $FileContent
    
    #Import the new configuration 
    secedit /import /db $SecDb /cfg $ImportFile
    secedit /configure /db $SecDb
    
    0 讨论(0)
  • 2020-12-15 18:17

    Solution without importing the whole db

    function setSecurityPolicy {
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [string] $username,
        [Parameter(Mandatory=$true, Position=1)]
        [string] $securityField
    )
    $sid;
    if($username -like "*\*"){
        $user = $username.split('\')
        $domain=$user[0]
        $usernametemp=$user[1]
        $sid=(get-wmiobject Win32_useraccount -filter "name='$usernametemp' and Domain='$domain'").SID
    } else {
        $sid=(get-wmiobject Win32_useraccount -filter "name='$username' and Domain='$($env:COMPUTERNAME)'").SID
    }
    if(-not($sid)){
        try{
            $sid= (Get-Localgroup "$username").SID.VALUE
        } catch{
    
        }
    }
    
    if(-not($sid)) {
        $Host.UI.WriteErrorLine("setSecurityPolicy error : Account $username not found!")
        exit 1
    }
    $tmp = [System.IO.Path]::GetTempFileName()
    secedit.exe /export /cfg "$tmp" | Out-Null
    
    $currentSetting = Select-String -Pattern "$securityField = (.*)" -path $tmp | select -Expand Matches |  % { $_.Groups[1].Value }
    remove-item $tmp -Force
    
    if($currentSetting -notlike  "*$sid*" ){
        Write-Host "Modify Setting ""$securityField"""
        if( [string]::IsNullOrEmpty($currentSetting) ) {
            $currentSetting = "*$sid"
        } else {
            $currentSetting = "*$sid,$currentSetting"
        }
        $outfile = @"
    [Unicode]
    Unicode=yes
    [Version]
    signature="`$CHICAGO`$"
    Revision=1
    [Privilege Rights]
    $securityField = $currentSetting
    "@
        $tmp2 = [System.IO.Path]::GetTempFileName()
        Write-Host "Import new settings to Local Security Policy"
        $outfile | Set-Content -Path $tmp2 -Encoding Unicode -Force
    
        try {
            secedit.exe /configure /db "secedit.sdb" /cfg "$tmp2" /areas USER_RIGHTS 
        } finally { 
            remove-item $tmp2 -Force
        }
    }
    
    }
    
    0 讨论(0)
  • 2020-12-15 18:19

    PowerShell doesn't have any native means of doing this, which means you'd probably be looking at either WMI or ADSI - you're more likely to find examples in VBScript, which has been around longer, although personally I don't think I've ever figured out how to programmatically assign user rights. Doesn't mean it can't be done, though, but you'll probably be looking outside the realm of PowerShell specifically.

    0 讨论(0)
  • 2020-12-15 18:20

    This is how I solved it:

    Based on: this article

    You can download Carbon from here

    First import Carbon module as follows:

    Import-Module -Name $Path_To_Carbon -Global -Prefix CA
    
    [array]$UserPrivileges = Get-CAPrivileges -Identity $UserName;
    [bool]$LogOnAsAServiceprivilegeFound = $false;
    
    if ($UserPrivileges.Length > 0)
    {
        if ($UserPrivileges -contains "SeServiceLogonRight")
        {
            $LogOnAsAServiceprivilegeFound = $true;
        }
    }
    
    if ($LogOnAsAServiceprivilegeFound -eq $false)
    {
        Grant-CAPrivilege -Identity $UserName "SeServiceLogonRight"
    }
    
    0 讨论(0)
  • 2020-12-15 18:27

    Here's a link that you could also do within PS: original | archived.

    The problem is that there aren't really any public APIs for managing these settings, so you're a bit stuck using command-line tools provided in ResKits.

    0 讨论(0)
提交回复
热议问题