locked
Powershell: set Registry key owner to the SYSTEM user throws error RRS feed

  • Question

  • 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 Win-7 and Powershell v-1, 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)

    Monday, July 10, 2017 1:15 PM

All replies

  • The registry key cannot be edited or changed as it is a protected key.  It is owned and protected by the Installer.  Any attempt to change the security will be rejected.

    You can use a GPO to set this key.


    \_(ツ)_/

    Monday, July 10, 2017 1:21 PM
  • The registry key change does work. I can change the owner and set value. And the part of setting back to the previous owner does work if the previous owner was not the SYSTEM user. 

    Btw what does GPO mean? Group Policy?

    Monday, July 10, 2017 1:26 PM
  • Hi,

    Group Policy is simply the easiest way to reach out and configure computer and user settings on networks based on Active Directory Domain Services (AD DS). If your business is not using Group Policy, you are missing a huge opportunity to reduce costs, control configurations, keep users productive and happy, and harden security. Think of Group Policy as “touch once, configure many.”

    Best Regards,

    Frank


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    Friday, July 14, 2017 9:10 AM
  • Hi,
    Just checking in to see if the information provided was helpful. Please let us know if you would like further assistance.

    Best Regards,
    Frank

    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    If you have feedback for TechNet Subscriber Support, contact tnmff@microsoft.com.

    Friday, July 21, 2017 7:47 AM