Providing a SeServiceLogonRight to a PowerShell User

In the script I'm writing right now, I create a dedicated user to run some of the Windows services that we internally developed. To start these services, our “dedicated” user needs the SeServiceLogonRight privilege. I am currently assigning this privilege using ntrights.exe with the following call from my PowerShell script:

{.$global:RootInstallDir\..\Common\SupportTools\ntrights.exe -m $env:COMPUTERNAME -u $HealthLinkUser +r SeServiceLogonRight } 

However, this does not suit me. There should be a cleaner way to do this using PowerShell.

+6
source share
5 answers

It worked for me. You can decide which is cleaner ;-) The key is the LsaAddAccountRights API function.

 Add-Type @' using System; using System.Collections.Generic; using System.Text; namespace MyLsaWrapper { using System.Runtime.InteropServices; using System.Security; using System.Management; using System.Runtime.CompilerServices; using System.ComponentModel; using LSA_HANDLE = IntPtr; [StructLayout(LayoutKind.Sequential)] struct LSA_OBJECT_ATTRIBUTES { internal int Length; internal IntPtr RootDirectory; internal IntPtr ObjectName; internal int Attributes; internal IntPtr SecurityDescriptor; internal IntPtr SecurityQualityOfService; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct LSA_UNICODE_STRING { internal ushort Length; internal ushort MaximumLength; [MarshalAs(UnmanagedType.LPWStr)] internal string Buffer; } sealed class Win32Sec { [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute] internal static extern uint LsaOpenPolicy( LSA_UNICODE_STRING[] SystemName, ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, int AccessMask, out IntPtr PolicyHandle ); [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute] internal static extern uint LsaAddAccountRights( LSA_HANDLE PolicyHandle, IntPtr pSID, LSA_UNICODE_STRING[] UserRights, int CountOfRights ); [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute] internal static extern int LsaLookupNames2( LSA_HANDLE PolicyHandle, uint Flags, uint Count, LSA_UNICODE_STRING[] Names, ref IntPtr ReferencedDomains, ref IntPtr Sids ); [DllImport("advapi32")] internal static extern int LsaNtStatusToWinError(int NTSTATUS); [DllImport("advapi32")] internal static extern int LsaClose(IntPtr PolicyHandle); [DllImport("advapi32")] internal static extern int LsaFreeMemory(IntPtr Buffer); } /// <summary> /// This class is used to grant "Log on as a service", "Log on as a batchjob", "Log on localy" etc. /// to a user. /// </summary> public sealed class LsaWrapper : IDisposable { [StructLayout(LayoutKind.Sequential)] struct LSA_TRUST_INFORMATION { internal LSA_UNICODE_STRING Name; internal IntPtr Sid; } [StructLayout(LayoutKind.Sequential)] struct LSA_TRANSLATED_SID2 { internal SidNameUse Use; internal IntPtr Sid; internal int DomainIndex; uint Flags; } [StructLayout(LayoutKind.Sequential)] struct LSA_REFERENCED_DOMAIN_LIST { internal uint Entries; internal LSA_TRUST_INFORMATION Domains; } enum SidNameUse : int { User = 1, Group = 2, Domain = 3, Alias = 4, KnownGroup = 5, DeletedAccount = 6, Invalid = 7, Unknown = 8, Computer = 9 } enum Access : int { POLICY_READ = 0x20006, POLICY_ALL_ACCESS = 0x00F0FFF, POLICY_EXECUTE = 0X20801, POLICY_WRITE = 0X207F8 } const uint STATUS_ACCESS_DENIED = 0xc0000022; const uint STATUS_INSUFFICIENT_RESOURCES = 0xc000009a; const uint STATUS_NO_MEMORY = 0xc0000017; IntPtr lsaHandle; public LsaWrapper() : this(null) { } // // local system if systemName is null public LsaWrapper(string systemName) { LSA_OBJECT_ATTRIBUTES lsaAttr; lsaAttr.RootDirectory = IntPtr.Zero; lsaAttr.ObjectName = IntPtr.Zero; lsaAttr.Attributes = 0; lsaAttr.SecurityDescriptor = IntPtr.Zero; lsaAttr.SecurityQualityOfService = IntPtr.Zero; lsaAttr.Length = Marshal.SizeOf(typeof(LSA_OBJECT_ATTRIBUTES)); lsaHandle = IntPtr.Zero; LSA_UNICODE_STRING[] system = null; if (systemName != null) { system = new LSA_UNICODE_STRING[1]; system[0] = InitLsaString(systemName); } uint ret = Win32Sec.LsaOpenPolicy(system, ref lsaAttr, (int)Access.POLICY_ALL_ACCESS, out lsaHandle); if (ret == 0) return; if (ret == STATUS_ACCESS_DENIED) { throw new UnauthorizedAccessException(); } if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) { throw new OutOfMemoryException(); } throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); } public void AddPrivileges(string account, string privilege) { IntPtr pSid = GetSIDInformation(account); LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1]; privileges[0] = InitLsaString(privilege); uint ret = Win32Sec.LsaAddAccountRights(lsaHandle, pSid, privileges, 1); if (ret == 0) return; if (ret == STATUS_ACCESS_DENIED) { throw new UnauthorizedAccessException(); } if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) { throw new OutOfMemoryException(); } throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); } public void Dispose() { if (lsaHandle != IntPtr.Zero) { Win32Sec.LsaClose(lsaHandle); lsaHandle = IntPtr.Zero; } GC.SuppressFinalize(this); } ~LsaWrapper() { Dispose(); } // helper functions IntPtr GetSIDInformation(string account) { LSA_UNICODE_STRING[] names = new LSA_UNICODE_STRING[1]; LSA_TRANSLATED_SID2 lts; IntPtr tsids = IntPtr.Zero; IntPtr tdom = IntPtr.Zero; names[0] = InitLsaString(account); lts.Sid = IntPtr.Zero; Console.WriteLine("String account: {0}", names[0].Length); int ret = Win32Sec.LsaLookupNames2(lsaHandle, 0, 1, names, ref tdom, ref tsids); if (ret != 0) throw new Win32Exception(Win32Sec.LsaNtStatusToWinError(ret)); lts = (LSA_TRANSLATED_SID2)Marshal.PtrToStructure(tsids, typeof(LSA_TRANSLATED_SID2)); Win32Sec.LsaFreeMemory(tsids); Win32Sec.LsaFreeMemory(tdom); return lts.Sid; } static LSA_UNICODE_STRING InitLsaString(string s) { // Unicode strings max. 32KB if (s.Length > 0x7ffe) throw new ArgumentException("String too long"); LSA_UNICODE_STRING lus = new LSA_UNICODE_STRING(); lus.Buffer = s; lus.Length = (ushort)(s.Length * sizeof(char)); lus.MaximumLength = (ushort)(lus.Length + sizeof(char)); return lus; } } public class LsaWrapperCaller { public static void AddPrivileges(string account, string privilege) { using (LsaWrapper lsaWrapper = new LsaWrapper()) { lsaWrapper.AddPrivileges(account, privilege); } } } } '@ [MyLsaWrapper.LsaWrapperCaller]::AddPrivileges("andy", "SeServiceLogonRight") 
