Skip to content

Instantly share code, notes, and snippets.

@mavaddat
Last active August 26, 2023 01:51
Show Gist options
  • Save mavaddat/d94ad7c4999de516c101134eea0e0703 to your computer and use it in GitHub Desktop.
Save mavaddat/d94ad7c4999de516c101134eea0e0703 to your computer and use it in GitHub Desktop.
# One time setup
if (-not (Get-Package 'Portable.BouncyCastle' -ErrorAction Ignore)) {
if (-not (Get-PackageSource -Name NuGet -ErrorAction Ignore)) {
Register-PackageSource -Name NuGet -Location https://api.nuget.org/v3/index.json -ProviderName NuGet | Set-PackageSource -Trusted
}
Install-Package -Name 'Portable.BouncyCastle' -Source NuGet -Scope CurrentUser -SkipDependencies
}
# Download the MySQLite repository (for PowerShell <5 without PowerShellGet)
if (-not(Get-Module -Name MySQLite -ErrorAction Ignore) -and ($PSVersionTable.PSVersion.Major -lt 5)) {
$RepositoryZipUrl = 'https://api.github.com/repos/jdhitsolutions/MySQLite/zipball/master'
Invoke-RestMethod -Uri $RepositoryZipUrl -OutFile 'MySQLite.zip'
# Unblock the zip
Unblock-File 'MySQLite.zip'
# Extract the MySQLite folder to a module path (e.g. $env:USERPROFILE\Documents\WindowsPowerShell\Modules\)
Expand-Archive -Path 'MySQLite.zip' -DestinationPath $($env:PSModulePath -split [System.IO.Path]::PathSeparator | Where-Object { (Test-Path -Path $_ -PathType Container -ErrorAction SilentlyContinue) -and $(try { $tmp = New-Item -Path $_ -Name ([System.IO.Path]::GetRandomFileName()) -ItemType File -Value (Get-Random) -ErrorAction SilentlyContinue; Remove-Item -Path $tmp; $true | Write-Output } catch { $false | Write-Output } ) } | Select-Object -First 1) -Force -Confirm
}
elseif (-not(Get-Module -Name MySQLite -ErrorAction Ignore) -and ($PSVersionTable.PSVersion.Major -ge 5)) {
#Simple alternative, if you have PowerShell ≥5, or the PowerShellGet module:
Install-Module MySQLite -Repository PSGallery -Scope CurrentUser
}
# Import the MySQLite module
Import-Module MySQLite #Alternatively, Import-Module \\Path\To\MySQLite
# Import Bouncy Castle Classes
Get-Package 'Portable.BouncyCastle' | ForEach-Object { Add-Type -LiteralPath ($_.Source | Split-Path | Get-ChildItem -Filter 'netstandard*' -Recurse -Directory | Get-ChildItem -Filter *.dll -Recurse -File ).FullName }
# Specify for which domain you want to retrieve cookies
$domain = 'mavaddat.ca'
$cookiesPath = "$env:LOCALAPPDATA\Google\Chrome Beta\User Data\Default\Network\Cookies" # "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\Network\Cookies" # "$env:APPDATA\Opera Software\Opera Stable\Cookies"
# Investigate the db structure
Get-MySQLiteTable -Path $cookiesPath -Detail
# Based on the schema of table `cookies`, form the query
$query = "SELECT name,encrypted_value,path,host_key FROM `"main`".`"cookies`" WHERE `"host_key`" LIKE '%$domain%' ESCAPE '\' LIMIT 0, 49999;"
# Or, get all cookies for all domains
$query = "SELECT name,encrypted_value,path,host_key FROM `"main`".`"cookies`" LIMIT 0, 49999;"
# Read the cookies from the SQLite
$cookies = Invoke-MySQLiteQuery -Path $cookiesPath -Query $query
# Get Chromium cookie master key
$localStatePath = "$env:LOCALAPPDATA\Google\Chrome Beta\User Data\Local State" # "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Local State" # "$env:APPDATA\Opera Software\Opera Stable\Local State"
$cookiesKeyEncBaseSixtyFour = (Get-Content -Path $localStatePath | ConvertFrom-Json).'os_crypt'.'encrypted_key'
$cookiesKeyEnc = [System.Convert]::FromBase64String($cookiesKeyEncBaseSixtyFour) | Select-Object -Skip ([System.Text.Encoding]::UTF8.GetBytes('DPAPI').Count)
$cookiesKey = [System.Security.Cryptography.ProtectedData]::Unprotect($cookiesKeyEnc, $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine)
# Create a web session object for the IWR work
$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
# Prep the cipher elements
$cipher = [Org.BouncyCastle.Crypto.Modes.GcmBlockCipher]::new([Org.BouncyCastle.Crypto.Engines.AesEngine]::new())
# Stuff the cookies into the session
foreach ($cookie in $cookies) {
$path = [string]::IsNullOrEmpty($cookie.path) ? '/' : $cookie.path
try {
$cipherStream = [System.IO.MemoryStream]::new($cookie.encrypted_value)
$cipherReader = [System.IO.BinaryReader]::new($cipherStream)
<#
# We don't need to keep the non-secret payload; however, this is how to retrieve it (spoiler alert, it's 'v10')
$nonSecretPayload = $cipherReader.ReadBytes(([System.Text.Encoding]::ASCII.GetBytes('v10').Count)) #
# if you want to read this, just use
[System.Text.Encoding]::Default.GetString($nonSecretPayload) | Out-Host
#>
# Alternatively, if you don't care about 'v10', move the stream pointer past it
$cipherReader.BaseStream.Position = [System.Text.Encoding]::ASCII.GetBytes('v10').Count
$nonce = $cipherReader.ReadBytes([System.Security.Cryptography.AesGcm]::NonceByteSizes.MinSize)
$parameters = [Org.BouncyCastle.Crypto.Parameters.AeadParameters]::new( ([Org.BouncyCastle.Crypto.Parameters.KeyParameter]::new($cookiesKey)), ([System.Security.Cryptography.AesGcm]::TagByteSizes.MaxSize * [byte]::MaxValue.GetShortestBitLength()), $nonce)
$cipher.Init($false, $parameters)
$cipherText = $cipherReader.ReadBytes($cookie.encrypted_value.Length)
$plainText = [byte[]]::new($cipher.GetOutputSize($cipherText.Length))
if (-not [string]::IsNullOrEmpty($plainText)) {
try {
$len = $cipher.ProcessBytes($cipherText, 0, $cipherText.Length, $plainText, 0)
$bytesDeciphered = $cipher.DoFinal($plainText, $len)
Write-Verbose "Deciphered $bytesDeciphered bytes"
}
catch [System.Management.Automation.MethodInvocationException] {
# if inner exception [Org.BouncyCastle.Crypto.InvalidCipherTextException]
if ($_.Exception.InnerException -is [Org.BouncyCastle.Crypto.InvalidCipherTextException]) {
Write-Error 'Invalid Cipher Text'
}
else {
Write-Error $_ # Echo the error unless you have a better way to handle
}
continue
}
finally {
$cipher.Reset()
}
try {
$session.Cookies.Add([System.Net.Cookie]::new(($cookie.name), [System.Text.Encoding]::Default.GetString($plainText), $path, ($cookie.host_key -replace '^\.')))
}
catch [System.Management.Automation.MethodInvocationException] {
if ($_.Exception.InnerException -is [System.Net.CookieException]) {
$session.Cookies.Add([System.Net.Cookie]::new(($cookie.name), [System.Web.HttpUtility]::UrlEncode([System.Text.Encoding]::Default.GetString($plainText)), $path, ($cookie.host_key -replace '^\.')))
}
else {
Write-Error $_ # Echo the error unless you have a better way to handle
}
}
}
}
finally {
$cipherStream.Dispose()
$cipherReader.Dispose()
}
}
# Remove sensitive objects
$cipherReader = $null
$cipherStream = $null
$cookiesKey = $null
$cookiesKeyEnc = $null
$cookiesKeyEncBaseSixtyFour = $null
$nonce = $null
$cipher = $null
$cipherText = $null
$plainText = $null
Remove-Variable cipher, cipherReader, cipherStream, cookiesKey, cookiesKeyEnc, cookiesKeyEncBaseSixtyFour, nonce, cipherText, plainText
# Do IWR Work
Invoke-WebRequest -Uri $domain -WebSession $session
@Revenant-8
Copy link

