Incorrect password in Remote Desktop Connections Manager (with some DPAPI insights)


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 didn’t 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. It’s a simple xml file and its structure is quite straightforward. A very simple .rdg file with three servers defined might look as follows:

<?xml version="1.0" encoding="utf-8"?>
<RDCMan programVersion="2.7" schemaVersion="3">
  <file>
    <credentialsProfiles>
      <credentialsProfile inherit="None">
        <profileName scope="Local">testuser</profileName>
        <userName>testuser</userName>
        <password>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA2RswPSIL7EGcvudhCBZVHwAAAAACAAAAAAADZgAAwAAAABAAAAA0t7PykLWY32WfWsQZ8yEWAAAAAASAAACgAAAAEAAAAEx7e35J0SATgvXU94/RTbQYAAAAXzEXsudQGdC7cL9+y6jbOstaWUEdIQtKFAAAAAIx3NxV/dDSNZ7iIQpKtAXKwFfL</password>
        <domain />
      </credentialsProfile>
    </credentialsProfiles>
    <properties>
      <expanded>True</expanded>
      <name>test-group</name>
    </properties>
    <server>
      <properties>
        <name>TESTWIN</name>
      </properties>
      <logonCredentials inherit="None">
        <profileName scope="Local">Custom</profileName>
        <userName>testuser</userName>
        <password>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA2RswPSIL7EGcvudhCBZVHwAAAAACAAAAAAADZgAAwAAAABAAAAAC1TS+S/9utgkT7FK9hzaHAAAAAASAAACgAAAAEAAAAGZ4jbOqYepnHT6+dOvhKGEQAAAADZcV+0aHOj6WN6kkVgau1RQAAACrW1CxTftLzUJk+SB4tsvyttrSpw==</password>
        <domain>DELAPTOP</domain>
      </logonCredentials>
    </server>
    <server>
      <properties>
        <name>TESTWIN2</name>
      </properties>
      <logonCredentials inherit="None">
        <profileName scope="Global">testuser</profileName>
      </logonCredentials>
    </server>
    <server>
      <properties>
        <name>TESTWIN3</name>
      </properties>
      <logonCredentials inherit="None">
        <profileName scope="File">testuser</profileName>
      </logonCredentials>
    </server>
  </file>
  <connected />
  <favorites />
  <recentlyUsed />
</RDCMan>

What’s 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-conn-manager

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:

<?xml version="1.0" encoding="utf-8"?>
<Settings programVersion="2.7">
  <AutoSaveFiles>False</AutoSaveFiles>
  <AutoSaveInterval>0</AutoSaveInterval>
  <BuiltInGroups>
     ...
  </BuiltInGroups>
  <ConnectionBarState>Pinned</ConnectionBarState>
  <CredentialsProfiles>
    <credentialsProfiles>
      <credentialsProfile inherit="None">
        <profileName scope="Local">testuser</profileName>
        <userName>testuser</userName>
        <password>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA2RswPSIL7EGcvudhCBZVHwAAAAACAAAAAAADZgAAwAAAABAAAACaDuqy6sE/J2nxsbv5V9qCAAAAAASAAACgAAAAEAAAAFbeWSKnrdtQupAMws7sDmoQAAAAB3miuam6b6PgVZd0I5LKpRQAAAAKS3lrI5soUOeyptrPTXTLzE78UQ==</password>
        <domain />
      </credentialsProfile>
    </credentialsProfiles>
  </CredentialsProfiles>
  ...
</Settings>

As you can see, here again we have the credentialsProfiles tag. Let’s 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> [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. It’s 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. Let’s 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) – that’s 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 bird’s eye view of the decryption process. If you are interested in details have a look at Michał’s post dedicated to DPAPI – it’s 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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s