Least Privileges for Windows Agents and Inspectors

This is a step-by-step guide on how to configure the Liongard Agent "Run as" Service Account to utilize the least privileged security practices.

Overview

This document is designed to assist MSPs who want to limit the permissions of their Liongard Agent's Service Account. This Service Account serves two purposes:

  1. Installation and auto-updating of your Liongard Agent
  2. Performing Windows-based Inspections (Active Directory, Hyper-V, SQL Server, and Windows Server)

🚧

Liongard Support of Least Privilege

Liongard will only support least privilege for Environments running the Windows Agent version 3.2.1 or higher with the auto-update checkbox enabled and only local Windows Inspections. Least privilege does not apply to inspecting remote windows servers.

Please ensure all Agents are using at least version 3.2.1 with auto-updating enabled before performing these steps.

Local Directory Permission

The Liongard Agent Service Account must have full access to the Liongardinc directory located in the Program Files (x86) folder.

Permissions Necessary for Active Directory Inspections

Make sure this Service Account is added to the DHCP users Group in Active Directory.

346

If this a Domain Network, the Agent’s Service Account needs to have at least read access to the MicrosoftDNS Container in Active Directory.

  1. To locate the MicrosoftDNS Container, open Active Directory and navigate to System > MicrosoftDNS.
  2. Right-click the MicrosoftDNS Container.
  3. Select Properties > Security and add your Liongard Service Account to the list of users.
  4. Set the permissions for the account to Read.
732

Remote Windows Server Inspection Permissions: PowerShell (Option 1)

🚧

Local Windows Server Inspections

On all remote machines, add the Liongard Service Account to the following local group:

  • Remote Management Users

The following script grants the Liongard Service Account permission to access the Service Control Manager (SCManager) and Configures WMI Permissions on all Remote Devices.

  1. Access your Agent Host Machine, open PowerShell, and run the following script
Param ( 
    [parameter(Mandatory=$false,Position=0)][string] $namespace,
    [parameter(Mandatory=$false,Position=1)][string] $operation,
    [parameter(Mandatory=$false,Position=2)][string] $account,
    [parameter(Mandatory=$true,Position=3)][string] $ServiceUser,
    [parameter(Mandatory=$false,Position=3)][string] $computerName,
    [parameter(Position=4)][string[]] $permissions = $null,
    [bool] $allowInherit = $false,
    [bool] $deny = $false,
    [System.Management.Automation.PSCredential] $credential = $null
    )

