无法从 ACL 中删除 ACE

Unable to remove ACE from ACL

我们想要完全清除文件夹的 NTFS ACL,并且只用 ACE Builtin\Administrator : Full Control 填充 ACL。下面的代码适用于大多数文件夹,但在一个特定文件夹上失败:

$Path = 'E:\DEPARTMENTS\Gemensam'

$BuiltinAdmin = [System.Security.Principal.NTAccount]'Builtin\Administrators'
$AdminFullControlAce = New-Object System.Security.AccessControl.FileSystemAccessRule(
    $BuiltinAdmin,
    [System.Security.AccessControl.FileSystemRights]::FullControl,
    [System.Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit',
    [System.Security.AccessControl.PropagationFlags]::None,
    [System.Security.AccessControl.AccessControlType]::Allow
)

(Get-Acl $Path).Access

$FolderItem = Get-Item -Path $Path -EA Stop
$Acl = $FolderItem.GetAccessControl()

Write-Verbose 'Set owner'
$Acl.SetOwner($BuiltinAdmin)
$FolderItem.SetAccessControl($Acl)

Write-Verbose 'Disable inheritance'
$Acl = $FolderItem.GetAccessControl()
$Acl.SetAccessRuleProtection($True, $False)
$FolderItem.SetAccessControl($Acl)

Write-Verbose 'Remove all ACEs from the ACL'
$Acl = $FolderItem.GetAccessControl()
$Acl.Access.ForEach({$Acl.RemoveAccessRule($_)})

Write-Verbose 'Add Admin and set the new ACL'
$acl.AddAccessRule($AdminFullControlAce)
$FolderItem.SetAccessControl($Acl)

Write-Verbose 'ACL corrected'
(Get-Acl $Path).Access

这段代码的输出是:

FileSystemRights  : FullControl
AccessControlType : Allow
IdentityReference : BUILTIN\Administrators
IsInherited       : False
InheritanceFlags  : ContainerInherit, ObjectInherit
PropagationFlags  : None

FileSystemRights  : Modify, Synchronize
AccessControlType : Allow
IdentityReference : GROUPHC\SWE CEM KVB EV
IsInherited       : False
InheritanceFlags  : ContainerInherit, ObjectInherit
PropagationFlags  : None

VERBOSE: Set owner
VERBOSE: Disable inheritance
VERBOSE: Remove all ACEs from the ACL
True
True
VERBOSE: Add Admin and set the new ACL
VERBOSE: ACL corrected
FileSystemRights  : FullControl
AccessControlType : Allow
IdentityReference : BUILTIN\Administrators
IsInherited       : False
InheritanceFlags  : ContainerInherit, ObjectInherit
PropagationFlags  : None

FileSystemRights  : Modify, Synchronize
AccessControlType : Allow
IdentityReference : GROUPHC\SWE CEM KVB EV
IsInherited       : False
InheritanceFlags  : ContainerInherit, ObjectInherit
PropagationFlags  : None

出于某种原因,似乎无法删除 GROUPHC\SWE CEM KVB EVACE。即使 Get-ACLSet-ACL it doesn't work. We've also tried to push the ACL after every change as indicated here, but that doesn't work either. According to the docs 继承被正确删除,所以它不能是继承的 ACE.

如有任何帮助,我们将不胜感激。

为避免所有权问题,我们首先 运行 以下代码:

#region Get super powers
    $AdjustTokenPrivileges = @"
using System;
using System.Runtime.InteropServices;

public class TokenManipulator
{
[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("kernel32.dll", ExactSpelling = true)]
internal static extern IntPtr GetCurrentProcess();
[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_DISABLED = 0x00000000;
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
public static bool AddPrivilege(string privilege)
{
try
{
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = GetCurrentProcess();
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;
}
catch (Exception ex)
{
throw ex;
}
}
public static bool RemovePrivilege(string privilege)
{
try
{
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero;
retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_DISABLED;
retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
return retVal;
}
catch (Exception ex)
{
throw ex;
}
}
}
"@

Try {
    Write-Verbose 'Get super powers'
    Add-Type $AdjustTokenPrivileges
    [void][TokenManipulator]::AddPrivilege('SeRestorePrivilege')
    [void][TokenManipulator]::AddPrivilege('SeBackupPrivilege')
    [void][TokenManipulator]::AddPrivilege('SeTakeOwnershipPrivilege')
}
Catch {
    throw "Failed getting super powers: $_"
}
#endregion

看我上面的评论:

Import-Module PSCX

Set-Privilege (new-object Pscx.Interop.TokenPrivilege "SeRestorePrivilege", $true) # Necessary to set Owner Permissions
Set-Privilege (new-object Pscx.Interop.TokenPrivilege "SeBackupPrivilege", $true) # Necessary to bypass Traverse Checking
Set-Privilege (new-object Pscx.Interop.TokenPrivilege "SeTakeOwnershipPrivilege", $true) # Necessary to override FilePermissions & take Ownership

现在在目标上递归使用 Get/Set ACL 命令

感谢 Theo 的评论,我们找到了解决该问题的方法。我们只需要创建一个空的 ACL 然后将我们的属性添加到对象中,之后就可以简单地应用它了:

$Path = 'E:\DEPARTMENTS\Gemensam'

$BuiltinAdmin = [System.Security.Principal.NTAccount]'Builtin\Administrators'
$AdminFullControlAce = New-Object System.Security.AccessControl.FileSystemAccessRule(
    $BuiltinAdmin,
    [System.Security.AccessControl.FileSystemRights]::FullControl,
    [System.Security.AccessControl.InheritanceFlags]'ContainerInherit,ObjectInherit',
    [System.Security.AccessControl.PropagationFlags]::None,
    [System.Security.AccessControl.AccessControlType]::Allow
)

#region Create new ACL
$NewAcl = New-Object System.Security.AccessControl.DirectorySecurity
$NewAcl.SetOwner($BuiltinAdmin)
$NewAcl.SetAccessRuleProtection($true,$false)
$NewAcl.AddAccessRule($AdminFullControlAce)
#endregion

$FolderItem = Get-Item -Path $Path -EA Stop
$FolderItem.SetAccessControl($NewAcl)


$FolderAcl = $FolderItem.GetAccessControl()
$FolderAcl.Access | select IdentityReference, FileSystemRights

或者两次应用ACL:

$M = Get-Item -Path 'C:\YourFolder'
$Acl = $M.GetAccessControl()

$BuiltinAdmin = [System.Security.Principal.NTAccount]'Builtin\Administrators'
$InheritedDirAcl = New-Object System.Security.AccessControl.DirectorySecurity
$InheritedDirAcl.SetOwner($BuiltinAdmin)
$InheritedDirAcl.SetAccessRuleProtection($false,$false)

# This is a workaround for non inherited permissions
# that do not get removed when simply applying the new ACL
$Acl.Access | ForEach-Object {
    $Acl.RemoveAccessRuleSpecific($_)
}
$M.SetAccessControl($Acl)
$M.SetAccessControl($InheritedDirAcl)

Related issue here.