|
|
|
<# |
|
.Synopsis |
|
Decrypt an MTPuTTY configuration file |
|
|
|
.Description |
|
Read an MTPuTTY configuration file, decrypt the passwords and dump the result |
|
|
|
.Parameter ConfigFile |
|
Path to the MTPuTTY configuration file |
|
|
|
.Example |
|
Invoke-MTPuTTYConfigDump mtputty.xml |
|
#> |
|
function Invoke-MTPuTTYConfigDump { |
|
[CmdletBinding(DefaultParameterSetName="ConfigFile")] |
|
Param( |
|
[Parameter(ParameterSetName = "ConfigFile", Position = 0, Mandatory = $true)] |
|
[String] |
|
$ConfigPath |
|
) |
|
$PROV_RSA_FULL = 1 |
|
$CRYPT_VERIFYCONTEXT = 0xF0000000 |
|
$CALG_SHA = 0x00008004 |
|
$CALG_RC2 = 0x00006602 |
|
|
|
Function Get-CryptoAPI { |
|
$MethodDefinition = @" |
|
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] |
|
[return : MarshalAs(UnmanagedType.Bool)] |
|
public static extern bool CryptAcquireContext(ref IntPtr hProv, string pszContainer, string pszProvider, uint dwProvType, long dwFlags); |
|
|
|
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] |
|
[return : MarshalAs(UnmanagedType.Bool)] |
|
public static extern bool CryptReleaseContext(IntPtr hProv, uint dwFlags); |
|
|
|
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] |
|
[return : MarshalAs(UnmanagedType.Bool)] |
|
public static extern bool CryptCreateHash(IntPtr hProv, uint algId, IntPtr hKey, uint dwFlags, ref IntPtr phHash); |
|
|
|
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] |
|
[return : MarshalAs(UnmanagedType.Bool)] |
|
public static extern bool CryptDestroyHash(IntPtr hHash); |
|
|
|
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] |
|
[return : MarshalAs(UnmanagedType.Bool)] |
|
public static extern bool CryptHashData(IntPtr hHash, byte[] pbData, uint dataLen, uint flags); |
|
|
|
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] |
|
[return : MarshalAs(UnmanagedType.Bool)] |
|
public static extern bool CryptDeriveKey(IntPtr hProv,int Algid, IntPtr hBaseData, int flags, ref IntPtr phKey); |
|
|
|
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] |
|
[return : MarshalAs(UnmanagedType.Bool)] |
|
public static extern bool CryptDestroyKey(IntPtr hKey); |
|
|
|
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] |
|
[return : MarshalAs(UnmanagedType.Bool)] |
|
public static extern bool CryptDecrypt(IntPtr hKey, IntPtr hHash, int Final, uint dwFlags, byte[] pbData, ref uint pdwDataLen); |
|
"@ |
|
try { |
|
$CryptoAPI = Add-Type -MemberDefinition $MethodDefinition -name advapi32 -Namespace CryptoAPI -PassThru |
|
} catch {} |
|
|
|
return [CryptoAPI.advapi32] |
|
} |
|
|
|
<# https://devblogs.microsoft.com/powershell/format-xml/ #> |
|
Function Format-XML ([System.Xml.XmlElement]$xml, $indent=2) { |
|
$StringWriter = New-Object System.IO.StringWriter |
|
$XmlWriter = New-Object System.XMl.XmlTextWriter $StringWriter |
|
$xmlWriter.Formatting = "indented" |
|
$xmlWriter.Indentation = $Indent |
|
$xml.WriteContentTo($XmlWriter) |
|
$XmlWriter.Flush() |
|
$StringWriter.Flush() |
|
Write-Output $StringWriter.ToString() |
|
} |
|
|
|
try { |
|
[xml]$config = Get-Content -Path $ConfigPath -ErrorAction Stop |
|
} catch { |
|
Write-Host $_ -ErrorAction Stop |
|
return |
|
} |
|
|
|
$CryptoAPI = Get-CryptoAPI |
|
[System.IntPtr]$hProv = 0 |
|
$servers = $config.SelectNodes("//Servers") |
|
if($servers.Count -gt 0 -and $CryptoAPI::CryptAcquireContext([ref]$hProv, $null, $null, $PROV_RSA_FULL, $CRYPT_VERIFYCONTEXT) -ne $false) { |
|
foreach($node in $config.SelectNodes("//Node")) { |
|
if($node.Type -eq 0) { |
|
Write-Host "$($node.DisplayName):" |
|
} elseif ($node.Type -eq 1) { |
|
[System.IntPtr]$hHash = 0 |
|
[System.IntPtr]$hKey = 0 |
|
$password = [system.Text.Encoding]::UTF8.GetBytes("1$($node.UserName.Trim())$($node.ServerName.Trim())") |
|
$ciphertext = [System.Convert]::FromBase64String($node.Password.Trim()) |
|
$ciphertextLength = $ciphertext.Length |
|
|
|
if($CryptoAPI::CryptCreateHash($hProv, $CALG_SHA, 0, 0, [ref]$hHash) -ne $false) { |
|
if($CryptoAPI::CryptHashData($hHash, $password, $password.Length, 0) -ne $false) { |
|
if($CryptoAPI::CryptDeriveKey($hProv, $CALG_RC2, $hHash, 0, [ref]$hKey) -ne $false) { |
|
if($CryptoAPI::CryptDecrypt($hKey, 0, $true, 0, $ciphertext, [ref]$ciphertextLength) -ne $false) { |
|
$ciphertext = $ciphertext[0..($ciphertextLength-1)] |
|
if($ciphertextLength -ge 2 -and $ciphertext[1] -eq 0) { |
|
$node.Password = [system.Text.Encoding]::Unicode.GetString($ciphertext) |
|
} else { |
|
$node.Password = [system.Text.Encoding]::UTF8.GetString($ciphertext) |
|
} |
|
} |
|
$null = $CryptoAPI::CryptDestroyKey($hKey); |
|
} |
|
} |
|
$null = $CryptoAPI::CryptDestroyHash($hHash); |
|
} |
|
Format-XML $node |
|
Write-Host |
|
} |
|
Write-Host |
|
} |
|
$null = $CryptoAPI::CryptReleaseContext($hProv, 0) |
|
} |
|
} |
|
|
|
Export-ModuleMember -Function Invoke-MTPuTTYConfigDump |
This comment has been minimized.
Thank you so much for writing this!