At XM Cyber, we have been hard at work on the techniques that attackers use against your VMware environments. What you’re about to read is the fruit of our ongoing research. You might think that your VMware passwords are safe. But to an experienced hacker, even these are up for grabs.
When you connect to an ESXi or Vsphere server from your VMware Workstation, you have the option to save the connection details on the computer, including the password. On your next connection to the server, you won’t have to enter the credentials again.
A classic “remember me” feature.
Using reverse-engineering methods, we analyzed the decryption flow and found that we can decrypt the saved VMware credentials on our own. And if we can do it, there are experienced hackers that can do the same.
In this blog, we’ll show you how the hacker steals your VMware credentials, step by step. At the end, we’ll present to you the tool that we created that does exactly this, to make red teamers’ lives easier.
Let’s jump into the research.
We started the research by supposing that the credentials are saved locally, within a file or a registry key. In order to find exactly where, we used Procmon from the SysInternals tools.
We can see that when we add or remove credentials through the Workstation, the following files are being accessed:
Let’s take a look at these two files:
The files seem to be encrypted. So we need to understand how, and to decrypt them. We will start with the preferences-private.ini file.
Let’s take a look at these files.
The file contains the properties: .encoding, encryption.userKey, encryption.keySafe, encryption.data
The encryption.userKey value is encrypted with Data Protection API (DPAPI), recognizable with the first constant ~60 bytes metadata.
DPAPI allows one to encrypt data using information from the current user. Thus, only the user that performs the encryption can decrypt the data.
Let’s write a simple .Net application that uses the Unprotect function and try to decrypt the data:
We execute the application and get the decrypted data:
We got an encryption algorithm type, AES-256 – and we also got a key. Let’s call it KEY_1.
We now need to decrypt the values of the other properties found in the preferences-private.ini file, which are encryption.keySafe and encryption.data. To do that we need to know, for each of the properties, the algorithm used, the Initialization Vector (IV), and the secret key.
To find these unknowns, we will use static and dynamic reverse engineering analysis techniques. Looking at the call stack in Procmon shows us which DLL files and functions required the access to the encrypted files.
In the vmwarebase.dll file, we find a call to the BCryptDecrypt function, a WinAPI function used to decrypt data:
Right before the function call, we can see the IV, as well as the encrypted data sent to the function and the decrypted data returned, pushed to the stack. Looking at the MSDN documentation tells us that the last parameter sent to the function, hKey, is a handle of the key used to decrypt the data. This handle is obtained from the BCryptGenerateSymmetricKey key creation function:
The pbSecret parameter contains the key (after base64 decoding) used for the encryption and decryption process.
Dynamic analysis should help us get the actual IV and secret key values, and to understand the full decryption flow. Let’s debug vmware.exe with WinDBG, sync the address base image with IDA, insert breakpoints, and analyze the flow.
We insert a breakpoint on the BCryptGenerateSymmetricKey function call, and then we display the content of the fifth parameter on the stack, pbSecret, which is the secret key.
After base64 encoding, we get:
This is our KEY_1, obtained from the DPAPI decryption above.
Now we insert a breakpoint on the BCryptDecrypt function call, and then we will see which payload is decrypted with this key and which IV is used:
The second parameter on the stack is the encrypted payload and the fifth is the IV:
After base64 encoding, we can see that this data corresponds to the encryption.keySafe value from the preferences-private.ini file. The first 16 bytes are the IV, and the rest is the encrypted data.
The decryption result is:
In brief, KEY_1 is used to decrypt the encryption.keySafe value, and the decrypted value is another secret key, which we’ll call KEY_2:
Moving forward, we can use the same method, debugging calls to the BCryptDecrypt function, in order to complete our understanding of the decryption flow.
Using KEY_2, we’re able to decrypt the encryption.data property. Again, the first 16 bytes are the IV, and with the AES-256 algorithm, the decrypted value is:
Good, we succeeded in decrypting the configuration, and we have the hostname and username. But… the password is still encrypted. What is the decryption key for the password?
For that, we’ll move on to the second file saved by the Workstation, %AppData%\VMware\ace.dat.
Here is the content of the file:
You can see that the data value is encrypted. We’ll call it DATA_1.
Tracing the stack call to the BCryptGenerateSymmetricKey function takes us to a hardcoded key in the vmwarebase.dll file. This key is used to decrypt the ace.dat file content:
Let’s use it and decrypt the DATA_1 value. Here is the decrypted data:
The data contains a key derivation function, PBKDF2-HMAC-SHA-1, a salt, and an encrypted data value, which we’ll call DATA_2.
The PBKDF2 algorithm is used to generate a key based on a secret value and a salt. The secret value is hardcoded in the vmwarebase.dll file:
Let’s generate the key and call it KEY_3:
Using KEY_3, we’ll decrypt the DATA_2 value.
Here is the decrypted data:
And we get another key. Using this key, we decrypt the passwords from the configuration decrypted above.
The IV used is the first 16 bytes of the encrypted password.
Finally, here’s the password in clear text!
Open source tool
The above decryption flow is quite long and complex, so we also wrote a tool that will do the job. Just run it and it will decrypt the VMware Workstation configuration file, including the password.
It can also verify that the extracted credentials are valid by trying to connect to the server.