Abusing DPAPI

Z3R0th
10 min readJan 4, 2021

--

Abusing DPAPI is no new attack vector by any means. I think the earliest presentation I’ve come across is from 2010, though I don’t know of many presentations or posts where it’s broken down to a very simple level, which is what I aim to do in this post.

First off, we’re only going to be talking about abusing DPAPI at a user level. There are still other ways to attack it though, such as offline attacks, via web browsers, or by attacking standalone systems. If you have no idea what any of this means then you’re in the right place.

What is DPAPI?

DPAPI stands for the Data Protection API. It’s a component that’s built into Windows as a means for encrypting/decrypting data. The keys utilized are tied to either a specific user or computer, and allows for both Windows functionality as well as third-party applications to access information that is otherwise protected.

Why is this important?

Since DPAPI is used by many different applications in order to store credentials this becomes something that’s attractive to attackers. Some programs that utilize this API are Internet Explorer, Google Chrome, and Skype. Though this is by far no means an exhaustive list. It’s also used by Windows in order to store sensitive information such as EFS (Encrypting File System) certificates and Wi-Fi (WEP and WPA) keys.

API calls broken down

DPAPI_IMP BOOL CryptProtectData(DATA_BLOB                   *pDataIn,LPCWSTR                     szDataDescr,DATA_BLOB                   *pOptionalEntropy,PVOID                       pvReserved,CRYPTPROTECT_PROMPTSTRUCT   *pPromptStruct,DWORD                       dwFlags,DATA_BLOB                   *pDataOut);

Here is the API call, and to non-programmers this may make no sense and look completely overwhelming. But actually it’s quite simple! Let’s take a closer look.

  • DATA_BLOB is the data that we wish to be encrypted.
  • LPCWSTR is the optional description of the data.
  • The second DATA_BLOB is the optional entropy (salt) to be used. (Windows provides (p)random data to be used as a salt when storing credentials).
  • PVOID is reserved for future use, and must be set to NULL
  • CRYPTPROTECT_PROMPTSTRUCT is the optional password to be used with the encrypting of data.
  • DWORD is for the setting of optional protection flags.
  • And the final DATA_BLOB is the actual encrypted data that’s returned to you.

Decryption API

DPAPI_IMP BOOL CryptUnprotectData(DATA BLOB  *pDataIn,LPWSTR  *ppszDataDescr,DATA_BLOB  *pOptionalEntropy,PVOID  pvReserved,CRYPTPROTECT_PROMPTSTRUCT  *pPromptStruct,DWORD  dwFlags,DATA_BLOB  *pDataOut);

We see that the CryptUnprotectData() call is pretty much the same exact thing as CryptProtectData(). Though instead of passing in what you want encrypted, you pass in what you want decrypted. Simple enough right? If you’re still a little bit confused and learn better with pictures (I know I do), then the following illustration will hopefully help.

We can see the application calling CryptProtectData() and passing in “MySecret” as the data to be encrypted and getting “hOs2kQ5l” returned as the encrypted data.

Something that does happen that’s unfortunately not in this illustration happens in the DPAPI block. When a DPAPI function is called they also make a local RPC call to the Local Security Authority (LSA), which in turn talk back to the CryptoAPI using Crypt32.dll for the actual encryption/decryption in the security context of LSA. It’s ran this way so that audits can be generated. It’s also worth noting that these API calls don’t actually store any bit of the encrypted data, they merely apply cryptographic protection to data and leave it up to the application in order to store it.

Lets get down to business

Now that we sort of understand what DPAPI actually is, let’s jump into how Windows uses it so we can learn how to attack it.

Excuse my poor illustration capabilities

In the above illustration we can see what the entire process looks like when Windows is storing credentials. We can see that the MasterKey is generated using Password-Based Key Derivation (PBKDF2) by encrypting the result with 3DES. The final product is then stored within the users profile directory as the MasterKey blob (Not Shown). What’s worth noting is that the MasterKey does not explicitly protect the data. Instead, a symmetric session key is generated based off of the MasterKey, some random data, and any additional entropy. All of this combined is then used to protect the data. The symmetric key is stored within the data of the credential blob. So when it comes back to decryption, the symmetric key is passed back in to DPAPI which is then used alongside the MasterKey in order to decrypt the data.

