ADGPO-009: GPOs with No Apply Permission
- Plataforma
- Active Directory
- Categoría
- AD Group Policy
- Severidad
- Medium
- Pilar de Zero Trust
- Governance (peso 1)
- Fixtures de referencia
- 3
- Cobertura de ramas
- Observada: los fixtures prueban los veredictos que ejercitan
- Procedencia
- baseline
Qué comprueba
For each GPO returned by Get-GPO -All, the check calls Get-GPPermission -Guid $gpo.Id -All and inspects every entry. It records the Trustee (SID, name, type), the Permission value, the Inherited flag, and the Denied flag. A GPO passes when at least one Allow ACE grants GpoApply (which implicitly bundles GpoRead) to a security principal that is enabled and contains at least one member. A GPO fails when (a) no ACE grants GpoApply, (b) the only GpoApply ACE is a Deny, (c) the only GpoApply ACE targets a disabled or empty group, or (d) the only GpoApply ACE targets a deleted SID that resolves to an orphan in the ACL. The check also evaluates the link state: a GPO with no Apply permission that is unlinked is informational only, while a GPO with no Apply permission that is linked to any OU, site, or the domain root is a finding because operators expect it to be enforcing settings. Supplementary metadata captured for each finding includes the GPO display name, GUID, GpoStatus, creation and modification time, the full list of linked SOM paths, every Trustee on the ACL, and the GpoApply state per trustee.
Por qué importa
GPO security filtering is the mechanism that scopes a linked GPO to a subset of the OU it is linked under. The default ACL on a new GPO grants Authenticated Users both Read and Apply Group Policy, which means every computer and user account in the linked scope applies the policy. Operators commonly tighten this by removing Authenticated Users and adding a specific security group such as TIER0_Servers or HR_Workstations. The trap is that "remove Authenticated Users" and "add the replacement group with Apply" must happen as a pair. When only the first step is done, or when the replacement group has Read but not Apply, the GPO silently stops applying everywhere. The MS16-072 patch (June 2016) made this trap worse: before MS16-072, Authenticated Users only needed Apply on the GPO itself; after MS16-072, the computer account must have Read on the GPO regardless of who has Apply, because Group Policy retrieval moved from user context to computer context. Many environments have GPOs created before MS16-072 whose ACLs were later modified to remove Authenticated Users, and those GPOs no longer apply on any modern client. The blast radius is determined by what the GPO was supposed to enforce: a security baseline that no longer applies leaves every workstation in the scope unhardened, an AppLocker policy that no longer applies turns every endpoint back into a default-execute environment, a logon banner GPO that no longer applies removes legal coverage, and a credential-guard or LSASS-protection GPO that no longer applies re-exposes the host to Mimikatz. Attackers who already enumerate the directory will notice these inert GPOs through PowerView, BloodHound, or a direct LDAP query and use them as a map of which OUs are not actually hardened.
Ruta de ataque
1. Reconnaissance: the attacker, with any authenticated domain user, enumerates GPOs and their security filtering. PowerView Get-DomainGPO returns the ACL, BloodHound (CollectionMethod ACL,GPOLocalGroup) maps GPO-to-OU relationships, and a direct LDAP query on CN=Policies,CN=System returns the nTSecurityDescriptor on every gPLink target. 2. Identification of inert GPOs: the attacker parses each GPO ACL and identifies GPOs where no principal holds GpoApply, or where the only GpoApply principal is an empty or disabled group. These GPOs are linked but enforce nothing. 3. Target selection: the attacker correlates inert GPOs with the OUs they are linked to and the names of the GPOs. A GPO named "Workstation_AppLocker_Enforcement" or "DC_LSA_Protection" that is inert tells the attacker which security control is not in force on which scope, without needing to touch the endpoint. 4. Tradecraft alignment: the attacker chooses techniques that the missing GPO would have blocked. If AppLocker is not enforced, drop unsigned binaries. If LSA Protection is not enforced, dump LSASS with comsvcs.dll or nanodump. If PowerShell ScriptBlock logging is not enforced, run encoded commands without telemetry. 5. Privileged path: if the attacker also has GPO edit rights (delegated permission, Group Policy Creator Owners membership, or a writeable ACE found through ADACL-007/ADACL-008), they can add their own Apply ACE to the inert GPO and inject a payload (Immediate Scheduled Task, Restricted Groups, startup script). Because the GPO is already linked, no new gPLink is created and no "new GPO" audit event fires; the change appears as a routine GPO modification. 6. Persistence and evasion: the operator who originally tightened the security filter sees the GPO with the expected filter and assumes it is enforcing. The defender who relies on GPMC for assurance does not notice that the GPO never applied. The inert GPO is a long-lived blind spot.
Cómo lo evalúa Guerrilla
Guerrilla loads the GroupPolicy and ActiveDirectory modules, calls Get-GPO -All -Domain $env:USERDNSDOMAIN, then for each GPO calls Get-GPPermission -Guid $gpo.Id -All. The returned objects are filtered for entries where Permission equals GpoApply or GpoEditDeleteModifySecurity and the Denied property is false. For each Allow GpoApply ACE, the Trustee SID is resolved via Get-ADObject -Filter "ObjectSid -eq $sid" and the resulting object is checked for ObjectClass (user, group, computer), Enabled state, and group membership count via Get-ADGroupMember -Recursive. A GPO is flagged as having no apply target when zero ACEs survive these filters. The check also enumerates linked scopes via Get-ADOrganizationalUnit -LDAPFilter "(gPLink=*$($gpo.Id)*)", Get-ADObject -SearchBase "CN=Sites,CN=Configuration,$($domain.DistinguishedName)" -LDAPFilter "(gPLink=*$($gpo.Id)*)", and the gPLink attribute on the domain root. Output rows include DisplayName, Id, GpoStatus, ApplyTrustees (collection), HasValidApplyTarget (bool), IsLinked (bool), LinkedScopes (string array), and a Notes field that flags the high-impact combinations: linked plus no apply target, linked to DC OU plus no apply target, name suggests a security baseline plus no apply target, and replacement group is empty.
Valor recomendado
All GPOs have Apply Group Policy permission granted to appropriate security groups; no GPOs with no apply targets
Remediación
Check each GPO for Apply Group Policy permissions using Get-GPPermission. Ensure that at least one security group with members has the Apply permission. For GPOs that should apply to specific groups only, verify the target groups contain the intended members. Add Authenticated Users with Read-only permission (without Apply) if security filtering is used.
Veredictos probados con fixtures
Cada veredicto de esta tabla está probado por un fixture de referencia en la suite de pruebas que valida el módulo. La tabla se deriva de la última ejecución en verde; no puede editarse a mano.
| Escenario | Veredicto esperado |
|---|---|
| clean | PASS |
| known-bad | WARN |
| throttled | Not Assessed |
Mapeos a marcos de referencia
- NIST SP 800-53
- CM-6, AC-3
- MITRE ATT&CK
- T1484.001