Sunday, February 12, 2012

Fixing Unresolvable NTFS ACL Accounts

An interesting question came up on SuperUser the other day. The OP wanted to know how to change the account of a NTFS ACL but still retain all of the ACL settings (privileges, inheritance etc.…). This could be needed if the original account was deleted, or if the ACL was on a drive that was moved to another machine etc.…

When the ACL entry has an unresolvable account it will appear like this:

image

The first thing that came to mind was to edit the SDDL string. The SDDL is a not so easy to read string representation of the ACL. It looks like this:

Easy to read huh?

You can learn all about the format on MSDN, however the format is not important for our purpose here. All we need to do is replace the SID with the SID of the account we want.

So first lets use the Win32_Account WMI class to get a list of user account SIDs:

This will get all account types from the local machine and the domain if the machine is on a domain which could make this command take a really long time.

To make it go faster we’ll filter the query a little bit. There are a couple properties of this class we can use to reduce the run time.

  • Name
  • SidType

    (1) User
    (2) Group
    (3) Domain
    (4) Alias
    (5) WellKnownGroup
    (6) DeletedAccount
    (7) Invalid
    (8) Unknown
    (9) Computer

  • LocalAccount

So to get the SID of a local user account named NewAccount we’ll use:

To find the SID of the broken ACL entry we’ll use the Get-ACL cmdlet. The ACE’s are stored in the Access property returned by the Get-Acl object. To retrieve the SID of the entry which account is unknown we’ll use the Translate method to find that one that can’t translate to an NTAccount object. We also aren’t interested in ACE’s that are inherited so we’ll filter them out.

The invalid SIDs are stored in the array $invalidSids.

Now we can do a simple string search and replace in the SDDL string.

The last step is to set the ACL with the updated SDDL. To do this we’ll use the SetSecurityDescriptorSddlForm method.

And that’s it. The ACL is now updated with the account but all of the previous ACE settings are retained as we wanted.

image

3 comments:

  1. Actually I found another method to determine unknown account SIDs and replace them;
    You can itterate over $ACL.Access object and check IdentityReference for match with -like "*S-1-5-21-*" or regex and then replace those SIDs with a known one. IdentityReference is username but returs SID if account is unknown.
    Also be sure to check if Access.IsInherited is false.

    Nevertheless this is a great post.

    -- AndyDeGroo, the OP on Superuser

    ReplyDelete
  2. It did appear that when the SID could not be translated to an actual account the SID would be displayed instead of the account name. Though just to make sure there are no false positives, I go ahead and attempt to translate to an NTAccount to make sure :-)

    ReplyDelete
  3. It's possible without powershell, with icacls or other tool?

    ReplyDelete