Powershell: set Registrykey owner to SYSTEM user

2

I have to add an exclusions path to Windows Defender registry key. I am aware that there are Cmdlets provided from Windows Defender to use them directly for these kinds of purposes. But unfortunately, in Windows 7 and Powershell v2, they are not available. So I am trying to build a script that will manually enter the value to the registry key. By researching online, I have put together a script which will first change the owner to Administrators, then add the value and finally set the owner to the initial owner again. My code is given below:

Code to enable the required privilege:

  param([string]$targetPath)

  function enable-privilege {
   param(
    ## The privilege to adjust. This set is taken from
    ## http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx
    [ValidateSet(
     "SeAssignPrimaryTokenPrivilege", "SeAuditPrivilege", "SeBackupPrivilege",
     "SeChangeNotifyPrivilege", "SeCreateGlobalPrivilege", "SeCreatePagefilePrivilege",
     "SeCreatePermanentPrivilege", "SeCreateSymbolicLinkPrivilege", "SeCreateTokenPrivilege",
     "SeDebugPrivilege", "SeEnableDelegationPrivilege", "SeImpersonatePrivilege", "SeIncreaseBasePriorityPrivilege",
     "SeIncreaseQuotaPrivilege", "SeIncreaseWorkingSetPrivilege", "SeLoadDriverPrivilege",
     "SeLockMemoryPrivilege", "SeMachineAccountPrivilege", "SeManageVolumePrivilege",
     "SeProfileSingleProcessPrivilege", "SeRelabelPrivilege", "SeRemoteShutdownPrivilege",
     "SeRestorePrivilege", "SeSecurityPrivilege", "SeShutdownPrivilege", "SeSyncAgentPrivilege",
     "SeSystemEnvironmentPrivilege", "SeSystemProfilePrivilege", "SeSystemtimePrivilege",
     "SeTakeOwnershipPrivilege", "SeTcbPrivilege", "SeTimeZonePrivilege", "SeTrustedCredManAccessPrivilege",
     "SeUndockPrivilege", "SeUnsolicitedInputPrivilege")]
    $Privilege,
    ## The process on which to adjust the privilege. Defaults to the current process.
    $ProcessId = $pid,
    ## Switch to disable the privilege, rather than enable it.
    [Switch] $Disable
   )

   ## Taken from P/Invoke.NET with minor adjustments.
   $definition = @'
   using System;
   using System.Runtime.InteropServices;

   public class AdjPriv
   {
    [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("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_ENABLED = 0x00000002;
    internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
    internal const int TOKEN_QUERY = 0x00000008;
    internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
    public static bool EnablePrivilege(long processHandle, string privilege, bool disable)
    {
     bool retVal;
     TokPriv1Luid tp;
     IntPtr hproc = new IntPtr(processHandle);
     IntPtr htok = IntPtr.Zero;
     retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
     tp.Count = 1;
     tp.Luid = 0;
     if(disable)
     {
      tp.Attr = SE_PRIVILEGE_DISABLED;
     }
     else
     {
      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;
    }
   }
  '@

   $processHandle = (Get-Process -id $ProcessId).Handle
   $type = Add-Type $definition -PassThru
   $type[0]::EnablePrivilege($processHandle, $Privilege, $Disable)
  }

The code part where I perfrom the changes:

  function getRegKeyOwner([string]$keyPath){
      $regRights=[System.Security.AccessControl.RegistryRights]::ReadPermissions
      $permCheck=[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree

      $Key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath,$permCheck,$regRights)
      $acl = $Key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::Owner)
      $owner = $acl.GetOwner([type]::GetType([System.Security.Principal.NTAccount]))
      $key.Close()
      return $owner
  }


  function setValueToKey([string]$keyPath, [string]$name, [System.Object]$value,[Microsoft.Win32.RegistryValueKind]$regValueKind){
      $regRights=[System.Security.AccessControl.RegistryRights]::SetValue
      $permCheck=[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree

      $Key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath,$permCheck,$regRights)

      "Setting value with properties [name:$name, value:$value, value type:$regValueKind]"
      $Key.SetValue($name, $value, $regValueKind)
      $key.Close()
  }

  function changeRegKeyOwner([string]$keyPath, [System.Security.Principal.NTAccount]$user){
      try{    
          $regRights=[System.Security.AccessControl.RegistryRights]::TakeOwnership
          $permCheck=[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree
          $key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($keyPath,$permCheck,$regRights)
          # You must get a blank acl for the key b/c you do not currently have access
          $acl = $key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::None)

          if([string]::IsNullOrEmpty($user)){
              $user = [System.Security.Principal.NTAccount]"$env:userdomain\$env:username"
          }

          "Changing owner of Registry key: HKEY_LOCAL_MACHINE\$keyPath to `"$user`""
          $acl.SetOwner($user)
          $key.SetAccessControl($acl)
      }
      catch {
          $_.Exception.toString()
          $key.Close()
          return
      }
      giveFullControlToUser -userName "$user" -key $key

      $key.Close()
  }

  function giveFullControlToUser([String]$userName,[Microsoft.Win32.RegistryKey] $key){
      "giving full access to $userName for key $key"
      # After you have set owner you need to get the acl with the perms so you can modify it.
      $acl = $key.GetAccessControl()
      $rule = New-Object System.Security.AccessControl.RegistryAccessRule ($userName,"FullControl", @("ObjectInherit","ContainerInherit"),"None","Allow")
      $acl.SetAccessRule($rule)
      $key.SetAccessControl($acl)
  }

  function getAdminUser {
      $windowsKey = "SOFTWARE\Microsoft\Windows"
      return getRegKeyOwner -keyPath $windowsKey
  }


  enable-privilege SeTakeOwnershipPrivilege 

  $exclussionsPathsKey="SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths"
  $adminGroupName  = gwmi win32_group -filter "LocalAccount = $TRUE And SID = 'S-1-5-32-544'" | select -expand name

  $originalOwner= getRegKeyOwner -keyPath $exclussionsPathsKey

  "original Owner to the key `"$exclussionsPathsKey`" is: `"$originalOwner`""
  changeRegKeyOwner -keyPath $exclussionsPathsKey -user ([System.Security.Principal.NTAccount]"$adminGroupName")

  if (!([string]::IsNullOrEmpty($targetPath))){
      $valueName=$targetPath
      $vaue=0
      $regValueKind=[Microsoft.Win32.RegistryValueKind]::DWord
      setValueToKey -keyPath $exclussionsPathsKey -name $valueName -value $vaue -regValueKind $regValueKind 
  }

  changeRegKeyOwner -keyPath $exclussionsPathsKey -user $originalOwner

But until the value setting part, everything works fine. Only when I try to set the original owner again, I get the following exception. This is my first time scripting with Powershell. And I am totally unable to understand/ solve the problem.

  True
  original Owner to the key "SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths" is: "NT AUTHORITY\SYSTEM"
  Changing owner of Registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths to "Administrators"
  giving full access to Administrators for key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths
  Changing owner of Registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths to "NT AUTHORITY\SYSTEM"
  System.Management.Automation.MethodInvocationException: Exception calling "SetAccessControl" with "1" argument(s): "The security identifier is not allowed to 
  be the owner of this object." ---> System.InvalidOperationException: The security identifier is not allowed to be the owner of this object.
     at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, SafeHandle handle, AccessControlSections includeSections, Object exceptionContex
  t)
     at System.Security.AccessControl.NativeObjectSecurity.Persist(SafeHandle handle, AccessControlSections includeSections, Object exceptionContext)
     at System.Security.AccessControl.NativeObjectSecurity.Persist(SafeHandle handle, AccessControlSections includeSections)
     at System.Security.AccessControl.RegistrySecurity.Persist(SafeRegistryHandle hKey, String keyName)
     at Microsoft.Win32.RegistryKey.SetAccessControl(RegistrySecurity registrySecurity)
     at SetAccessControl(Object , Object[] )
     at System.Management.Automation.MethodInformation.Invoke(Object target, Object[] arguments)
     at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[] origin
  alArguments)
     --- End of inner exception stack trace ---
     at System.Management.Automation.DotNetAdapter.AuxiliaryMethodInvoke(Object target, Object[] arguments, MethodInformation methodInformation, Object[] origin
  alArguments)
     at System.Management.Automation.ParserOps.CallMethod(Token token, Object target, String methodName, Object[] paramArray, Boolean callStatic, Object valueTo
  Set)
     at System.Management.Automation.MethodCallNode.InvokeMethod(Object target, Object[] arguments, Object value)
     at System.Management.Automation.MethodCallNode.Execute(Array input, Pipe outputPipe, ExecutionContext context)
     at System.Management.Automation.ParseTreeNode.Execute(Array input, Pipe outputPipe, ArrayList& resultList, ExecutionContext context)
     at System.Management.Automation.StatementListNode.ExecuteStatement(ParseTreeNode statement, Array input, Pipe outputPipe, ArrayList& resultList, ExecutionC
  ontext context)
powershell
access-control-list
windows-registry
asked on Server Fault Jul 10, 2017 by ShaDooW • edited Jul 13, 2017 by ShaDooW

1 Answer

0

Finally, I have found what was missing in the script. The Administrator user needed extra privilege for restoring the privilege. Just a simple call to the function enable-privilege SeRestorePrivilege before setting the original owner back, gives the current process the required permission.

answered on Server Fault Jul 13, 2017 by ShaDooW

User contributions licensed under CC BY-SA 3.0