PowerShell: Adding Privileges to a User
For PowerShell on Windows, adding a security privilege to a user takes more effort than one would expect. Here we show a method with no third party dependencies.
Discussion
Basically we use PowerShell’s Add-Type
cmdlet to add .NET (C#) code which
calls a Win32 API function (LsaAddAccountRights
) to the script’s context.
We then instantiate a wrapper class and call a method which calls the
LsaAddAccountRights
function (with necessary setup and cleanup).
The PowerShell bits are taken from An answer on Stack Overflow, and the Win32 code is taken from Another answer on Stack Overflow.
The original variation of this code was found at http://www.hightechtalks.com/csharp/lsa-functions-276626.html
which no longer exists but can be found on the WayBack Machine
at
https://web.archive.org/web/20151218044501/http://www.hightechtalks.com/csharp/lsa-functions-276626.html
A discussion of the hitechtalks code which I found helpful is at https://www.roelvanlisdonk.nl/2010/03/24/how-to-grant-log-on-as-a-service-rights-to-an-user-account-using-powershell/.
A handy list of of UserRights can be found here: https://blakedrumm.com/blog/set-and-check-user-rights-assignment/#set-user-rights, and is reproduced below.
PowerShell
This bit in the code below should be edited to suit your script, and all of the code should be in a Try/Catch block.
Write-Host -NoNewline "Denying interactive (desktop) login with account SomeUser..."
[AnLsaSecurityWrapper.LsaWrapperCaller]::AddPrivileges("SomeUser", "SeDenyInteractiveLogonRight")
Write-Host "done."
Full code
# Code from https://www.wildtechgarden.com/doc/sysadmin-devops/powershell-on-windows-adding-security-privileges-to-a-user
# Which is based on PowerShell from https://stackoverflow.com/a/14469248 and .NET
# code from https://stackoverflow.com/a/10188472
# Code adds a Win32 API class to PowerShell so we can use one
# of its methods (LsaAddAccountRights) to add the
# SeDenyInteractiveLogonRight to a user which only exists for
# background processing
Add-Type @'
using System;
namespace AnLsaSecurityWrapper
{
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Principal;
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")]
internal static extern int LsaNtStatusToWinError(int NTSTATUS);
[DllImport("advapi32")]
internal static extern int LsaClose(IntPtr PolicyHandle);
}
sealed class Sid : IDisposable
{
public IntPtr pSid = IntPtr.Zero;
public SecurityIdentifier sid = null;
public Sid(string account)
{
sid = (SecurityIdentifier) (new NTAccount(account)).Translate(typeof(SecurityIdentifier));
Byte[] buffer = new Byte[sid.BinaryLength];
sid.GetBinaryForm(buffer, 0);
pSid = Marshal.AllocHGlobal(sid.BinaryLength);
Marshal.Copy(buffer, 0, pSid, sid.BinaryLength);
}
public void Dispose()
{
if (pSid != IntPtr.Zero)
{
Marshal.FreeHGlobal(pSid);
pSid = IntPtr.Zero;
}
GC.SuppressFinalize(this);
}
~Sid()
{
Dispose();
}
}
public sealed class LsaWrapper : IDisposable
{
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)
{
uint ret = 0;
using (Sid sid = new Sid(account))
{
LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1];
privileges[0] = InitLsaString(privilege);
ret = Win32Sec.LsaAddAccountRights(lsaHandle, sid.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
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);
}
}
}
}
'@
Write-Host -NoNewline "Denying interactive (desktop) login with account SomeUser..."
[AnLsaSecurityWrapper.LsaWrapperCaller]::AddPrivileges("SomeUser", "SeDenyInteractiveLogonRight")
Write-Host "done."
List of UserRights (privileges) which can be added
Privilege | PrivilegeName |
---|---|
SeAssignPrimaryTokenPrivilege | Replace a process level token |
SeAuditPrivilege | Generate security audits |
SeBackupPrivilege | Back up files and directories |
SeBatchLogonRight | Log on as a batch job |
SeChangeNotifyPrivilege | Bypass traverse checking |
SeCreateGlobalPrivilege | Create global objects |
SeCreatePagefilePrivilege | Create a pagefile |
SeCreatePermanentPrivilege | Create permanent shared objects |
SeCreateSymbolicLinkPrivilege | Create symbolic links |
SeCreateTokenPrivilege | Create a token object |
SeDebugPrivilege | Debug programs |
SeDelegateSessionUserImpersonatePrivilege | Obtain an impersonation token for another user in the same session |
SeDenyBatchLogonRight | Deny log on as a batch job |
SeDenyInteractiveLogonRight | Deny log on locally |
SeDenyNetworkLogonRight | Deny access to this computer from the network |
SeDenyRemoteInteractiveLogonRight | Deny log on through Remote Desktop Services |
SeDenyServiceLogonRight | Deny log on as a service |
SeEnableDelegationPrivilege | Enable computer and user accounts to be trusted for delegation |
SeImpersonatePrivilege | Impersonate a client after authentication |
SeIncreaseBasePriorityPrivilege | Increase scheduling priority |
SeIncreaseQuotaPrivilege | Adjust memory quotas for a process |
SeIncreaseWorkingSetPrivilege | Increase a process working set |
SeInteractiveLogonRight | Allow log on locally |
SeLoadDriverPrivilege | Load and unload device drivers |
SeLockMemoryPrivilege | Lock pages in memory |
SeMachineAccountPrivilege | Add workstations to domain |
SeManageVolumePrivilege | Perform volume maintenance tasks |
SeNetworkLogonRight | Access this computer from the network |
SeProfileSingleProcessPrivilege | Profile single process |
SeRelabelPrivilege | Modify an object label |
SeRemoteInteractiveLogonRight | Allow log on through Remote Desktop Services |
SeRemoteShutdownPrivilege | Force shutdown from a remote system |
SeRestorePrivilege | Restore files and directories |
SeSecurityPrivilege | Manage auditing and security log |
SeServiceLogonRight | Log on as a service |
SeShutdownPrivilege | Shut down the system |
SeSyncAgentPrivilege | Synchronize directory service data |
SeSystemEnvironmentPrivilege | Modify firmware environment values |
SeSystemProfilePrivilege | Profile system performance |
SeSystemtimePrivilege | Change the system time |
SeTakeOwnershipPrivilege | Take ownership of files or other objects |
SeTcbPrivilege | Act as part of the operating system |
SeTimeZonePrivilege | Change the time zone |
SeTrustedCredManAccessPrivilege | Access Credential Manager as a trusted caller |
SeUndockPrivilege | Remove computer from docking stations |
[From https://blakedrumm.com/blog/set-and-check-user-rights-assignment/#set-user-rights]