During one of our recent red teaming engagements we managed to get a foothold in a customer's domain. While gathering information we stumbled upon a hidden backup share on one of the servers. The hidden share contained a database and webroot backup for the PasswordState website password vault! PasswordState is an enterprise password management solution. This means we hit the jackpot, for obvious reasons.
Searching the Internet for a way to decrypt all password did not yield any results, so we decided to look into the product and write our own!
This post starts with an overview how the encryption works and ends with a ready-to-go PowerShell script to use during your red teaming engagements.
PasswordState installs itself to
by default. In this directory, the web application and service source code is installed. The
contains the executable file
, which is also the service binary. This is a perfect binary for starting our reverse engineering efforts, since this most likely contains either a reference to the code that decrypts passwords, or a reference to that code.
Reversing password encryption
The binary is a .NET Framework 4.5 binary. Using dnSpy it's possible to decompile this binary into (mostly) readable source code. The binary contains several namespaces, the most interesting being
This namespace contains the service class
Looking through the methods of this class, the
function stands out. This function adds a password to the password database, encrypting the plaintext in the process. The decompiled code snippet below shows where the application encrypts the password before storing it in the database.
// Token: 0x06000193 RID: 403 RVA: 0x0003EEA8 File Offset: 0x0003D0A8
public byte AES_Encrypt(string myString)
byte array = new byte;
bool flag = !this.FIPSMode;
RijndaelManaged rijndaelManaged = new RijndaelManaged();
rijndaelManaged.KeySize = 256;
rijndaelManaged.BlockSize = 256;
rijndaelManaged.Key = this.EncryptionKey;
array = this.encryptStringToBytes_AES(myString, rijndaelManaged.Key, rijndaelManaged.IV);
array = PasswordstateService.Combine(new byte
AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider();
aesCryptoServiceProvider.BlockSize = 128;
aesCryptoServiceProvider.KeySize = 256;
aesCryptoServiceProvider.Key = this.EncryptionKey;
aesCryptoServiceProvider.Mode = CipherMode.CBC;
aesCryptoServiceProvider.Padding = PaddingMode.PKCS7;
byte bytes = Encoding.Default.GetBytes(myString);
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateEncryptor())
byte array2 = cryptoTransform.TransformFinalBlock(bytes, 0, bytes.Length);
array2 = PasswordstateService.Combine(new byte
array = array2;
aesCryptoServiceProvider = null;
In both cases, the encryption key is taken from a class property
This property is set in the
method of the same class. This method gets 4 secrets, 1 and 2 from the web.config file and 3 and 4 from the database. Secret 1 and 3 are then combined into the
and secret 2 and 4 are combined into the
The diagram below shows which secrets are used for which key.
The simplified code snippet below shows this process for the
class comes from the import
This library allows a developer to split up secrets in two parts and store them apart from eachother. Combining both parts would lead to the original secret again.
Reversing the PasswordState decryption routine gives the following insights:
Getting the necessary information
As stated before the application stores its secrets spread over the database and the web.config file. Getting the secret from web.config is easy using PowerShell, since PowerShell is capable of parsing XML files and performing XPath queries. Getting the secret is a simple as:
In this code snippet we use XPath to select the necessary value.
Getting secret3 involves reading the database. As always, StackOverflow has the answer. Using the code found in that post getting secret3 is as easy as:
Combining the secrets to get the encryption key is done like so (after loading in the Moserware DLL):
Note: this key is a hex-encoded variant of the actual key. Using a snippet from the SANS website we can transform it into a byte-array.
So now we have the key, we need to write the decryption function in PowerShell. As mentioned before, PowerShell is able to use the RijndaelManaged class directly. The snippet below shows how to decrypt one single entry. PasswordState uses a non-default Key and Block size.
Putting it all together
Combining the things we discovered above leads to a PowerShell script that runs on the PasswordState server itself. If ran on the PasswordState server itself, it only needs the
location (and the SecretSplitter.dll location if it's not installed to the default location).
Alternatively, you can export all necessary secrets and run from another host, as shown in the screenshot below. You just need the secret1 and secret3 value (or the EncryptionKey) and all entries in CSV format.
Please use PowerShell's Get-Help to find even more information on how to use this script!
The script can be found on Northwave's GitHub.