Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

  •  
  •  
  •  
  •  
  •  
  •  

Today, while logging into a Windows Server via my favorite RDP tool (RDCMan), I was faced with the “Your password has expired” prompt.  No worries, just enter my password and change it.

Except for the part where the password has been saved and I neglected to add it to my password manager. Oops.  So, naturally, the first thing I want to do is just go look in the saved file (an .RDG).  Not surprisingly, it’s encrypted with something:
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

After tinkering around wit ConvertFrom-SecureString, I guessed that it was probably done by something in the application itself.  So, I started by importing the DLLs as modules and looking around.  I imported the MSTSCLib.dll, and started tabbing through the types.  I stumbled onto a good potential in MSTSCLib.IMsTscSecuredSettings, but alas, it was a false alarm.
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

I dug around a little more, but met with similar dashed hopes.

I then started looking for ways to decrypt it using the RDCMan executable.

Sometimes, you can luck out by attempting to run Import-Module against an EXE.  It might have exported commands available. Nope. Not it.
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

Sometimes, you can add an EXE as an assembly, and then import:
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

Struck out again.

And then, you can sometimes load it via reflection:
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

Ooh, promising.

Next, I decided to see if there were any exports that could further ignite hope:
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

Now that we have something maybe to go on, we can try importing and creating some objects to see what sticks.  First, import the reflected assembly:
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

The module is called RDCMan, so that’s what we’re going to use as the base to add the values we discovered in GetExportedTypes() a few steps earlier:
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

Looks like we have a couple of potential opportunities with EncryptionMethod and EncryptionSettings.  Running a Get-Member on both of those helps us further narrow down the field of “interesting things” to EncryptionSettings:
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

While we’re here, let’s also take a look at what methods RDCMan.Encryption has to offer us.  After typing [RDCMan.Encryption]:: in the prompt and tabbing, I’m delighted to find two methods: DecryptPasswords() and DecryptString().
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

Both look like potential winners, but the DecryptString caught my eye because the OverloadDefinitions had some detail about what to pass to it: I needed an encrypted string (which I had in the .RDG XML file) and something in RdcMan.EncryptionSettings.  I had successfully executed New-Object against RdcMan.EncryptionSettings, so mayyyyybe:
Decrypting Credentials Stored in Remote Desktop Connection Manager (RDCMan) .rdg

BOOM. SUCCESS.

No, my password isn’t really Password123. I promise.  

So, I bundled it all up as a handy script that you can copy/paste here or get over on the TN Gallery.  Note: If you saved the file with a Windows account credential and attempted to run this from a different computer, this method won’t work.

# Decrypt passwords in RDG files
param($RDGFile,
	$PasswordString,
	$RDCManSource
	)

If (!$RDCManSource)
{
	$RDCManSource = (Get-ChildItem -Path @('C:\Program Files\Microsoft', 'C:\Program Files (x86)\Microsoft') -File "RDCMan.exe" -Recurse -ErrorAction SilentlyContinue)[0]
}

If (!$RDCManSource)
{
	Write-Error "Remote Desktop Manager must be installed.  If it is installed, use the -RDCManSource parameter to specify the executable's location."
	Exit
}
else
{
	try
	{
		$Assembly = [Reflection.Assembly]::LoadFile($RDCManSource.FullName)
	}
	catch
	{
		$_.Exception.Message.ToString();
		Write-Host "Catch"; Exit
	}
	try { Import-Module $Assembly }
	catch
	{
		$_.Exception.Message.ToString();
		Write-Host "Import Exception"; exit }
}

If ($RDGFile)
{
	[xml]$Data = Get-Content $RDGFile
	$CredentialValues = $Data.SelectNodes("*//logonCredentials")
	$global:Output = @()
	foreach ($obj in $CredentialValues)
	{
		try
		{
			$EncryptionSettings = New-Object -TypeName RdcMan.EncryptionSettings
			$Password = [RdcMan.Encryption]::DecryptString($obj.password, $EncryptionSettings)
		}
		catch
		{
			$_.Exception.Message.ToString(); continue
		}
		If ($Password -and ($Password -notcontains 'Failed to decrypt'))
		{
			$CredObject = New-Object PSObject
			$CredObject | Add-Member -Type NoteProperty -Name "ProfileName" -Value $obj.ProfileName -ea SilentlyContinue -Force
			$CredObject | Add-Member -Type NoteProperty -Name "UserName" -Value $obj.username -ea SilentlyContinue -Force
			$CredObject | Add-Member -Type NoteProperty -Name "Password" -Value $Password
			$CredObject | Add-Member -Type NoteProperty -Name "Domain" -Value $obj.domain
			$global:Output += $CredObject
		}
	}
	If ($Output)
	{
		$Output
	}
	Else
	{
		Write-Host "Nothing to show."
	}
}
else
{
	If ($PasswordString)
	{
		$EncryptionSettings = New-Object -TypeName RdcMan.EncryptionSettings
		$Password = [RdcMan.Encryption]::DecryptString($PasswordString, $EncryptionSettings)
		Write-Host "Cleartext password: $($Password)"
	}
}

 
Good luck recovering!

Published by Aaron Guilmette

Helping companies conquer inferior technology since 1997. I spend my time developing and implementing technology solutions so people can spend less time with technology. Specialties: Active Directory and Exchange consulting and deployment, Virtualization, Disaster Recovery, Office 365, datacenter migration/consolidation, cheese.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.