$theTitle=wp_title(" - ", false); if($theTitle != "") { ?>
About Virtualization, VDI, SBC, Application Compatibility and anything else I feel like
The Dell vWorkspace (previously Quest vWorkspace) Client can save a connection to a .pit file which is very similar to an .rdp file with one big difference: it is encrypted!
I am not sure why Dell/Quest have chosen to encrypt their files but a while ago I needed to know what was in a particular pit file so I could troubleshoot an issue.
I first created a test .pit file with the client (pntsc.exe version 7.6.305.791).
Using the Save As option I saved it as test.pit and the I opened it again via the Open option while monitoring the process with Process Monitor:
In the Process Monitor screenshot you can see that a few moments after opening the .pit file a temporary file tmpACE4.tmp is opened. Perhaps an intermediate file with the unencrypted version?
I opened pntsc.exe in Ida Pro and searched for pit in the strings tab:
There was only one reference to this string:
This code verifies if the file has a .pit extension and then copies the file to the temp path with a .tmp extension.
Then it calls into sub_41C91C with the tmp file, possibly to decrypt the file. To make it more readable I used Ida’s Rename function to rename sub_41C91C to DecryptPitFile
I tried this using Ida Pro’s Appcall mechanism. Appcall is a mechanism to call functions inside a debugged program from the debugger or your script as if it were a built-in function.
I copied my test.pit file to C:\Temp and launched pntsc with Ida’s integrated debugger. Once the GUI was shown I Paused the process and openend the Script Command Window (Shift-F2).
I intered the function’s name: sub_41C91C with the filename as a parameter and pressed the Run button:
There was no output in Ida, however my test.pit file was decrypted:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | full address:s:MyServer domain:s:MyDomain username:s:MyUser password:s:01000000D08C9DDF0115D1118C7A00C04FC297EB010000006987A9C4E1D48E4CB9DAFB23005EF0FB000000000C00000070006E007400730063000000106600000001000020000000293AD44A6A7DB316F785F0C3ADBA10208E497A3D23DF7D809E5B0A6C4591CF90000000000E8000000002000020000000830D9CE26181EC7FD236DEB2981707C4D23B17C32640A1807778B39C46B5FEEF10010000294D6A20B9B15F4B770C1467EEFA3C1AF4C0E333876B04E9FD6C7236585DCCD55D1A7C1ABFB1201F48CF9D382D68001CF93C561CFF1BFC48A23AAD4ECA16C7EED6E5F38DBB9225A23609C40EDC4230BA9C38EFC2033E9028E9084C6013025F6404B72F79BDAFCB7F65006894EAD1E2DADAA1611D13A1A29A755800EB2F41D560771AF79853BE1D3A47DE506737E4A6A989FB09DBFF6906E6745F7F22DCA7A0FA5C79431DC9975D0658634500F3E84CF746480EB8B0EF07853AF121BA0F41DD78A9E7CF9DA2C4B1F93E9FBDBDB0C0C6E9E2F2CC76EF410190316F4197768A0418F03A95DF76E6A719251796ABC1904250421107FBCEC504DBB0F3B7DEF4764EE97F4CF964AEDA02A12ADA8B6BEE54739D40000000BC5A7613C4E238867FC459E79528A9DC973637C390CE22BD143DA457FA0511ED5E08BB77A7AAEDBAD5829E3BB898D85BD2B7EC13DA0EEDEAB8CA711200FAE54A enablesso:i:0 enablekerberos:i:0 screen mode id:i:2 desktopwidth:i:1920 desktopheight:i:1080 session bpp:i:8 displayconnectionbar:i:1 pinconnectionbar:i:1 spanmonitors:i:0 smartsizing:i:0 audiomode:i:0 keyboardhook:i:0 redirectclipboard:i:1 redirectdrives:i:0 redirectprinters:i:0 redirectcomports:i:0 redirectsmartcards:i:0 redirecthandhelds:i:0 redirectmicrophone:i:0 redirectuniversalprinters:i:0 seamless:d:0 connection type:i:6 disable wallpaper:i:1 disable full window drag:i:1 disable menu anims:i:1 disable themes:i:1 bitmapcachepersistenable:i:0 enabledesktopcomposition:i:0 enablefontsmoothing:i:0 bitmapacceleration:i:0 localtextecho:i:0 multimediaredirection:i:0 flashredirection:i:0 autoreconnection enabled:i:1 compression:i:1 encryption:i:1 useproxy:i:1 editable:i:1 |
My first solution was to build a tool that works similar to Ida’s Appcall. It launched the vWorkspace client but made sure the window was hidden.
Then it allocated and wrote a string (the filename) to the process and created a thread in it using the CreateRemoteThread API setting the lpStartAddress to the sub_41C91C function and the lpParameter address to the allocated string.
Last step is resuming the thread and waiting for it to finish.
I placed all that code in a dll and that was enough to solve my problem. If you are interested in the code, I have included it in the download at the bottom of this post.
This solution depends on the exact same version of the pntsc.exe client though so it would be better to understand the encryption algorithm.
In Ida Pro a large (256) byte array was visible and screaming for attention:
This byte array that I’ve renamed to QuestKey here is likely an encryption key or salt. In the code can we see that is used in a function that I’ve renamed to KeySchedule
I had a chat with Benjamin Delpy about the encryption algorithm and he was very quick to recognise rc4 in the asm, impressive!
So if we look at the code snippet above we can conclude that a double rc4 encryption is used. The pit file is first encrypted with a random, 16 byte key and the output is encrypted a second time using a fixed key (the large byte array).
Finally a sanity check is performed, the content must start with the sequence PIT.
So in order to decrypt a pit file we must do the following:
I decided to do this in PowerShell because it would be a nice exercise (in fact, that’s why I wrote the rc4 function I published earlier).
The complete script can be downloaded at the bottom of the post and includes the rc4 function, an example pit file and the “Appcall” dll example.
First we need to define the large byte array in PowerShell:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | $QuestKey = @( 0x80, 0x5B, 0xE8, 0x18, 0x6F, 0x64, 0x89, 0x3A, 0x34, 0xCE, 0x59, 0xDF, 0x4D, 0xB4, 0x5A, 0x0F, 0x69, 0x94, 0x58, 0x70, 0x71, 0x4B, 0x17, 0xCF, 0xC3, 0x40, 0xAA, 0xFC, 0xC5, 0xE0, 0x21, 0xDB, 0x9A, 0x49, 0x68, 0xB8, 0x2F, 0x4A, 0x6C, 0xDC, 0x7A, 0x8B, 0x7F, 0x5C, 0x03, 0x08, 0xFE, 0x39, 0xA3, 0xC6, 0x31, 0xA6, 0x8C, 0xBD, 0x72, 0xA4, 0x8A, 0x1B, 0x92, 0xD5, 0x87, 0xAD, 0x78, 0x8F, 0x55, 0x96, 0x0B, 0x30, 0xA8, 0x43, 0x53, 0xB0, 0x62, 0xA0, 0xDA, 0x7C, 0x13, 0x8D, 0x5D, 0x81, 0xC0, 0x8E, 0x90, 0x88, 0xE4, 0xB7, 0x76, 0xC2, 0xB5, 0x04, 0x93, 0xA5, 0xA9, 0x9E, 0xAB, 0xF5, 0x37, 0xAC, 0x99, 0x26, 0xE2, 0x38, 0x85, 0xE1, 0x74, 0x77, 0x32, 0xE5, 0x91, 0x23, 0xB1, 0x10, 0x4C, 0x47, 0x3F, 0xBE, 0x82, 0x22, 0x6A, 0x51, 0xD0, 0x63, 0x75, 0x11, 0x33, 0x9B, 0xFB, 0x3B, 0xCA, 0xED, 0xDD, 0x44, 0xE6, 0x12, 0x4E, 0x97, 0x3C, 0x79, 0x4F, 0x41, 0x66, 0xBA, 0x50, 0x0E, 0xC9, 0x6B, 0x05, 0xEE, 0x6E, 0xE7, 0x95, 0x7B, 0x60, 0x9D, 0xFF, 0xC4, 0x29, 0x86, 0xB9, 0x7D, 0x98, 0xC8, 0x9C, 0x35, 0xBB, 0xBC, 0xEF, 0xFA, 0x3D, 0x06, 0xF9, 0x36, 0xBF, 0x3E, 0x7E, 0xA2, 0xC7, 0x56, 0xAE, 0xCB, 0xAF, 0xE9, 0x42, 0x61, 0xF0, 0x1D, 0xFD, 0x65, 0x9F, 0x52, 0x27, 0xEA, 0x24, 0xA1, 0xA7, 0xB2, 0x6D, 0x14, 0xB3, 0x45, 0xF8, 0xB6, 0xF7, 0x73, 0xC1, 0x83, 0x84, 0xF4, 0xCC, 0xCD, 0xF3, 0xE3, 0x54, 0x15, 0xD1, 0x46, 0x07, 0x57, 0x2C, 0xD2, 0xD3, 0xD6, 0xD4, 0xD7, 0xF6, 0xEB, 0xD8, 0x1C, 0x00, 0x09, 0xEC, 0x67, 0x0A, 0xD9, 0x16, 0xDE, 0xF1, 0xF2, 0x01, 0x2D, 0x5E, 0x48, 0x02, 0x0C, 0x5F, 0x0D, 0x19, 0x1A, 0x28, 0x1E, 0x1F, 0x20, 0x25, 0x2A, 0x2B, 0x2E) |
Then the function that decrypts the PIT file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function DecryptPITFile([string]$SourceFile, [string]$DestinationFile) { # Read Encrypted PIT file as a byte array [Byte[]]$Buffer = gc -encoding:Byte -Path $SourceFile # Decrypt RC4 rc4 $Buffer $QuestKey # Last 16 bytes contains another RC4 key that differs per pit file [Byte[]]$PitKey = New-Object Byte[] 16 # Copy last 16 Bytes [array]::Copy($Buffer, $Buffer.Length-16, $PitKey, 0, 16) # Decrypt RC4 rc4 $Buffer $PitKey # Buffer should start with PIT if ([char]$Buffer[0] -ceq 'P' -and [char]$Buffer[1] -ceq 'I' -and [char]$Buffer[2] -ceq 'T') { # Copy Buffer after PIT upto PitKey to $Output $Output = New-Object Byte[] ($Buffer.Length - $PitKey.Length - 3) [array]::Copy($Buffer, 3, $Output, 0, $Output.Length) # Save the Decrypted PIT file to disk $Output | Set-Content $DestinationFile -Encoding:Byte } } |
Usage is very simple:
1 | DecryptPITFile "in.pit" "out.pit" |
If you look at the unencrypted pit file you will notice that it’s very similar to the contents of an rdp file. This is logical since vWorkspace is built on top of RDP.
The password is only included when you’ve checked the “Save my password (encrypted) checkbox:
The password is encrypted using CryptProtectData, again similar to RDP passwords (as I described earlier).
However the password is encrypted before being passed to Crypt(Un)ProtectData.
DecryptPITFile.zip (8737 downloads )
One Response for "Decrypting Dell vWorkspace .pit files"
Hi Remko,
First of all, great work! Script runs fine. however, and I don’t know if it’s because the client version (8.6.309.2722), the decoded pit seems to contain another RC4 encoded string:
url:s:14061602040A7AE57AE40B48E9A0BC02D53FE51472B0…B81A
And that’s the only line in that pit file.
Any clues on how I can get a readable string?
I’m checking some issues and need to know the contents of this string to see if it’s garbled or not.
Greetz.
Leave a reply