function Set-Permissions { 
param (
        $namespace
    )
Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow "Configuring $namespace Permissions"

    $ErrorActionPreference = "Stop"
 
    Function Get-AccessMaskFromPermission($permissions) {
        $WBEM_ENABLE = 1
                $WBEM_METHOD_EXECUTE = 2
                $WBEM_FULL_WRITE_REP = 4
                $WBEM_PARTIAL_WRITE_REP = 8
                $WBEM_WRITE_PROVIDER = 0x10
                $WBEM_REMOTE_ACCESS = 0x20
                $WBEM_RIGHT_SUBSCRIBE = 0x40
                $WBEM_RIGHT_PUBLISH  = 0x80
        $READ_CONTROL = 0x20000
        $WRITE_DAC = 0x40000
       
        $WBEM_RIGHTS_FLAGS = $WBEM_ENABLE,$WBEM_METHOD_EXECUTE,$WBEM_FULL_WRITE_REP,`
            $WBEM_PARTIAL_WRITE_REP,$WBEM_WRITE_PROVIDER,$WBEM_REMOTE_ACCESS,`
            $READ_CONTROL,$WRITE_DAC
        $WBEM_RIGHTS_STRINGS = "Enable","MethodExecute","FullWrite","PartialWrite",`
            "ProviderWrite","RemoteAccess","ReadSecurity","WriteSecurity"
 
        $permissionTable = @{}
 
        for ($i = 0; $i -lt $WBEM_RIGHTS_FLAGS.Length; $i++) {
            $permissionTable.Add($WBEM_RIGHTS_STRINGS[$i].ToLower(), $WBEM_RIGHTS_FLAGS[$i])
        }
       
        $accessMask = 0
 
        foreach ($permission in $permissions) {
            if (-not $permissionTable.ContainsKey($permission.ToLower())) {
                throw "Unknown permission: $permission`nValid permissions: $($permissionTable.Keys)"
            }
            $accessMask += $permissionTable[$permission.ToLower()]
        }
       
        $accessMask
    }
 
    if ($PSBoundParameters.ContainsKey("Credential")) {
        $remoteparams = @{ComputerName=$computer;Credential=$credential}
    } else {
        $remoteparams = @{ComputerName=$computerName}
    }
       
    $invokeparams = @{Namespace=$namespace;Path="__systemsecurity=@"} + $remoteParams
 
    $output = Invoke-WmiMethod @invokeparams -Name GetSecurityDescriptor
    if ($output.ReturnValue -ne 0) {
        throw "GetSecurityDescriptor failed: $($output.ReturnValue)"
    }
 
    $acl = $output.Descriptor
    Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow "Current ACL for $namespace is $acl"
    $OBJECT_INHERIT_ACE_FLAG = 0x1
    $CONTAINER_INHERIT_ACE_FLAG = 0x2
 
    $computerName = (Get-WmiObject @remoteparams Win32_ComputerSystem).Name
   
    if ($account.Contains('\')) {
        $domainaccount = $account.Split('\')
        $domain = $domainaccount[0]
        if (($domain -eq ".") -or ($domain -eq "BUILTIN")) {
            $domain = $computerName
        }
        $accountname = $domainaccount[1]
    } elseif ($account.Contains('@')) {
        $domainaccount = $account.Split('@')
        $domain = $domainaccount[1].Split('.')[0]
        $accountname = $domainaccount[0]
    } else {
        $domain = $computerName
        $accountname = $account
    }
    $getparams = @{Class="Win32_Account";Filter="Name='$accountname'"}
    $win32account = Get-WmiObject @getparams

    Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow "Applying permissions for " + $win32account

    Start-Sleep 2

    Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow $win32account.Sid

    if ($win32account -eq $null) {
        throw "Account was not found: $account"
    }
 
    switch ($operation) {
        "add" {
            if ($permissions -eq $null) {
                throw "-Permissions must be specified for an add operation"
            }
            $accessMask = Get-AccessMaskFromPermission($permissions)
   
            $ace = (New-Object System.Management.ManagementClass("win32_Ace")).CreateInstance()
            $ace.AccessMask = $accessMask
            if ($allowInherit) {
                $ace.AceFlags = $OBJECT_INHERIT_ACE_FLAG + $CONTAINER_INHERIT_ACE_FLAG
            } else {
                $ace.AceFlags = 0
            }
                       
            $trustee = (New-Object System.Management.ManagementClass("win32_Trustee")).CreateInstance()
            $trustee.SidString = $win32account.Sid
            $ace.Trustee = $trustee
           
            $ACCESS_ALLOWED_ACE_TYPE = 0x0
            $ACCESS_DENIED_ACE_TYPE = 0x1
 
            if ($deny) {
                $ace.AceType = $ACCESS_DENIED_ACE_TYPE
            } else {
                $ace.AceType = $ACCESS_ALLOWED_ACE_TYPE
            }
 
            $acl.DACL += $ace.psobject.immediateBaseObject
        }
       
        default {
            throw "Unknown operation: $operation`nAllowed operations: add"
        }
    }
 
    $setparams = @{Name="SetSecurityDescriptor";ArgumentList=$acl.psobject.immediateBaseObject} + $invokeParams
 
    $output = Invoke-WmiMethod @setparams
    if ($output.ReturnValue -ne 0) {
        throw "SetSecurityDescriptor failed: $($output.ReturnValue)"
    }

    Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow "COMPLETE"

    }

try {

if ($ServiceUser -eq $null) {
$ServiceUser = read-host -Prompt "Please enter a Service User (LGUser)" 
}

Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow "Generating a list of servers....."

$currentServer = $env:COMPUTERNAME
    
$serverlist = Get-ADComputer -Filter 'Name -ne $currentServer -and OperatingSystem -Like "*Windows Server*"'

Add-Type -AssemblyName System.Windows.Forms
$Form = New-Object System.Windows.Forms.Form
$Form.width = 250
$Form.height = 500
$Form.Text = 'Server Select'

$checkedlistbox=New-Object System.Windows.Forms.CheckedListBox
$checkedlistbox.Location = '10,10'
$checkedlistbox.Size = '220,420'

$form.Controls.Add($checkedlistbox)
$checkedlistbox.CheckOnClick = $true
$checkedlistbox.Items.Add("Select All")
foreach ($svr in $serverlist) {
    $checkedlistbox.Items.Add($svr.Name)   
}
$checkedlistbox.Add_click({
    If($this.selecteditem -eq 'Select All'){
        If ($This.GetItemCheckState(0) -ne 'Checked') {
            For($i=1;$i -lt $CheckedListBox.Items.Count; $i++){
                $checkedlistbox.SetItemChecked($i,$true)
            }           
        } Else {
            For($i=1;$i -lt $CheckedListBox.Items.Count; $i++){
                $checkedlistbox.SetItemChecked($i,$False)
            }
        }
    }
})

$ApplyButton = new-object System.Windows.Forms.Button
$ApplyButton.Location = '70, 420'
$ApplyButton.Size = '100, 40'
$ApplyButton.Text = 'OK'
$ApplyButton.DialogResult = 'Ok'
$form.Controls.Add($ApplyButton)

[void]$Form.ShowDialog()


if ($checkedlistbox[0].Items[0] -eq "Select All") {
    $checkedlistbox.SetItemChecked(0,$false)
}

$x = $checkedlistbox.CheckedItems

foreach($server in $x){

    Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow "Configuring $server"

    $account = $ServiceUser

    $namespace1 = "root/cimv2"

    $namespace2 = "root/Microsoft/Windows/TaskScheduler"

    $namespace3 = "root/standardcimv2"

    $operation = "add"


    $permissions = @("readsecurity","remoteaccess","methodexecute","enable")

    $SID = Get-ADUser -Identity $ServiceUser | select-object SID

    $SID = $SID.SID.Value

    Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow "Granting Service Control Manager Permissions to $ServiceUser"

    Start-Sleep 2

    Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow "$ServiceUser SID = $SID"

    Start-Sleep 2

    #sc sdshow scmanager

    invoke-command -computername $server -argumentlist $SID -scriptblock {
        param ($SID)
        $scm = cmd.exe /c sc sdshow scmanager
        Write-Host -BackgroundColor DarkMagenta -ForegroundColor Yellow "SCM is $scm" 
        $entry = "(A;;CCLCRPRC;;;"
        Write-Host -BackgroundColor DarkMagenta -ForegroundColor Yellow "entry is $entry"
        Write-Host -BackgroundColor DarkMagenta -ForegroundColor Yellow "SID is $SID.SID"
        $newscm = $entry + $SID
        Write-Host -BackgroundColor DarkMagenta -ForegroundColor Yellow "newscm is $newscm"
        $groups = $scm -split "\)"
        Write-Host -BackgroundColor DarkMagenta -ForegroundColor Yellow "Groups are $groups"
        $groupsnew = $groups | select -First 3
        Write-Host -BackgroundColor DarkMagenta -ForegroundColor Yellow "First 2 groups are $groups"
        $groupsnew += $newscm
        Write-Host -BackgroundColor DarkMagenta -ForegroundColor Yellow "New groups list is $groupsnew"
        $groupsnew += $groups | select -Skip 3
        Write-Host -BackgroundColor DarkMagenta -ForegroundColor Yellow "Complete groups list is $groupsnew"
        $groupsnew = ($groupsnew -join ")").TrimStart("\)")
        Write-Host -BackgroundColor DarkMagenta -ForegroundColor Yellow "Final string is $groupsnew"
        Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow "Adding new SDDL to scm"
        Start-Sleep 2

        cmd.exe /c sc sdset scmanager $groupsnew

        Write-Host -BackgroundColor DarkBlue -ForegroundColor Yellow $groupsnew

        Start-Sleep 2
    }

    $computerName = $server

    Set-Permissions -namespace $namespace1
    Set-Permissions -namespace $namespace2
    Set-Permissions -namespace $namespace3
}
}
catch {
    Write-Error $_.Exception.ToString()
    Read-Host -Prompt "The above error occurred. Press Enter to exit."

}

The script will ask for the Liongard service account, and then will list the Windows servers found in the host's active directory instance. Select the checkboxes for the servers you would like to remotely inspect and press OK.

🚧

The RPC server is unavailable

While running the script If you receive the error "Invoke-WmiMethod : The RPC server is unavailable." Please check the target server's local firewall or antivirus software to confirm that neither are blocking access.

Remote Windows Server Inspection Permissions: Manual (Option 2)

Service Control Manager Permissions

On all remote machines, you must allow the Liongard Service Account to access the Service Control Manager (SCManager).

  1. Access your Agent Host Machine, open PowerShell, and run the following command by replacing the username below with the username of your designated Liongard Service Account.
Get-ADuser -Identity LGuser

Your result should look like this:

666
  1. Copy and save the user SID value
  2. Access the remote machine
  3. Next, we will retrieve the SDDL of the Service Control Manager. Open Command Prompt, copy and paste the following commands and press enter:
sc sdshow scmanager > %Temp%/file.txt
%Temp%/file.txt

The SDDL value printed into the text file should resemble the following:

D:(A;;CC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)S:(AU;FA;KA;;;WD)(AU;OIIOFA;GA;;;WD)
  1. Add (A;;CCLCRPRC;;;) to the beginning of the SID value you saved in step 2 and copy it to the SDDL. In the example below we have added (A;;CCLCRPRC;;;S-1-5-21-2763123298-1191401962-1747402453-1127).)
