Remote Desktop Connection Manager unexpected XML node credentials profiles

Few weeks ago my Remote Desktop Connections Manager started to report an access denied while trying to connect to some servers on my list, prompting me for a password. I was pretty sure the password stored in the RDCMan profile was correct, but didnt really have time to investigate it further. Until today

Remote Desktop Connections Manager configuration

RDCMan stores information about servers and groups they belong to in the .rdg file. Its a simple xml file and its structure is quite straightforward. A very simple .rdg file with three servers defined might look as follows:

testuser testuser AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA2RswPSIL7EGcvudhCBZVHwAAAAACAAAAAAADZgAAwAAAABAAAAA0t7PykLWY32WfWsQZ8yEWAAAAAASAAACgAAAAEAAAAEx7e35J0SATgvXU94/RTbQYAAAAXzEXsudQGdC7cL9+y6jbOstaWUEdIQtKFAAAAAIx3NxV/dDSNZ7iIQpKtAXKwFfL True test-group TESTWIN Custom testuser AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA2RswPSIL7EGcvudhCBZVHwAAAAACAAAAAAADZgAAwAAAABAAAAAC1TS+S/9utgkT7FK9hzaHAAAAAASAAACgAAAAEAAAAGZ4jbOqYepnHT6+dOvhKGEQAAAADZcV+0aHOj6WN6kkVgau1RQAAACrW1CxTftLzUJk+SB4tsvyttrSpw== DELAPTOP TESTWIN2 testuser TESTWIN3 testuser

Whats interesting in the above example is the way sign-in credentials are stored for each of the servers. Each server has the logonCredentials section configured, but with different profile scopes. As you could see possible values for the scope attribute are: Global, File and Local. The scope of the profile is set when saving credentials in the server properties window:

Remote Desktop Connection Manager unexpected XML node credentials profiles

Each profile in my test .rdg file is configured for the same user account (testuser) this is exactly the situation I had in my original .rdg file. When the profile has Local scope, the user credentials are stored next to the server properties (check TESTWIN server). File scope is broader and profiles with this scope can be used in the whole .rdg file. They are referenced by name, with the profile definition stored inside the credentialsProfiles tag at the beginning of the .rdg file. Finally, the Global scope profiles are also referenced by name, but stored in a different file: %LOCALAPPDATA%\Microsoft\Remote Desktop Connection Manager\RDCMan.settings. Here is the snippet of this file content:

False 0 ... Pinned testuser testuser AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA2RswPSIL7EGcvudhCBZVHwAAAAACAAAAAAADZgAAwAAAABAAAACaDuqy6sE/J2nxsbv5V9qCAAAAAASAAACgAAAAEAAAAFbeWSKnrdtQupAMws7sDmoQAAAAB3miuam6b6PgVZd0I5LKpRQAAAAKS3lrI5soUOeyptrPTXTLzE78UQ== ...

As you can see, here again we have the credentialsProfiles tag. Lets try to decrypt the passwords stored in those files.

Decrypting passwords and DPAPI

There two versions of the encrypted password for my testuser account:

  • AQAAANCMnd8BFdERj...AAAAIx3NxV/dDSNZ7iIQpKtAXKwFfL (File scope)
  • AQAAANCMnd8BFdERj...AKS3lrI5soUOeyptrPTXTLzE78UQ== (Local and Global scope)

We can guess that we are dealing here with the base64 encoded byte arrays and both arrays have the same beginning. The starting bytes also indicate that we are dealing here with DPAPI blobs (the block header is the same for various DPAPI blobs). Being signed-in as the owner of the blobs I may decrypt them in Powershell:

