Unable to remove ACE from ACL

0

We have want to clear an NTFS ACL of a folder completely and only have the ACL populated with the ACE Builtin\Administrator : Full Control. The code below works fine for most folders but fails on one specific folder:

$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

The output of this code is:

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

For one reason or another it seems to be impossible to remove the ACE of GROUPHC\SWE CEM KVB EV. Even with Get-ACL and Set-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 the inheritance is properly removed, so it can't be an inherited ACE.

enter image description here enter image description here enter image description here

Any help would be much appreciated.

To avoid issues with ownership we run the following code first:

#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
powershell
permissions
file-permissions
acl
ntfs
asked on Stack Overflow Jan 23, 2020 by DarkLite1 • edited Jan 23, 2020 by DarkLite1

2 Answers

0

See my comment above:

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

Now use Get/Set ACL commands recursively on the target

answered on Stack Overflow Jan 23, 2020 by Scepticalist
0

Thanks to Theo from the comments we found a workaround for the problem. We just have to create an empty ACL and then add our properties to the object, after that it can simply be applied:

$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

Or apply the ACL twice:

$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.

answered on Stack Overflow Jan 23, 2020 by DarkLite1 • edited Jan 28, 2020 by DarkLite1

User contributions licensed under CC BY-SA 3.0