This process of created a MasterKey is repeated every three months since MasterKeys expire. This is done in order to try and mitigate attackers compromising keys. Another time this process is repeated is when a user changes their password. Though when this happens the filesystem will then create a “Credential History” file within the users profile directory. This can subsequently be compromised in order to gain access to old passwords which can then be used in a wordlist to find possible variations/mutations of new passwords. Though that’s for another day.

Why keep a credential history then?

Well, it’s actually got a decent enough reason of creating this file. Say you want to decrypt some data but you don’t have the specific password you used whenever initially encrypted. DPAPI will then try the old password (stored in the credential history) and try to decrypt the MasterKey. This process is repeated until the MasterKey is decrypted.

But what do I need to attack?

I see a lot of different posts and presentations saying that you need Domain Admin level access in order to take advantage of DPAPI. While this is definitely true for some, it’s not needed if we want to take a more targeted approach. The very minimum we’re going to need are:

  • The User’s SID (Security Identifier)
  • The User’s MasterKey(s)
  • The User’s Password (Only if we don’t have access to the domain backup key)

Say we have our initial access through whatever means, but we don’t have the user’s password. One way that we can go about getting this is through DPAPI abuse. We can easily find our user’s SID in a multitude of ways, though we can also find it within our users profile directory, the same is true for our user’s MasterKey(s). We shouldn’t need the password since we’re going to go about requesting the domain backup key to decrypt our MasterKey.

This is where our credential blobs are stored

C:\users\<user>\appdata\local\microsoft\credentials\<blob>

This is where our MasterKey(s) are stored

C:\users\<user>\appdata\roaming\microsoft\protect\<SID>\<MasterKey>

Let’s run through our attack

Here we can see that we’re a regular user with no special permissions. For our scenario’s sake, we gained access through phishing and received our callback.

Nothing special to see

Our first step is to verify that there are items stored within the Windows Credential Vault, we can do this with the following.

We have two credential blobs stored, but we’re going to take a look at that first one.

If we download this file and then try and view the contents, we unfortunately gain nothing at all.

Look at that lovely binary data.

Luckily for us, Mimikatz has some DPAPI functionality built into it!

Peep that guidMasterKey and pbData tho

Using the following command we are able to see some of the contents within the credential blob. While it is all pertinent information, as attackers we are mainly looking at the guidMasterKey and the pbData. The guidMasterKey is going to correlate to a specific MasterKey and the pbData is the actual encrypted credential data.

This command is ran through Cobalt Strike, if you’re using something different then the syntax will change.

mimikatz dpapi::cred /in:C:\users\<user>\appdata\local\microsoft\credentials\<credential blob>

Now that we’ve seen our credential blob, we need to hunt for the correlating MasterKey for decryption. If we navigate to where our MasterKey(s) are stored, we see that we have only one (in our example). This filename should match up with our guidMasterKey from the previous step! Though it should be noted that this is not our cleartext MasterKey, we still have to do a little bit more work in order for this to be beneficial.

Since we’re on a domain joined machine we are able to query the Domain Controller for the backup key in order to decrypt our credential blobs. This can be obtained with yet another simple Mimikatz command.

mimikatz dpapi::masterkey /in:C:\users\<user>\appdata\roaming\microsoft\protect\<SID>\<MasterKey blob> /rpc

This is us telling Mimikatz to interact with the DC through the MS-BKRP (BackupKey Remote Protocol) RPC service. This service handles the RSA private key associated with all of the MasterKey(s) in the domain. Additionally it’s in charge of the decryption of MasterKey(s), though only to authorized users. This means we can’t go about requesting MasterKey(s) for other users as a regular user. If this command executes correctly we can see that we are given the cleartext of the MasterKey as well as the SHA-1 hash of it. In the next command we’re going to use the cleartext version of the key, though it’s also interchangeable with the hash.

As long as the MasterKey we gained correlates with a credential blob we are able to decrypt it. In our example we see that our user somehow has the Administrator credentials saved and the computer they go to. Which in our case was the Domain Controller.

