Skip to content
06 · Group Policy & SYSVOLPart 3 of 4Foundation

Finding and removing GPP passwords (cpassword)

Find every Group Policy Preferences cpassword in SYSVOL, backups and client caches, map it to the exposed account, rotate it and stop it coming back.

Florian Amette7 min read

Group Policy Preferences once let administrators set local account passwords, service credentials, scheduled task run-as accounts, mapped drive credentials and data source passwords through a GPO. The password was stored in SYSVOL as a cpassword attribute, AES-256 encrypted with a key Microsoft published in its protocol documentation. SYSVOL is readable by every authenticated user, and tools such as Get-GPPPassword and the equivalent modules in common attack frameworks decrypt the value in a second. The usual payload is a local administrator password shared across every workstation and server, which turns one phished user into lateral movement across the estate.

MS14-025 closed the editor in May 2014 but left existing values in place, and they are still found in assessments today. The Group Policy and SYSVOL pillar shows the basic SYSVOL search. This guide covers the complete job: every place a cpassword can hide, working out which account each one exposes, rotating it properly, and making sure it does not come back.

Where cpassword values live

Preference items that could carry a password write it to these files inside a GPO's Machine\Preferences or User\Preferences folders:

FilePreference typeAccount attribute to read
Groups\Groups.xmlLocal Users and GroupsuserName, newName
Services\Services.xmlServicesaccountName
ScheduledTasks\ScheduledTasks.xmlScheduled TasksrunAs
Drives\Drives.xmlDrive MapsuserName
DataSources\DataSources.xmlData Sourcesusername
Printers\Printers.xmlPrintersusername

The same XML files also exist outside the live Policies folder:

  • GPO backups made with GPMC or Backup-GPO, often on admin file shares or in change-control repositories.
  • Client-side caches: each client keeps a copy of applied preference XML under %ProgramData%\Microsoft\Group Policy\History\{GUID}\ (computer settings) and %LocalAppData%\Microsoft\Group Policy\History\ (user settings), which also ends up in VM templates and disk images.
  • Stale SYSVOL copies left behind by an FRS-to-DFSR migration or manual copies on DCs (SYSVOL.bak, SYSVOL_old).
  • Scripts in NETLOGON and GPO script folders, which are not cpassword but frequently contain plain-text credentials for the same accounts.

Measure: find every instance

Parse the XML rather than grepping so you can extract which account each value belongs to. The script reports the value's presence, never the value itself.

PowerShell
function Find-GppCpassword {
    param([Parameter(Mandatory)] [string[]] $Path)
    Get-ChildItem -Path $Path -Recurse -Include Groups.xml, Services.xml, ScheduledTasks.xml,
        Drives.xml, DataSources.xml, Printers.xml -ErrorAction SilentlyContinue |
    ForEach-Object {
        $file = $_.FullName
        Select-Xml -Path $file -XPath '//*[@cpassword]' | ForEach-Object {
            $n = $_.Node
            if ($n.cpassword) {
                [pscustomobject]@{
                    File    = $file
                    GpoGuid = if ($file -match '\{([0-9A-Fa-f-]{36})\}') { $Matches[1] }
                    Item    = $n.ParentNode.name
                    Account = @($n.userName, $n.accountName, $n.runAs, $n.username, $n.newName) |
                                  Where-Object { $_ } | Select-Object -First 1
                    Changed = $n.ParentNode.changed
                }
            }
        }
    }
}

$dns = (Get-ADDomain).DNSRoot
$hits = Find-GppCpassword -Path "\\$dns\SYSVOL\$dns\Policies", 'D:\GPOBackups'
$hits | ForEach-Object {
    $gpo = if ($_.GpoGuid) { Get-GPO -Guid $_.GpoGuid -ErrorAction SilentlyContinue }
    $_ | Add-Member -NotePropertyName GpoName -NotePropertyValue $gpo.DisplayName -PassThru
} | Format-Table GpoName, Item, Account, Changed, File -AutoSize