D:(A;;CC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;S-1-5-21-2763123298-1191401962-1747402453-1127)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)S:(AU;FA;KA;;;WD)(AU;OIIOFA;GA;;;WD)
  1. Lastly use "Command Prompt "to run the command sc sdset scmanager "yourSDDLvalue"
  • Make sure to include the quotation marks.
    Example:
sc sdset scmanager "D:(A;;CC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;S-1-5-21-2763123298-1191401962-1747402453-1127)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)S:(AU;FA;KA;;;WD)(AU;OIIOFA;GA;;;WD)"

If the command was successful you will receive the following message:

749

Configure WMI Permissions on all Remote Devices

  1. Open wmimgmt.msc
  2. Right-click WMI Control and navigate to Properties
  3. Select the Security tab
  4. Expand Root
  5. Highlight CIMV2 and click the Security button at the bottom-right of the window.
561
  1. Add the Liongard Service Account to the list of users with the permissions:
  • Execute Methods
  • Enable Account
  • Remote Enable
  • Read Security
  1. Press OK.
  2. Return to the Root tree and expand Microsoft > Windows.
  3. Highlight TaskScheduler and click the Security button at the bottom-right of the window.
  4. Add the Liongard Service Account to the list of users with the permissions:
  • Execute Methods
  • Enable Account
  • Remote Enable
  • Read Security
  1. Then press OK.
  2. Next, return to the Root tree and highlight StandardCimv2. Click the Security button at the bottom-right of the window.
  3. Add the Liongard Service Account to the list of users with the permissions:
  • Execute Methods
  • Enable Account
  • Remote Enable
  • Read Security**
  1. Then press OK.
  2. Once these steps are complete, the Agent will be able to run remote Windows inspections.

Permissions Necessary for Hyper-V Inspections

Non-Domain Joined Hyper-V servers:

  1. On the Hyper-V Host machine, open the Windows Computer Management Console and create a standard local user
  2. Add the user account to the local group Hyper-V Administrators
1069
  1. Install the Liongard Agent on the Host machine using the newly created local user. For the Windows MSI installation guide click Here.

Domain Joined Hyper-V Servers

  1. If you haven't already, install the Liongard Agent on a Domain Controller in your network. Agent MSI Installation
  2. Add your Liongard domain user service account to the local groups "Remote Management Users" and "Hyper-V Administrators" on the Hyper-V Host Machine
  3. Open Command Prompt and run GPUpdate on both the Hyper-V host and the Agent host.

Inspector FAQs