The TargetName field shows what computer the credentials are for

Attacking At Scale

So now that we have Domain Administrator credentials we can take this a step further. Nobody really wants to go one by one for each user and credential blob as that would be a large amount of time invested, as well as requests made to the domain controller. We can save both time and effort by using SharpDPAPI. If you’re not familiar with it definitely check it out here!

Since we’re a Domain Administrator we have sufficient permissions to request what’s called the Domain Backup Key. This is significant because this key allows for the decryption of ANY MasterKey(s) throughout the domain. And better yet, this key NEVER changes! Talk about fortuitous right?

Here we’re requesting the backup key and storing it into a file called key.pvk
SharpDPAPI.exe backupkey /file:key.pvk

But do I have to save it as a file? Well, no. But it definitely makes it easier. If you want to copy/paste it as a base64 encoded string feel free to.

This is what we get if we don’t specify an output file.
SharpDPAPI.exe backupkey

With the following command we are able to decrypt every single users MasterKey(s) on our target system! We can then correlate each MasterKey to a user based on the MasterKey blob filename.

SharpDPAPI.exe masterkeys /pvk:key.pvk

Using the above method unfortunately still requires us to go one by one with the credential blobs in order to find possible credentials. And earlier I promised that it would be much simpler and time saving. This can be done using the <triage> command within SharpDPAPI alongside the Domain Backup Key we saved earlier. This command will run through the user Credentials, Vaults, RDG (Remote Desktop Gateway), and Certificates.

Look at those creds!
SharpDPAPI.exe triage /pvk:key.pvk

Wow! That’s definitely a lot faster than manually decrypting! But how does all of this actually look?

Here we can see the process of manually decrypting credential blobs

As we’ve learned so far:

  • We find our credential blob(s) and correlate that with a MasterKey blob
  • We then talk to the DC over RPC in order to decrypt the MasterKey blob
  • Using the MasterKey we are then able to decrypt the credential blob in order to gain plaintext credentials

In Summary

What’s interesting about this attack vector is that we don’t need any special permissions in order to perform it (as a user). A general user on a domain should theoretically have absolutely everything we need in order to grab their credentials. Though somewhat of a limitation is that we do need to communicate with the Domain Controller in order to grab the backup MasterKey(s). But as we’ve learned, if we have Domain Admin rights we can just grab everything in one fell swoop.

But what about defenders?

Defense, I haven’t forgotten about you! But unfortunately there’s not a massive amount of countermeasures you can take since this attack vector is abusing a legitimate Windows function. That being said, there are four Event ID’s which directly correlate to DPAPI use and are somewhat rare of an occurrence. So it’s worth looking into them, especially Event ID 4692 as it very rarely occurs. Always look more into these types of events as a cursory glance doesn’t really tell you much.

•Event ID: 4692. Backup of data protection master key was attempted.•Event ID: 4693. Recovery of data protection master key was attempted.•Event ID: 4694. Protection of auditable protected data was attempted.•Event ID: 4695. Unprotection of auditable protected data was attempted.

Don’t just take my word for it either. Other security researchers such as Harmj0y and TrustedSec both agree that creating a solid alert for this type of activity would not be entirely effective due to the false positives it creates. Harmj0y also did break down what this type of traffic looks like on a network level for those willing to try and find something to detect on, though to me it looks like valid communication.

•An SMB connect to the IPC$ interface on the remote system•The creation of the protected_storage named pipe on the remote system•Several RPC over SMB calls with encrypted stub data portions•Reading the backup key from the protected_storage named pipe•Cleanup

Possible Mitigations

That being said, all hope isn’t lost quite yet. We can still disable Windows Credential Manager since it’s enabled by default. If this option isn’t available to use we can push a GPO to not allow storage of passwords and credentials for network authentication. An argument to be made for straight up disabling Windows Credential Manager is that it’s a legacy application that can apply more risk than benefit within an organization. And if we already have credentials stored within it, we can simply delete them from the Credential Manager GUI.

--

--

Z3R0th

A neophyte in a world of giants. Hit me up on twitter at @DarthSnorlax