Empty cpassword="" attributes are ignored because they carry no secret. A hit with no resolvable GpoName usually points to a backup or an orphaned GPT folder whose GPC was deleted; orphaned folders are still readable by everyone and must be removed too.

Check for leftover SYSVOL copies on each DC and for cached copies on a sample of clients and on your golden images:

PowerShell
# On each DC
Get-ChildItem C:\Windows -Directory -Filter 'SYSVOL*' | Select-Object FullName

# On a client or a mounted image
Find-GppCpassword -Path "$env:ProgramData\Microsoft\Group Policy\History"

Audit: work out what each value exposes

For every hit, answer three questions before touching the GPO:

  1. Which account? A Groups.xml item with userName="Administrator" (or a renamed built-in via newName) exposes the local administrator on every computer in the GPO's scope. A Services.xml or ScheduledTasks.xml item usually exposes a domain service account.
  2. Where did it apply? Check the GPO's links and security filtering with Get-GPOReport -Guid <id> -ReportType Html. A local admin password applied to servers and workstations alike means the same credential works on both tiers.
  3. Is it still valid? For domain accounts compare pwdLastSet with the item's changed timestamp. If the password was last set before or at that date, the SYSVOL value is almost certainly current.
PowerShell
Get-ADUser svc-backup -Properties pwdLastSet, lastLogonTimestamp, memberOf |
    Select-Object Name, @{n='PwdLastSet';e={[datetime]::FromFileTime($_.pwdLastSet)}},
        @{n='LastLogon';e={[datetime]::FromFileTime($_.lastLogonTimestamp)}}, memberOf

To size the blast radius of a local administrator entry, count the computers in scope of the GPO. The query below walks every OU where the GPO is linked; adjust it if the GPO is also security-filtered to a group.

PowerShell
$guid = '<GpoGuid from the scan>'   # e.g. 6AC1786C-016F-11D2-945F-00C04FB984F9
Get-ADObject -LDAPFilter "(gPLink=*$guid*)" -SearchBase (Get-ADDomain).DistinguishedName |
    ForEach-Object {
        [pscustomobject]@{
            LinkedTo  = $_.DistinguishedName
            Computers = (Get-ADComputer -SearchBase $_.DistinguishedName -Filter * | Measure-Object).Count
        }
    }

A count that includes servers and workstations together tells you the password bridged tiers: anyone who harvested it from a workstation could log on to servers with the same account. Record that fact in the finding, because it determines how much of the estate you should treat as potentially accessed.

If the exposed account is privileged (a member of any admin group, or a service account with rights on Tier 0 systems), treat this as a potential incident: review its logon history on DCs (events 4624 and 4768) before rotation destroys the evidence of reuse.

Enforce: replace, remove, rotate

Order matters. Replace the mechanism first, otherwise removing the preference breaks whatever it was doing. Plan the work as one change per exposed account rather than one change per GPO: the same service account often appears in several GPOs, and rotating it while one of them still pushes the old password means the next Group Policy refresh either breaks the service or, for local accounts, resets the password back to the exposed value.

  • Local administrator passwords → deploy Windows LAPS to the same OUs, confirm passwords are being backed up, then delete the Local Users and Groups item. LAPS immediately rotates the local password on each machine, which is also your rotation step.
  • Service and scheduled task accounts → move to a gMSA where the application supports it. Scheduled tasks can run as a gMSA (New-ScheduledTaskPrincipal -UserId 'CORP\svc-task$' -LogonType Password) or as SYSTEM if the task only needs local rights.
  • Drive maps and printers with credentials → remove the credential; access should use the user's own identity with share permissions granting what they need.
  • Data sources → use integrated authentication or an application-managed secret store.

Then remove the item. Use the Group Policy Management Editor (Computer or User Configuration > Preferences > the relevant node > delete the item) rather than editing XML by hand, so the GPO version number increments and clients process the change. If the GPO contains nothing else, unlink and delete the GPO after backing it up somewhere access-controlled.