+6
source

It really should be a comment on Andy, but I do not have the comments needed for comments.

The code above will have access to freed memory, which may "work" (sometimes), but always a bad practice. See my answer to another question about a problem with the same code: Why does LsaAddAccountRights return STATUS_INVALID_PARAMETER?

It seems that this code has been passing for several years.

+6
source

Basically, the same P / Invoke method for using C # to call system DLL routines, but this example might be a bit simpler:

 $code = @" using System; using System.Runtime.InteropServices; public class TokenManipulator { [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); [DllImport("kernel32.dll", ExactSpelling = true)] internal static extern IntPtr GetCurrentProcess(); [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok); [DllImport("advapi32.dll", SetLastError = true)] internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct TokPriv1Luid { public int Count; public long Luid; public int Attr; } internal const int SE_PRIVILEGE_DISABLED = 0x00000000; internal const int SE_PRIVILEGE_ENABLED = 0x00000002; internal const int TOKEN_QUERY = 0x00000008; internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; public static bool AddPrivilege(string privilege) { try { bool retVal; TokPriv1Luid tp; IntPtr hproc = GetCurrentProcess(); IntPtr htok = IntPtr.Zero; retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); tp.Count = 1; tp.Luid = 0; tp.Attr = SE_PRIVILEGE_ENABLED; retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid); retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); return retVal; } catch (Exception ex) { throw ex; } } public static bool RemovePrivilege(string privilege) { try { bool retVal; TokPriv1Luid tp; IntPtr hproc = GetCurrentProcess(); IntPtr htok = IntPtr.Zero; retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); tp.Count = 1; tp.Luid = 0; tp.Attr = SE_PRIVILEGE_DISABLED; retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid); retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); return retVal; } catch (Exception ex) { throw ex; } } } "@ add-type $code "`nInitial privileges" whoami /priv | Select-String "SeRestorePrivilege" whoami /priv | Select-String "SeBackupPrivilege" whoami /priv | Select-String "SeTakeOwnershipPrivilege" "`nAdding privileges" #Activate necessary admin privileges to make changes without NTFS perms [void][TokenManipulator]::AddPrivilege("SeRestorePrivilege") #Necessary to set Owner Permissions [void][TokenManipulator]::AddPrivilege("SeBackupPrivilege") #Necessary to bypass Traverse Checking [void][TokenManipulator]::AddPrivilege("SeTakeOwnershipPrivilege") #Necessary to override FilePermissions whoami /priv | Select-String "SeRestorePrivilege" whoami /priv | Select-String "SeBackupPrivilege" whoami /priv | Select-String "SeTakeOwnershipPrivilege" "`nRemoving privileges just added before terminating" [void][TokenManipulator]::AddPrivilege("SeRestorePrivilege") #Necessary to set Owner Permissions [void][TokenManipulator]::AddPrivilege("SeBackupPrivilege") #Necessary to bypass Traverse Checking [void][TokenManipulator]::AddPrivilege("SeTakeOwnershipPrivilege") #Necessary to override whoami /priv | Select-String "SeRestorePrivilege" whoami /priv | Select-String "SeBackupPrivilege" whoami /priv | Select-String "SeTakeOwnershipPrivilege" 

This confused me a bit, and there are many examples of forum topics that ended with the “use a third-party application” application. It would be nice if .Net included this necessary function, but despite the length of this sample code, it’s quite easy to reproduce :) Greetings, Barnaby

+3
source

This script contains functions that work fine and without any dependencies:

https://gallery.technet.microsoft.com/scriptcenter/Grant-Revoke-Query-user-26e259b0

In addition, here is a specific solution to your problem:

 function Add-ServiceLogonRight([string] $Username) { Write-Host "Enable ServiceLogonRight for $Username" $tmp = New-TemporaryFile secedit /export /cfg "$tmp.inf" | Out-Null (gc -Encoding ascii "$tmp.inf") -replace '^SeServiceLogonRight .+', "`$0,$Username" | sc -Encoding ascii "$tmp.inf" secedit /import /cfg "$tmp.inf" /db "$tmp.sdb" | Out-Null secedit /configure /db "$tmp.sdb" /cfg "$tmp.inf" | Out-Null rm $tmp* -ea 0 } 
+3
source

Here's how I solved it:

Based on: this article

You can download carbon here

First import the 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
source

Source: https://habr.com/ru/post/913396/


All Articles