Change ownership of registry key through command-line

Discussion in 'Windows 10' started by Magmarock, Jul 6, 2019.

  1. Magmarock

    Magmarock MDL Junior Member

    Oct 12, 2016
    55
    11
    0
    Hi there. I'm writing a sensitive script and I need to be able to change ownership and permissions of specific registry keys. The script in question will work like an on & off switch. Every time it's used it must grant itself permission to change a value in the registry, then lock it down to prevent other programs and the user from altering the settings.

    This must all be done without the use of 3rd tools. I can use regini to restrict access, but retaking access afterwards is where I run into problems. Does anyone know how to do this?
     
  2. TairikuOkami

    TairikuOkami MDL Addicted

    Mar 15, 2014
    849
    751
    30
    That can not be done, you can use powershell at best, but not CMD, you can not even remove folders from a folder via CMD, it is very limited, besides it is being deprecated and replaced by powershell.
     
  3. rpo

    rpo MDL Addicted

    Jan 3, 2010
    855
    632
    30
    You can adapt the code to your need, it's up to you.
     
  4. Magmarock

    Magmarock MDL Junior Member

    Oct 12, 2016
    55
    11
    0
    I'm not very experienced with powershell. What's the code I need?
     
  5. rpo

    rpo MDL Addicted

    Jan 3, 2010
    855
    632
    30
    I just give you a powershell script example. I use it to take ownership of the "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\UserSwitch".
    Code:
    ## 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 rele);
      [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 TOKEN_QUERY = 0x00000008;
      internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
      public static bool EnablePrivilege(long processHandle, string privilege) {
        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;
        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;
      }
    }
    '@
    # Take ownership privilege
    $ProcessHandle = (Get-Process -id $pid).Handle
    $type = Add-Type $definition -PassThru
    $max_retry=10
    for ($i=1; $i -le $max_retry;$i++){
        $status=$type[0]::EnablePrivilege($processHandle, "SeTakeOwnershipPrivilege")
        if ($status){break}
        if ($i -eq $max_retry){read-host "Unable to take ownership privilege";exit}
        start-sleep 1|out-null
        }
    #
    $keypath="SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\UserSwitch"
    #
    # Get localized admin group name
    $admin=(get-wmiobject win32_group| Where-Object {$_.sid -eq "s-1-5-32-544"}).name
    # Change Owner to the local Administrators group
    $regKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("$keypath", "ReadWriteSubTree", "TakeOwnership")
    $regACL = $regKey.GetAccessControl()
    $regACL.SetOwner([System.Security.Principal.NTAccount]"$admin")
    $regKey.SetAccessControl($regACL)
    # Change Permissions for the local Administrators group
    $regKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("$keypath", "ReadWriteSubTree", "ChangePermissions")
    $regACL = $regKey.GetAccessControl()
    $regRule = New-Object System.Security.AccessControl.RegistryAccessRule ("$admin","FullControl","ContainerInherit","None","Allow")
    $regACL.SetAccessRule($regRule)
    # Change Permissions for System
    $regRule = New-Object System.Security.AccessControl.RegistryAccessRule ("SYSTEM","SetValue","ContainerInherit","None","Deny")
    $regACL.SetAccessRule($regRule)
    $regKey.SetAccessControl($regACL)
    New-ItemProperty -Path "HKLM:\$keyPath" -Name "Enabled" -Value 1 -PropertyType DWORD -Force |out-null
    
    The Administrators group gets ownership of the key and gets "FullControl" of the key.
    The system account is no longer the owner of the key and "SetValue" is denied; it can read but no change.
     
  6. pf100

    pf100 MDL Expert

    Oct 22, 2010
    1,629
    2,330
    60
    I think I'm misunderstanding you. Surely you know you can remove folders from a folder with cmd. You can remove the UpdateAssistantV2 folder from the Windows folder like this:
    Code:
    takeown /f "%systemroot%\UpdateAssistantV2" /a
    icacls "%systemroot%\UpdateAssistantV2" /reset
    del %systemroot%\UpdateAssistantV2\*.* /f /q
    rmdir %systemroot%\UpdateAssistantV2 /s /q
    If it's not a system protected folder the first two lines of code aren't necessary.
    But I think you know all of this, so what did you mean?
     
  7. LiteOS

    LiteOS MDL Expert

    Mar 7, 2014
    1,636
    700
    60
    Run the script in trusted installer account
     
  8. abbodi1406

    abbodi1406 MDL KB0000001

    Feb 19, 2011
    9,382
    34,958
    300
    I think he means removing all folders from a folder (i.e. all the content of a folder without knowing them)
    as example C:\Windows\SoftwareDistribution\Download\

    there are two ways that works
    the easy
    Code:
    pushd %systemroot%\SoftwareDistribution\Download
    rd /s /q .
    popd
    
    the pro
    Code:
    for /f "delims=" %i in ('dir /b /s /ad %systemroot%\SoftwareDistribution\Download 2^>nul') do rd /s /q "%~i"
    del /f /q %systemroot%\SoftwareDistribution\Download\*
     
  9. pf100

    pf100 MDL Expert

    Oct 22, 2010
    1,629
    2,330
    60
    I like the "dir /b /s /ad" trick to get subdirectory names in a "for" loop. I've never seen that before.
     
  10. TairikuOkami

    TairikuOkami MDL Addicted

    Mar 15, 2014
    849
    751
    30
    It does not work, when I am trying to remove desktop.
     
  11. Magmarock

    Magmarock MDL Junior Member

    Oct 12, 2016
    55
    11
    0
    The Powershell script doesn't work for me either.
     
  12. rpo

    rpo MDL Addicted

    Jan 3, 2010
    855
    632
    30
    Are you talking about the script i posted? It works for me as it is. You must run the the script with admin privileges and ensure you are allowed to run ps scripts.
     
  13. BAU

    BAU MDL Senior Member

    Feb 10, 2009
    353
    557
    10
  14. abbodi1406

    abbodi1406 MDL KB0000001

    Feb 19, 2011
    9,382
    34,958
    300
    @BAU

    RtlAdjustPrivilege don't work with TrustedInstaller owned
     
  15. BAU

    BAU MDL Senior Member

    Feb 10, 2009
    353
    557
    10
    How come? the powershell process gets the required access. After that, it's only the script to blame - which I did refactored and appears to work fine.
    Can you please give a non-working regkey example with the updated snippet that otherwise works after using something else (AdjustTokenPrivileges or DuplicateToken)?
    I do have a variant with DuplicateToken but that's a few lines more code and at this point is probably more convenient to just hijack trustedinstaller service
     
  16. abbodi1406

    abbodi1406 MDL KB0000001

    Feb 19, 2011
    9,382
    34,958
    300