Unable to find type [System.Security.Cryptography.AesGcm] - Error message
"Powershell 5.x is based upon .NET Framework. The functionality you are looking for only works with .NET Core/5 apps. Therefore you won't be able to use it in PS 5.x

Install PS Core 7.x and you will then have access to the new functionality. If this isn't possible then you'll have to find an alternative implementation as the .NET Core assembly won't load in .NET Framework PS."

Are there any solutions?

@mavaddat
Copy link
Author

mavaddat commented May 26, 2023

Are there any solutions?

Yes, you can import the following Clixml as drop-in replacements for each respective field of System.Security.Cryptography.AesGcm:

  1. [System.Security.Cryptography.AesGcm]::NonceByteSizes:
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Security.Cryptography.KeySizes</T>
      <T>System.Object</T>
    </TN>
    <ToString>System.Security.Cryptography.KeySizes</ToString>
    <Props>
      <I32 N="MinSize">12</I32>
      <I32 N="MaxSize">12</I32>
      <I32 N="SkipSize">1</I32>
    </Props>
  </Obj>
</Objs>
  1. [System.Security.Cryptography.AesGcm]::TagByteSizes:
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Security.Cryptography.KeySizes</T>
      <T>System.Object</T>
    </TN>
    <ToString>System.Security.Cryptography.KeySizes</ToString>
    <Props>
      <I32 N="MinSize">12</I32>
      <I32 N="MaxSize">16</I32>
      <I32 N="SkipSize">1</I32>
    </Props>
  </Obj>
</Objs>

Finally, I believe if you install PowerShell 7, you will be able to access the assembly above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment