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:

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.

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.

Sometimes, you can add an EXE as an assembly, and then import:

Struck out again.

And then, you can sometimes load it via reflection:

Ooh, promising.

Next, I decided to see if there were any exports that could further ignite hope:

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:

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:

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:

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().

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:

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.