PS> Add-Type -AssemblyName "System.Security" PS> [System.Text.Encoding]::GetEncoding("UTF-16LE").GetString([System.Security.Cryptography.ProtectedData]::Unprotect($(ConvertFrom-Base64 "AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA2RswPSIL7EGcvudhCBZVHwAAAAACAAAAAAADZgAAwAAAABAAAAA0t7PykLWY32WfWsQZ8yEWAAAAAASAAACgAAAAEAAAAEx7e35J0SATgvXU94/RTbQYAAAAXzEXsudQGdC7cL9+y6jbOstaWUEdIQtKFAAAAAIx3NxV/dDSNZ7iIQpKtAXKwFfL"), $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)) test1234 PS> [System.Text.Encoding]::GetEncoding("UTF-16LE").GetString([System.Security.Cryptography.ProtectedData]::Unprotect($(ConvertFrom-Base64 "AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA2RswPSIL7EGcvudhCBZVHwAAAAACAAAAAAADZgAAwAAAABAAAACaDuqy6sE/J2nxsbv5V9qCAAAAAASAAACgAAAAEAAAAFbeWSKnrdtQupAMws7sDmoQAAAAB3miuam6b6PgVZd0I5LKpRQAAAAKS3lrI5soUOeyptrPTXTLzE78UQ=="), $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)) test123

So the mystery is solved the second password is invalid and should be replaced with the first one (those are not my real password, you know ;)). If both passwords were invalid and you would like to generate a DPAPI blob for a new one (to use it for instance in the Remote Desktop Connections Manager config file) you may run:

> [System.Security.Cryptography.ProtectedData]::Protect([System.Text.Encoding]::GetEncoding("UTF-16LE").GetBytes("test12345"), $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser) | ConvertTo-Base64 AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAUnvxAeKA7UqcRVYGP3OR/QAAAAACAAAAAAADZgAAwAAA ABAAAAAJr2SANVAA4TdDIWxMd0HGAAAAAASAAACgAAAAEAAAAJ7GiIfxWCVnRXiAffPHFKsYAAAA S8P+E7OVW33zx0Y3BD8z9W+lzGcATU/4FAAAAIWLt4O6kZJsk/TBmTWLSeukVhG7

We have found solution to my original problem, but we merely touched the subject of DPAPI. Its time to go a little bit deeper

DPAPI going deeper

Imagine we need to decrypt passwords from an offline .rdg file. To accomplish that we need to have access to the c:\users\{username}\AppData\Roaming\Microsoft\Protect folder this is the main folder where all DPAPI files are stored. We will also need to know the current user password or its hash (on older systems MD4, on newer SHA1). To decrypt the DPAPI blob we will use the dpapick toolkit written in Python. You would need Python 2.7 installed (I used the 32-bit version). You would also need two additional packages:

> pip install --egg M2CryptoWin32 > pip install python-registry

I made some changes to the dpapidec.py file in order to make it runnable. I also added a solution file so you may open the project in Visual Studio (you would need Python Tools for Visual Studio). The modified version of the dpapick can be found in my fork on github. We will decrypt the first of the two blobs listed earlier. Lets first convert it to binary and save to a file:

> ConvertFrom-Base64 "AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA2RswPSIL7EGcvudhCBZVHwAAAAACAAAAAAADZgAAwAAAABAAAAA0t7PykLWY32WfWsQZ8yEWAAAAAASAAACgAAAAEAAAAEx7e35J0SATgvXU94/RTbQYAAAAXzEXsudQGdC7cL9+y6jbOstaWUEdIQtKFAAAAAIx3NxV/dDSNZ7iIQpKtAXKwFfL" | Set-Content -Encoding Byte DPAPI-blob.bin

We are ready to run the dpapick tool (some parts of the output are stripped):

