TL;DR cheatsheet for setting SACLs, taking the action, how to query the logs to show relevant info, and RE Event IDs.
Welcome to Part IX of our Cheatsheet Series!
Part I: Mimikatz cheatsheet
Part II: Set-Acl cheatsheet
Part III: Get-Acl cheatsheet
Part IV: Enumerating AD cheatsheet
Part V: Windows reverse shells cheatsheet
Part VI: MS Graph PowerShell cheatsheet
Part VII: Hash cracking cheatsheet
Part VIII: The Credential Theft Shuffle
Part IX: SACLs & Querying Collected Logs
Part X: Setting up a simple AD lab in Azure
Background
We went over setting up Windows Event Forwarding and basic tweaking & querying SACLs earlier. This cheatsheet is about
- Setting the SACL
- Taking the action
- Querying the logs
- Outputting meaningful data
It sounds simple, but due to how Microsoft formats the Message property in a given Event ID this turned into an interesting exercise in data parsing.
We will be dealing with the CEO’s account and the VIPs group throughout this cheatsheet. If one needs to imagine a reason for doing so, then pretend that a business has dictated that certain VIP users & groups require extra auditing.
Set the SACLs
#Audit for Password Reset
$object = (Get-ADUser "CEO").DistinguishedName
$subject = [Security.Principal.NTAccount]"Everyone"
$ObjectType_GUID = [system.guid]"00299570–246d-11d0-a768–00aa006e0529"
$InheritedObjectType_GUID = [system.guid]"00000000–0000–0000–0000–000000000000"
$acl = Get-Acl $object -Audit
$NewRule = New-Object System.DirectoryServices.ActiveDirectoryAuditRule($subject,"WriteOwner","Success",$ObjectType_GUID,"None",$InheritedObjectType_GUID)
$ACL.AddAuditRule($NewRule)
Set-Acl $object $acl
#Confirm
(Get-Acl (Get-ADUser "CEO").DistinguishedName -Audit).Audit
# - - Set auditing rules on a group - -
#Audit for changes to ownership, the DACL, and attributes
$object = (Get-ADGroup "VIPs").DistinguishedName
$subject = [Security.Principal.NTAccount]"Everyone"
$ObjectType_GUID = [system.guid]"00000000–0000–0000–0000–000000000000"
$InheritedObjectType_GUID = [system.guid]"00000000–0000–0000–0000–000000000000"
$acl = Get-Acl $object -Audit
$NewRule = New-Object System.DirectoryServices.ActiveDirectoryAuditRule($subject,"WriteProperty, WriteOwner, WriteDACL","Success",$ObjectType_GUID,"None",$InheritedObjectType_GUID)
$ACL.AddAuditRule($NewRule)
Set-Acl $object $acl
#Confirm
(Get-Acl (Get-ADGroup "VIPs").DistinguishedName -Audit).Audit
Set the DACL
Next we will give a mere Domain User WriteOwner on the VIPs group. This will give us something to abuse as Mishka by seizing ownership, changing the group’s DACL, and adding herself to the group.
(If one wants more details then please see our Set-Acl cheatsheet here.)
Import-Module ActiveDirectory
Set-Location AD:
$root = (Get-ADDomain).DistinguishedName
#Give a user WriteOwner over a given group
$victim = (Get-ADGroup "VIPs").DistinguishedName
$acl = Get-ACL $victim
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADUser -Identity "Mishka").SID
#Allow WriteOwner
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"WriteOwner","ALLOW",([GUID]("00000000–0000–0000–0000–000000000000")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
#Apply above ACL rules
Set-ACL $victim $acl
#Confirm
(Get-Acl (Get-ADGroup "VIPs").DistinguishedName).Access | Where-Object {$_.IdentityReference -like "*Mishka*"}
Take the action & seize ownership
Mishka will now
Import-Module ActiveDirectory
Set-Location AD:
#Enumerate
(Get-Acl (Get-ADGroup "VIPs").DistinguishedName).Access | Where-Object {$_.IdentityReference -like "*Mishka*"}
#Take ownership
$target = (Get-ADGroup "VIPs").DistinguishedName
$acl = Get-Acl $target
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADUser "Mishka").SID
$acl.SetOwner($user)
Set-ACL $target $acl
#Confirm
(Get-Acl (Get-ADGroup "VIPs").DistinguishedName).Owner
Query the logs, show who changed the owner, and to whom
One could simply run the first line alone and pull changes to the VIPs group that happened within the last 30 minutes, or any given timeframe, granted that your logs go that far back.
However one would get some incredibly ugly and barely usable output as a result.
That’s just for the old ACL.
If a user changes an account in most cases there are two Event ID 5136s generated; one marked ‘Value Deleted’ and a second event marked “Value Added”. These two events together show the old and new values.
Hence we can pull both and compare them, then output what changed.
First however we make use of an incredibly useful PowerShell command: ConvertFrom-SddlString
This simple command takes that insanely long, illegible SDDL from the Event ID and outputs something readable.
Further, if we want to see the entire DACL or SACL we can expand the property.
We can then use another incredibly useful command: Compare-Object
On a sidenote, we used the technique from Part IV of Back to the Basics to grab the Properties value number for data points that are in the Message Property of a given Event ID.
Put it all together
$ACL_Events = Get-WinEvent -Path "$env:SystemRoot\System32\Winevt\Logs\ForwardedEvents.evtx" | Where-Object {($_.Id -eq "5136") -and ($_.Message -like "*VIPs*") -and ($_.TimeCreated -ge (Get-Date).AddMinutes(-30))}
$Testing = $ACL_Events | Select-Object -Last 2
$EventII = $Testing[0] #Value Added, aka the new ACL
$EventI = $Testing[1] #Value Deleted, aka the old ACL
If($EventI.Properties[11].Value -eq "nTSecurityDescriptor") #Means the Owner, Group, DACL, SACL, or RawDescriptor changed (we care about the Owner & DACL here)
{
$SDDL_CrapI = $EventI.Properties[13]
$ACLI = ConvertFrom-SddlString -Sddl $SDDL_CrapI.Value -Type ActiveDirectoryRights
$SDDL_CrapII = $EventII.Properties[13]
$ACLII = ConvertFrom-SddlString -Sddl $SDDL_CrapII.Value -Type ActiveDirectoryRights
If(Compare-Object -ReferenceObject $ACLI.Owner -DifferenceObject $ACLII.Owner)
{
$WhatChanged = "Owner"
$OldValue = $ACLI.Owner
$NewValue = $ACLII.Owner
}
If(Compare-Object -ReferenceObject $ACLI.DiscretionaryAcl -DifferenceObject $ACLII.DiscretionaryAcl)
{
$XYZ = Compare-Object -ReferenceObject $ACLI.DiscretionaryAcl -DifferenceObject $ACLII.DiscretionaryAcl
$WhatChanged = "DACL"
$OldValue = $ACLI.DiscretionaryAcl
#If you want to see the entire thing: $ACLChange.OldValue | Format-List
$NewValue = $XYZ
}
} #Close the check on whether the DACL changed
Else
{
$WhatChanged = $EventI.Properties[11].Value
$OldValue = $EventI.Properties[13].Value
$NewValue = $EventII.Properties[13].Value
}
$ACLChange = [PSCustomObject]@{
#Now just tease out the Properties[] value
Target = $EventI.Properties[8].Value
WhatChanged = $WhatChanged
OldValue = $OldValue
NewValue = $NewValue
WhoDunnit = $EventI.Properties[3].Value
DC = $EventI.MachineName
Time = $EventI.TimeCreated
}
Write-Host "An AD object was modified. Details are below."
$ACLChange
Voila, we get nice, legible output showing that Mishka changed the ownership of the VIPs group to herself, what time she did it, and which DC processed the request.
Take the action & change the DACL
#Give a user GenericWrite over a given group
$victim = (Get-ADGroup "VIPs").DistinguishedName
$acl = Get-ACL $victim
$user = New-Object System.Security.Principal.SecurityIdentifier (Get-ADUser -Identity "Mishka").SID
#Allow WriteOwner
$acl.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule $user,"GenericWrite","ALLOW",([GUID]("00000000–0000–0000–0000–000000000000")).guid,"None",([GUID]("00000000–0000–0000–0000–000000000000")).guid))
#Apply above ACL rules
Set-ACL $victim $acl
Query the logs, show who modified the DACL, & what they changed
We run the same query as above. It parses the data depending on whether the owner or the DACL was changed and then outputs accordingly.
We can see that Mishka had WriteOwner before she made the change and afterwards she had GenericWrite, as well as some other rights that fall under it.
Take the action & add yourself to the group
#Mishka adds herself to the VIPs group
Add-ADGroupMember -Identity "VIPs" -Members "Mishka"
Query the logs and show who changed the group membership
Changing a group’s membership creates Event ID 5136, however it was easier to see who had added whom to what group using Event ID 4728.
$GroupChange = Get-WinEvent -Path "$env:SystemRoot\System32\Winevt\Logs\ForwardedEvents.evtx" | Where-Object {($_.Id -eq "4728") -and ($_.Message -like "*VIPs*") -and ($_.TimeCreated -ge (Get-Date).AddMinutes(-30))}
$GroupChanged = [PSCustomObject]@{
#Now just tease out the Properties[] value
Target = (Get-ADGroup -Filter * -Properties * | Where-Object {$_.SID -eq $GroupChange.Properties[4].Value}).DistinguishedName
UserAdded = (Get-ADUser -Filter * -Properties * | Where-Object {$_.SID -eq $GroupChange.Properties[1].Value}).DistinguishedName
WhoDunnit = (Get-ADUser -Filter * -Properties * | Where-Object {$_.SID -eq $GroupChange.Properties[5].Value}).DistinguishedName
DC = $GroupChange.MachineName
Time = $GroupChange.TimeCreated
}
Write-Host "An AD object was modified. Details are below."
$GroupChanged
Take the action & reset a user’s password
Set-ADAccountPassword -Identity “CEO” -Reset -NewPassword (ConvertTo-SecureString -AsPlainText “Summer2023!!” -Force)
Query the logs & show who reset the password
We are looking for Event ID 4724.
$pwdReset = Get-WinEvent -Path "$env:SystemRoot\System32\Winevt\Logs\ForwardedEvents.evtx" | Where-Object {($_.Id -eq "4724") -and ($_.Message -like "*CEO*") -and ($_.TimeCreated -ge (Get-Date).AddMinutes(-30))}
$Target = (Get-ADUser -Filter * | Where-Object {$_.SID -eq $pwdReset.Properties[2].Value}).DistinguishedName
$Subject = (Get-ADUser -Filter * | Where-Object {$_.SID -eq $pwdReset.Properties[3].Value}).DistinguishedName
$Reset = [PSCustomObject]@{
#Now just tease out the Properties[] value
Target = $Target
WhoDunnit = $Subject
DC = $pwdReset.MachineName
Time = $pwdReset.TimeCreated
}
Write-Host "An AD password was reset. Details are below."
$Reset
Summary
This is a cheatsheet regarding setting SACLs and querying the collected logs. It is not a defense strategy by itself.
Detection is a distant second to prevention. Audit who holds ‘Dangerous Rights’ and clean up any Misconfiguration Debt before an attacker can take actions like those listed above.
Additionally, there are nice, easy to use paid tools out there from companies like NetWrix that will alert you if someone makes changes to AD that they shouldn’t be doing.
We are just big fans of trying stuff out in the lab for free and learning along the way.
References
Event ID 5143: https://www.socinvestigation.com/threat-hunting-using-windows-event-id-5143/
PSCustomObject: https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-pscustomobject?view=powershell-7.3
ConvertFrom-SddlString: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertfrom-sddlstring?view=powershell-7.3
Find the difference: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/compare-object?view=powershell-7.3
Eventopedia, only mildly useful: http://eventopedia.cloudapp.net/default.aspx?LogType=Windows+Event+Log&LogName=InTrust+for+AD&Source=ITAD+Directory+Changes&EventID=44&action=go
NetWrix AD Change Reporter: https://www.netwrix.com/active_directory_auditing.html
Auditing in AD IOT catch unapproved changes: https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/who-moved-the-ad-cheese/ba-p/255596