Finally, rotate. For domain accounts, reset the password to a new random value and update every consumer; if you suspect the account was used by an attacker, also review what it had access to rather than treating the reset as the end of the matter. For local accounts not covered by LAPS, rotate on every machine in scope. Removing a preference never reverts or changes the password it already set.

Delete or sanitise GPO backups containing the values, remove leftover SYSVOL copies, and rebuild or clean golden images that captured the client-side history.

Verify

Rerun Find-GppCpassword against SYSVOL, the backup locations and a sample of clients after their next Group Policy refresh; all should return nothing. For domain accounts, confirm pwdLastSet is after the rotation date. For LAPS-covered machines, confirm a LAPS password exists and was set recently:

PowerShell
Get-LapsADPassword -Identity WS0142 | Select-Object ComputerName, PasswordUpdateTime, ExpirationTimestamp

Add the SYSVOL scan to a scheduled job so a restored backup or an imported legacy GPO is caught within a day. PingCastle also checks for GPP passwords, so it will flag a regression in your periodic assessment.

A useful detection layer is a deliberate decoy: a Groups.xml in an unlinked GPO with a cpassword for a honeytoken account that is never legitimately used. Any authentication attempt for that account (4625, 4771 or 4776 on DCs) means someone is harvesting SYSVOL.

What it breaks

  • Removing a Local Users and Groups item before LAPS is deployed leaves machines with a known, unmanaged local admin password. Deploy LAPS first.
  • Rotating a domain service account breaks every service, task and application still holding the old password. Inventory consumers with 4624/4768 logon data before resetting.
  • Deleting drive map credentials causes access denied for users whose own accounts lack share permissions; fix the share ACLs first.
  • Deleting old GPO backups removes your ability to restore those GPOs as they were. Keep a sanitised backup if you need history.
  • The decoy GPO will be flagged by your own assessment tools; document it so it is not "fixed" by a colleague.

Related reading: the Group Policy & SYSVOL topic hub, auditing GPO permissions, and password policy that works for the account-side controls that limit the damage of any leaked credential.

Frequently asked questions

If I delete the cpassword from SYSVOL, am I safe?

No. Any authenticated user could read SYSVOL for as long as the value was there, often years, so assume the password is known. Deleting the preference item stops new exposure but does not change the password on any system it was applied to. Rotate the credential on every machine or account that uses it, and check GPO backups, client caches and images for copies.

Did MS14-025 remove existing cpassword values?

No. MS14-025 (KB2962486, May 2014) removed the ability to set passwords in the affected Group Policy Preferences dialogs, so administrators cannot create new ones through the editor. It did not touch XML files already in SYSVOL, and it does not stop someone importing an old GPO backup that contains them. Existing values must be found and removed manually.

What should replace GPP local administrator passwords?

Windows LAPS. It sets a unique, random password for the built-in administrator (or a named account) on every machine, rotates it on a schedule, and stores it in AD or Entra ID with access controlled per OU and optionally encrypted. It removes the shared-password problem entirely, which is what made GPP local admin accounts so damaging in the first place.

Finding and removing GPP passwords (cpassword)

Related guides

Group Policy & SYSVOL

Group Policy and SYSVOL hardening

Lock down GPO delegation, purge Group Policy Preferences cpassword secrets from SYSVOL, and add change control to prevent silent GPO abuse.

Foundation
Group Policy & SYSVOL

Deploying Microsoft security baselines with GPO

Deploy Microsoft security baselines with Group Policy: SCT, Policy Analyzer gap analysis, LGPO testing, ring-based rollout, exceptions and drift checks.

Intermediate
Group Policy & SYSVOL

Auditing GPO permissions and gPLink rights

Find who can edit, create and link GPOs in Active Directory: GPO ACLs, gPLink rights on OUs and sites, Group Policy Creator Owners and WMI filters.

Intermediate