PS dpapick> python .\dpapidec.py --sid "S-1-5-21-609950197-4191880330-798146650-6124" --masterkey "c:\temp\Protect\S-1-5-21-609950197-4191880330-798146650-6124" --credhist "c:\temp\Protect\CREDHIST" --password "{my-secret-password}" "c:\temp\DPAPI-blob.bin" Found 1 keys: Attempting to unlock with password: #### MasterKeyFile 3d301bd9-0b22-41ec-9cbe-e7610816551f #### version = 2 Policy = 0x0 MasterKey = 136 BackupKey = 104 DomainKey = 372 + Master Key: Masterkey block cipher algo = DES3 [0x6603] hash algo = HMAC [0x8009] rounds = 24000 IV = ca7b00d7ed1c0e39d1f0a62fe1492eb6 key = ... hmacSalt = c883938fb0cbcbd3d1e57cff52dcea92 hmac = 5baf30cad87a35e92e46fa85b7383f4e8d60af4a hmacComputed = 5baf30cad87a35e92e46fa85b7383f4e8d60af4a key hash = 85f6ddd6eceb164d1d7ee6c02c3571a96bc28329 ciphertext = 015c909fe327fb2847d7bebe19f7c0337ee08556dd4bf68f8c37e0c0878e81f0ea1c426c53f945e312103e822d3108ec6372f41b0062c54735b328893f73383df7487a2aa167a0d5714c967002103c9bd2b0b0097b33b1b51cf37e02527e741471907d235987b61f + Backup Key: Masterkey block cipher algo = DES3 [0x6603] hash algo = HMAC [0x8009] rounds = 24000 IV = 0c2bccfe9d78df7d728df9f436a8fef0 key = ... hmacSalt = a5437369f874a3a84cc9f6c1ef25762a hmac = 8cd5e92c2a12b32d8cd8f2e392c58e9242d0d3d3 hmacComputed = 5038c291d9a9406aeeb74a2e0acdbf2b61db49d3 ciphertext = 50811627060dfb855b5258a2f8ae1d53e820fc419eabdf2576cc52ae3d893032465a132fbf2f0597d356f076a70c3e9da3265c5cf230b07ab12b081b1a6d127dde19f6da698b0992 + DomainKey block version = 2 guid = 8b5bbf28-6ba1-4d36-8fcd-8b974dfd1866 secret = ... accessCheck = ... Attempt to decrypt blob: DPAPI BLOB version = 1 provider = df9d8cd0-1501-11d1-8c7a-00c04fc297eb mkey = 3d301bd9-0b22-41ec-9cbe-e7610816551f flags = 0x0 descr = cipherAlgo = DES3 [0x6603] hashAlgo = sha1 [0x8004] salt = 34b7b3f290b598df659f5ac419f32116 hmac = 4c7b7b7e49d1201382f5d4f78fd14db4 cipher = 5f3117b2e75019d0bb70bf7ecba8db3acb5a59411d210b4a sign = 0231dcdc55fdd0d2359ee2210a4ab405cac057cb signComputed = 0231dcdc55fdd0d2359ee2210a4ab405cac057cb cleartext = 't\x00e\x00s\x00t\x001\x002\x003\x004\x00'

Dpapick was able to decrypt the cipher (the last line contains the clear text in UTF-16LE encoding) and we may analyze the output. The decryption starts from some MasterKey with a cryptic id: 3d301bd9-0b22-41ec-9cbe-e7610816551f (line 5) we might assume that this MasterKey is a key required to decrypt our DPAPI blob. MasterKey is located under the %APPDATA%\Microsoft\Protect\{user-SID} folder. If you open this folder you will see various files with GUIDs in their names. In my case I could find a file named 3d301bd9-0b22-41ec-9cbe-e7610816551f. My MasterKey file contains three types of encrypted keys: Master Key (line 11), Backup Key (line 22) and Domain Key (line 32). As we are in possession of the user password we can decrypt only the Master Key. With the decrypted MasterKey we may decipher DPAPI blob file (line 38). Notice that the DPAPI blob contains an id of the master key with which it was encrypted (mkey, line 41) thats why the dpapick knew where to start the decryption at the very beginning. I had a domain account and all master keys were encrypted with a key derived from my password hash. Things get more complicated if our CREDHIST file (%APPDATA%\Microsoft\Protect\CREDHIST, a file storing the history of our password changes) is not empty and Master Key was encrypted with one of our previous passwords. In such a case we first would need to extract the old password hash and only after that we might decrypt the Master Key.

Although I named this section DPAPI going deeper, I provided you with a birds eye view of the decryption process. If you are interested in details have a look at Michałs post dedicated to DPAPI its in Polish but armed with translator you should be able to understand everything. I also recommend reading the whole series of posts describing Encrypted File System (EFS) details. You may also debug the dpapidec script (for example in Visual Studio) and examine the decryption process. Finally, the dpapick project contains templates of the DPAPI structures for the 010 Editor with which you may examine in details the MasterKey and DPAPI-blob binary files.

Share this: