Active Directory Certificate Services (AD CS) is a server role that allows you to build a public key infrastructure (PKI) and provide public key cryptography, digital certificates, and digital signature capabilities for your organization. Since the publication of “Certified Pre-Owned: Abusing Active Directory Certificate Services” in 2021 by Will Schroeder (@harmj0y) and Lee Christensen (@tifkin_), AD CS is prone to many attacks. One of them is more of a persistance technique, and is known as a shadow credentials attack.
A shadow credentials attack on Active Directory involves an attacker adding a certificate to a machine account that they control. This allows the attacker to authenticate as that machine and perform actions on its behalf, potentially gaining unauthorized access to resources. At the core of this technique is the LDAP attribute
msDS-KeyCredentialLink that stores the certificate used to authenticate to the machine account.
The LDAP attribute
msDS-KeyCredentialLink is used to store the public key credentials associated with a computer or user object. When a certificate is added to a machine account, it is stored in this attribute.
You can view these as a system administrator by going in the
Active Directory Users and Computers console, and activate
Then on the machine account that has certificates installed, go into
Attribute Editor then scroll down until finding the
In this attribute editor, we see:
| msDS-KeyCredentialLink | 'B:828:0002000020000108E2E5....' |
This string is actually a string version of a DN with Binary data blob.
Now that we know where to find it, it would be cool to understand the format and parse it!
Parsing the DN with Binary data blob
The DN with Binary data blob is a unique format that combines a distinguished name (DN) with a binary large object (BLOB). The distinguished name (DN) is a string that uniquely identifies an entry in the LDAP directory, while the BLOB is used to store binary data as an hexadecimal string. In the context of the
msDS-KeyCredentialLink attribute, the DN with Binary data blob is used to store the certificate added to a machine account. Parsing this data involves extracting the DN and the binary data from the blob, which can then be parsed.
Here is what the raw value of this attribute looks like:
B:828:0002000020000108E2E5700BC0E9522D434C139C15B767678284D922DF7E92BA0DD17D62407E9D20000 2A786DBE1C8FD5259753E75E32DE90CA2CBE04D2A2AF4DACC29DB7C6B06FE3E411B0103525341310008000003 000000000100000000000000000000010001CC86F6DEB74305A202C68F94489B82C499C6F21A74D5E290869E8 2FFF5B5774B961251741E42D7FEC1F5EEAE52B5C759DE321491987D6586F9F3672B4E5B88EB32827480D4444D 958275113832EA7B52E91E8ED414796E6AE9061BE323D3BBFACF4448FE2451DF9325393FE1189CE53E7AE6D02 E7710D71EB1F16B8ACCF13EB63F34834021DAF3398962A6C1DF4F359CE9A81BD2E6FA3D9DD095898C9FC2E231 6DD10B59A5C0C09A71EA12CE70E3AC6EA42C74E258A0C13F102577B9AB2B55658A4F72CAFCDF26B0EBB124EEB 3F7D97BD705D04FFA4B0D4C442F68CDC724A5DD4B42B114BB3A667A54F7A733A26F7AFC6FA09CEC688246EC32 494668148C09950100040101000500100006335A9368151ACC785211B3C9DA8EC9720200070100080008A7E1A 5606E06DA01080009A7E1A5606E06DA01:CN=DC01,OU=Domain Controllers,DC=LAB,DC=local
The DN with Binary string is divided into three parts:
The length of the hexadecimal string. The number following the colon (
828in this case) is the length of the hexadecimal string containing the binary data.
The hexadecimal string of raw data This is the binary data. In the context of the
msDS-KeyCredentialLinkattribute, this data represents the certificate added to a machine account.
The distinguished name of the object in LDAP. This distinguished name (DN) uniquely identifies an entry in the LDAP directory. In this example the distinguished name is
To parse this data, you would first separate the three parts based on the colons. Then, you would convert the binary data from hexadecimal to binary and the DN from its string representation to a structured format. Here is a summary of this DN with binary format:
Now we know how to parse the DN-Binary structure, we still have to parse the raw data that is present inside it.
Parsing the certificate
The raw certificate data is a
BCRYPT_RSAKEY_BLOB structure, a structure that represents a RSA key. In this case it is a public key but the structure can also be used to represent private keys. The BCRYPT_RSAKEY_BLOB structure contains information about the key, such as the key type, the length of the key, and the public exponent, followed by the modulus of the RSA key. Here is a schema to help visualize this structure:
Writing a library
To be able to write a Python version of Elad Shamir’s Whisker with my colleague Shutdown (we’ve called it
pyWhisker), and to make the process of parsing and generating
msDS-KeyCredentialLink values easier, I have created a Python library called
pydsinternals. This library is inspired by the
DSInternals library by Michael Grafnetter. The
pydsinternals library provides a set of tools and functions that allow you to parse and generate
msDS-KeyCredentialLink values from within your Python scripts.
The library includes classes such as
KeyCredential, which represent the previously described DN with Binary data blob and key credential respectively. These classes provide methods to parse the raw data into a structured format and to generate a new X509 certificate and export it in the expected format for the
Here is an example of how you can use the
pydsinternals library to parse a
# Import the necessary modules from dsinternals.common.data.hello.KeyCredential import KeyCredential from dsinternals.common.data.DNWithBinary import DNWithBinary # The msDS_KeyCredentialLink attribute is a DN with Binary that contains the certificate added to a machine account msDS_KeyCredentialLink = b"B:828:0002000020000108E2E5700BC0E9522D434C139C15B767678284D922DF7E92BA0DD17D62407E9D200002A786DBE1C8FD5259753E75E32DE90CA2CBE04D2A2AF4DACC29DB7C6B06FE3E411B0103525341310008000003000000000100000000000000000000010001CC86F6DEB74305A202C68F94489B82C499C6F21A74D5E290869E82FFF5B5774B961251741E42D7FEC1F5EEAE52B5C759DE321491987D6586F9F3672B4E5B88EB32827480D4444D958275113832EA7B52E91E8ED414796E6AE9061BE323D3BBFACF4448FE2451DF9325393FE1189CE53E7AE6D02E7710D71EB1F16B8ACCF13EB63F34834021DAF3398962A6C1DF4F359CE9A81BD2E6FA3D9DD095898C9FC2E2316DD10B59A5C0C09A71EA12CE70E3AC6EA42C74E258A0C13F102577B9AB2B55658A4F72CAFCDF26B0EBB124EEB3F7D97BD705D04FFA4B0D4C442F68CDC724A5DD4B42B114BB3A667A54F7A733A26F7AFC6FA09CEC688246EC32494668148C09950100040101000500100006335A9368151ACC785211B3C9DA8EC9720200070100080008A7E1A5606E06DA01080009A7E1A5606E06DA01:CN=DC01,OU=Domain Controllers,DC=LAB,DC=local" # Parse the DN with Binary data blob data = DNWithBinary.fromRawDNWithBinary(msDS_KeyCredentialLink) # Create a KeyCredential object from the parsed data kc = KeyCredential.fromDNWithBinary(data) # Display the parsed contents of the KeyCredential object kc.show()
And you will get a complete description of your certificate:
DN with Binary object
Shadow credentials attack