ADGPO-004: Disabled GPOs with Content
- Plataforma
- Active Directory
- Categoría
- AD Group Policy
- Severidad
- Low
- 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
The check enumerates every groupPolicyContainer object under CN=Policies,CN=System,DC=<domain> in each domain of the forest, reads the flags attribute (an integer where bit 0 is User Configuration disabled and bit 1 is Computer Configuration disabled), and resolves it to the GpoStatus enumeration exposed by the GroupPolicy module: AllSettingsEnabled (0), UserSettingsDisabled (1), ComputerSettingsDisabled (2), or AllSettingsDisabled (3). For every GPO where flags is 1, 2, or 3, the check then walks the SYSVOL path \\<domain>\SYSVOL\<domain>\Policies\{GUID}\ and inspects the disabled side: the \User subtree if user settings are disabled, the \Machine subtree if computer settings are disabled. A side is considered to contain settings when registry.pol exists and is non-empty, when GptTmpl.inf under Microsoft\Windows NT\SecEdit contains any non-default section, when any Group Policy Preferences XML file is present under Preferences\, when scripts.ini exists under Scripts\, or when fdeploy.ini, IEAK files, or any other CSE artifact is present. The check also reads gPCUserExtensionNames and gPCMachineExtensionNames on the AD object to confirm which client-side extensions are registered, since these attributes drive whether the disabled side has stranded CSE registrations as well as stranded files.
Por qué importa
GpoStatus is metadata. It is a single integer on a single AD object, protected by the same DACL as the rest of the groupPolicyContainer. A principal with Edit settings, delete, modify security on the GPO (the right granted by the "Edit settings" option in GPMC, which maps to write on flags, versionNumber, gPCMachineExtensionNames, and gPCUserExtensionNames) can re-enable a disabled side in a single LDAP write with no SYSVOL change required, no GPMC interaction, and no version increment that would trigger most baseline diff tooling. The dormant content takes effect on the next group policy refresh, typically within 90 to 120 minutes. The blast radius depends entirely on what is in the stranded side. Common findings include: legacy Restricted Groups entries that add a stale service account back into local Administrators, GPP cpassword blobs from pre-MS14-025 deployments (CVE-2014-1812) that disclose plaintext credentials directly from SYSVOL, scheduled task XML that runs a binary from a UNC path the attacker now controls, registry.pol entries that lower LM compatibility, enable WDigest, or disable LSA protection, and Software Installation MSI deployments pointing at a share that no longer enforces integrity. The check is rated Low because exploitation requires existing GPO edit rights and the dormant settings may be benign, but it is a high-signal hygiene finding: every disabled-with-content GPO is a pre-staged payload waiting for a write primitive.
Ruta de ataque
Step 1: Discovery. The attacker, holding any authenticated foothold, runs Get-GPO -All | Where-Object { $_.GpoStatus -ne "AllSettingsEnabled" } or queries (objectClass=groupPolicyContainer) over LDAP and filters where (flags -band 3) -ne 0. They cross-reference each hit with \\<domain>\SYSVOL\<domain>\Policies\{GUID} to find sides that still contain registry.pol, GptTmpl.inf, Preferences XML, or scripts. Step 2: Content triage. For each disabled-with-content GPO the attacker inspects the dormant payload. Tools like Get-GPRegistryValue, parse-policy.py, or simply opening registry.pol with a parser reveal the exact settings that would activate. The attacker prioritizes GPOs that link to high-value OUs (Domain Controllers, Tier 0 servers, privileged workstation tiers). Step 3: ACL evaluation. The attacker enumerates the nTSecurityDescriptor on each candidate groupPolicyContainer object and the SYSVOL folder DACL. They look for explicit grants of Edit settings or Full Control to over-privileged groups: stale help desk groups, decommissioned service accounts, application teams, or wildcard groups like Authenticated Users where a misconfigured delegation exists. BloodHound edges WriteGPLink, GenericWrite, and GenericAll on the GPO object surface the same paths. Step 4: Activation. With write access to flags or any property on the GPO, the attacker issues a single LDAP modify: Set-ADObject -Identity <GPO DN> -Replace @{flags=0} re-enables both sides. No SYSVOL modification is required, no GPMC audit entry is generated beyond the standard 5136 directory service change event, and the version number does not have to be incremented (clients re-evaluate on the next refresh regardless). Step 5: Payload delivery. At the next gpupdate cycle (forced or scheduled), the previously dormant settings apply forest-wide on every object in scope. A Restricted Groups payload adds the attacker to local Administrators on every targeted machine. A GPP scheduled task XML executes the attacker payload as SYSTEM on every machine in the OU. A registry.pol that lowers LSA protection or enables WDigest hands the attacker plaintext credentials at next logon. Step 6: Cleanup. The attacker reverts flags to its original value. The dormant content is once again "disabled," the attack window closes, and the only forensic artifact is the brief flags flip in event 5136 on the DC PDC emulator.
Cómo lo evalúa Guerrilla
Guerrilla binds to each domain in the forest, enumerates (&(objectClass=groupPolicyContainer)(!(flags=0))) under CN=Policies,CN=System, and projects displayName, distinguishedName, gPCFileSysPath, flags, gPCUserExtensionNames, gPCMachineExtensionNames, versionNumber, whenChanged, and whenCreated. For each result, flags is decoded against the User-Disabled (1) and Computer-Disabled (2) bits to determine which side to inspect. The check then walks the gPCFileSysPath UNC (typically \\<domain>\SYSVOL\<domain>\Policies\{GUID}\) and tests the disabled side(s) for content: file existence and non-zero size on registry.pol, presence of any non-template section in GptTmpl.inf, any *.xml under Preferences\, scripts.ini under Scripts\Logon, Scripts\Logoff, Scripts\Startup, or Scripts\Shutdown, and any other CSE artifact directory. Where SMB access to SYSVOL is restricted, the check falls back to inspecting gPCUserExtensionNames and gPCMachineExtensionNames: a disabled side with a non-empty extension list is a strong proxy signal that the side has registered CSEs and therefore content. The check captures the nTSecurityDescriptor on the GPO object and the linked OUs (gPLink on each OU) so the operator can immediately see blast radius and which principals could weaponize the dormant content.
Valor recomendado
No GPOs with disabled sections that contain configured settings; disabled sections should be empty
Remediación
Review all GPOs where GpoStatus is UserSettingsDisabled or ComputerSettingsDisabled. Verify that the disabled section does not contain active settings. Either re-enable the section if the settings are needed, or remove the settings from the disabled section. Document the reason for any intentionally disabled sections.
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-2, CM-6
- MITRE ATT&CK
- T1484.001