Fix TrustedInstaller-owned registry key write via token privileges

- 03-system-registry.ps1: replace .NET OpenSubKey approach with proper
  P/Invoke that enables SeTakeOwnershipPrivilege and SeRestorePrivilege
  before attempting to take ownership of TrustedInstaller-owned keys
  (e.g. HKLM\...\Communications\ConfigureChatAutoInstall)
- Remove SYSTEM scheduled task fallback (not needed with token approach)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
X9 2026-03-14 20:06:01 +01:00
parent d853df0aa4
commit 3a3513c3bc

View file

@ -11,11 +11,41 @@ function Write-Log {
Add-Content -Path $LogFile -Value $line -Encoding UTF8 Add-Content -Path $LogFile -Value $line -Encoding UTF8
} }
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class RegPrivilege {
[DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)]
static extern bool AdjustTokenPrivileges(IntPtr htok, bool disAll, ref TokPriv1Luid newState, int len, IntPtr prev, IntPtr relen);
[DllImport("kernel32.dll", ExactSpelling=true)]
static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)]
static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError=true)]
static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[StructLayout(LayoutKind.Sequential, Pack=1)]
struct TokPriv1Luid { public int Count; public long Luid; public int Attr; }
const int TOKEN_QUERY = 0x8;
const int TOKEN_ADJUST = 0x20;
const int SE_PRIVILEGE_ENABLED = 2;
public static bool Enable(string privilege) {
IntPtr htok = IntPtr.Zero;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST | TOKEN_QUERY, ref htok)) return false;
TokPriv1Luid tp; tp.Count = 1; tp.Luid = 0; tp.Attr = SE_PRIVILEGE_ENABLED;
if (!LookupPrivilegeValue(null, privilege, ref tp.Luid)) return false;
return AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
}
}
"@ -ErrorAction SilentlyContinue
function Grant-RegWriteAccess { function Grant-RegWriteAccess {
param([string]$Path) param([string]$Path)
# Grants Administrators FullControl on a registry key that has restricted ACL. # Grants Administrators FullControl on a TrustedInstaller-owned registry key.
# Required for keys owned by TrustedInstaller or with locked-down ACL. # Enables SeTakeOwnershipPrivilege + SeRestorePrivilege to override ACL.
try { try {
[RegPrivilege]::Enable("SeTakeOwnershipPrivilege") | Out-Null
[RegPrivilege]::Enable("SeRestorePrivilege") | Out-Null
$hive = $Path -replace '^(HKLM|HKCU|HKU|HKCR|HKCC):\\.*', '$1' $hive = $Path -replace '^(HKLM|HKCU|HKU|HKCR|HKCC):\\.*', '$1'
$subkey = $Path -replace '^(HKLM|HKCU|HKU|HKCR|HKCC):\\', '' $subkey = $Path -replace '^(HKLM|HKCU|HKU|HKCR|HKCC):\\', ''
$rootKey = switch ($hive) { $rootKey = switch ($hive) {
@ -23,17 +53,22 @@ function Grant-RegWriteAccess {
"HKCU" { [Microsoft.Win32.Registry]::CurrentUser } "HKCU" { [Microsoft.Win32.Registry]::CurrentUser }
"HKCR" { [Microsoft.Win32.Registry]::ClassesRoot } "HKCR" { [Microsoft.Win32.Registry]::ClassesRoot }
} }
$rights = [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree
$regRights = [System.Security.AccessControl.RegistryRights]::TakeOwnership # Take ownership (requires SeTakeOwnershipPrivilege)
$key = $rootKey.OpenSubKey($subkey, $rights, $regRights) $key = $rootKey.OpenSubKey($subkey,
[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,
[System.Security.AccessControl.RegistryRights]::TakeOwnership)
if ($key) { if ($key) {
$acl = $key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::None) $acl = $key.GetAccessControl([System.Security.AccessControl.AccessControlSections]::None)
$acl.SetOwner([System.Security.Principal.NTAccount]"BUILTIN\Administrators") $acl.SetOwner([System.Security.Principal.NTAccount]"BUILTIN\Administrators")
$key.SetAccessControl($acl) $key.SetAccessControl($acl)
$key.Close() $key.Close()
} }
# Re-open with ChangePermissions to grant full control
$key = $rootKey.OpenSubKey($subkey, $rights, [System.Security.AccessControl.RegistryRights]::ChangePermissions) # Grant FullControl to Administrators (requires ChangePermissions)
$key = $rootKey.OpenSubKey($subkey,
[Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree,
[System.Security.AccessControl.RegistryRights]::ChangePermissions)
if ($key) { if ($key) {
$acl = $key.GetAccessControl() $acl = $key.GetAccessControl()
$rule = New-Object System.Security.AccessControl.RegistryAccessRule( $rule = New-Object System.Security.AccessControl.RegistryAccessRule(
@ -41,12 +76,12 @@ function Grant-RegWriteAccess {
[System.Security.AccessControl.RegistryRights]::FullControl, [System.Security.AccessControl.RegistryRights]::FullControl,
[System.Security.AccessControl.InheritanceFlags]"ContainerInherit,ObjectInherit", [System.Security.AccessControl.InheritanceFlags]"ContainerInherit,ObjectInherit",
[System.Security.AccessControl.PropagationFlags]::None, [System.Security.AccessControl.PropagationFlags]::None,
[System.Security.AccessControl.AccessControlType]::Allow [System.Security.AccessControl.AccessControlType]::Allow)
)
$acl.SetAccessRule($rule) $acl.SetAccessRule($rule)
$key.SetAccessControl($acl) $key.SetAccessControl($acl)
$key.Close() $key.Close()
} }
Write-Log " ACL fixed for $Path" -Level INFO
} }
catch { catch {
Write-Log " Grant-RegWriteAccess failed for $Path - $_" -Level WARN Write-Log " Grant-RegWriteAccess failed for $Path - $_" -Level WARN