DES Is Useful... Sometimes

Posted on Tue 08 August 2023 in Active Directory

Data Encryption Standard (DES) is a symmetric-key block cypher algorithm created in the 1970’s and was historically used within Active Directory (AD) environments to secure data. Most notably, DES was used as an encryption type for Kerberos tickets, although on modern environments it is disabled by default. This tweet by Steve Syfuhs mentions that DES is uninteresting and “off by default” but that got me thinking, what is involved in re-enabling it?

In this research, I’ll describe exactly how DES can be re-enabled and when it is enabled, how to use DES to takeover any AD account that isn’t krbtgt or a trust account.

Domain Controller Configuration

The main configuration required for DES to be enabled for Kerberos on a domain is at the domain controller’s level. The following GPO setting can be used to configure which encryption algorithms are supported by the DC’s, when it is applied to DC’s:

Computer Configuration\Windows Settings\Security Settings\Local Policies\Security Options\Network security: Configure encryption types allowed for Kerberos

Figure 1: Kerberos Encryption Types GPO Setting

Ultimately this sets the following registry key:

HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\parameters\SupportedEncryptionTypes

Figure 2: Kerberos Encryption Types Registry Setting

This is a bitfield where 2 represents DES-CBC-MD5. This means that the GPO setting does not need to be set for DES to be supported, this registry key only requires the bit that represent ‘2’ to be set on 1 domain controller on the domain.

The best way I’ve found to test this without the ability to query the registry for each DC is to request a TGT with a DES session key. I have modified Rubeus, which now includes the /suppenctype argument to asktgt, to make it easy to request TGT’s this way:

Figure 3: Requesting a TGT with a DES session key

Even though the TGT is encrypted with the krbtgt AES256 key, the session key is DES-CBC-MD5. It is important to note that this is possible to perform with any account without requiring any changes to the account. As shown below, there is no indication that the account supports DES in any way:

Figure 4: Account Supported Encryption Types and UAC

If the DC does not support DES-CBC-MD5, a KDC_ERR_ETYPE_NOTSUPP error is returned:

Figure 5: DES Unsupported

Cracking DES Tickets (Kerberoasting)

After understanding how DES could be enabled and how to tell if it was enabled, the next step was to understand exactly how to exploit this configuration.

The simplest example is when a service account is already configured for DES, this means has an SPN (service account) and has the useraccountcontrol (UAC) setting USE_DES_KEY_ONLY set:

Figure 6: Service account configured for DES

This makes it possible to request a service ticket (ST) for the service user des that is encrypted using DES:

Figure 7: Request DES encrypted service ticket

While the “KeyType” shown here is the session key type, which can be different to the encryption type of the ticket, the following wireshark output shows that the ticket is in fact encrypted using DES:

Figure 8: DES encrypted service ticket

Now the question was how can this be cracked? And more importantly, can the key be brute forced rather than cracking back to the plain text password? The reason this second question is more important is because if this is possible it would mean that any key can be recovered, even that of machine accounts.

To help figure out how to do this, I asked my friend Spiros Fraganastasis for help and he asked Sam Croley. Sam realised that if we knew the first block (8 bytes) of plain text, we could form a hash that would be crackable using hashcat’s mode 14000.

To form the hash, we need the initialization vector (IV), first block of cypher and first block of plain text. The IV is stored as the first 8 bytes of the cypher (after the checksum), the first block of cypher is the following 8 bytes after the IV. So, the only thing left to determine was the first block of plain text.

Dumping the decrypted enc-part for several tickets at first showed the same first block, but after more testing it was clear that this was changing a little:

Figure 9: Decrypted enc-part

Looking at this in an Asn.1 viewer it’s clear that the first 4 bytes are the header for the outer most tag, which contains the length of the data inside it (essentially the full length of the decrypted enc-part minus the length of the header (4 bytes)):

Figure 10: Application Asn.1 Tag

Similarly, the next 4 bytes is the header for a Sequence tag which contains the rest of the actual data and contains a length which is 4 bytes less than the previous length (length of the header less):

Figure 11: Sequence Asn.1 Tag

At first, I thought I could predict these values from having only the cypher, this is because the length of the plain text is always 24 bytes less than the full length of the cypher. The problem with this is after some testing, the full length of the plain text often differed from the full length of the actual data. This is because with DES the data is padded to ensure 8 byte blocks, so the plain text often had some junk padding at the end, which couldn’t be determined just from the cypher.

What I needed was a way to know with as close to 100% certainty what this first block is. After looking at the plain text again, I realised there are 3 sections which can vary in length:

Figure 12: Full enc-part Asn.1

These sections, from top to bottom, are the session key, client username (cname) and the PAC.

The session key type is determined, in large part, by the supported etypes supplied within the req-body of the request (controlled by the user requesting the ticket), the client username is determined by the TGT used to request the ticket. All that is left is the PAC, most of which is determined by the client user attributes within the AD database, the only exception is the server checksum, whose length is determined by the encryption type of the ticket (AES results in a 12 byte checksum, DES/RC4 results in a 16 byte checksum).

With all of this in mind, I realised it was possible to request a user-to-user (U2U) ticket which would match all of these requirements and result in a ticket that I could decrypt to get the first block of plain text. The session key as previously mentioned is determined by the supported etypes within the req-body of the request. The client user will be the user within the ticket granting ticket (TGT) which will be the same for the U2U request as for the service ticket request, meaning the cname along with most of the PAC will be the same (providing nothing changes for that user within AD between when the U2U ticket is requested and the DES encrypted ST is requested). And the U2U ticket encryption type (and ultimately the server checksum length within the PAC) is determined by the TGT session key, controllable when requesting the TGT.

After modifying Rubeus to return the first block of plain text when a ticket is decrypted within the DisplayTicket function, which is run when requesting a U2U ticket by default with Rubeus, we can see the plain text within the output:

Figure 13: Request U2U ticket to get plain text of first block

With knowledge of the plain text, it was now possible to form a crackable hash to brute force the DES key. This is done as follows:

CYPHER:(PLAINTEXT XOR IV)

So, imagine we have the following:

  • IV: D98C3338A9CB3626
  • CYPHER: 6C25DDEE294588FD
  • PLAINTEXT: 638203F8308203F4

We XOR the PLAINTEXT and IV, resulting in: BA0E30C0994935D2, and we end up with the following hash:

6C25DDEE294588FD:BA0E30C0994935D2

I’ve automated this into Rubeus’ describe command with the newly implemented /desplaintext argument, supplying the plain text we received from the U2U request along with the ST we requested earlier, we get back a crackable hash:

Figure 14: Rubeus describe returns crackable DES hash

Using this hash, the DES key used for encryption can be brute forced using the following hashcat command:

hashcat -a 3 -m 14000 A2CCBE7DA8A76607:94D29CF55C02153B -1 charsets/DES_full.charset --hex-charset ?1?1?1?1?1?1?1?1

Figure 15: Crack DES key

It’s worth noting 2 things here:

  1. For the DES key cracking in this post, I’ve put the first byte in manually to reduce the cracking time length, this is because I do not have access to a powerful cracking rig to demonstrate in the post.
  2. Due to DES having a parity bit at the end of each byte, there are 16 possible usable keys, the cracking will stop when it finds the first usable key, which may not be exactly the same as the key within the AD database but will work none-the-less.

This key can then be used to request a TGT as the service user:

Figure 16: Request TGT using DES

For the sake of completion, it should be clear that with write access to an account it would be possible to perform targeted DES Kerberoasting of the account (user or computer, due to brute forcing of the key) similar to Will Schroeder’s targeted Kerberoasting with the addition of modifying the accounts UAC bit USE_DES_KEY_ONLY to allow for retrieving the DES encrypted ST as well as authenticating using the DES key, of course this bit can be removed in between these actions to cause as little disruption as possible.

It is also worth noting that once the TGT has been retrieved, the USE_DES_KEY_ONLY UAC bit can be switched off:

Figure 17: USE_DES_KEY_ONLY disabled

Even with the USE_DES_KEY_ONLY bit off, the TGT retrieved using the DES key is still usable for its lifetime:

Figure 18: Use TGT retrieved using DES key

This means the USE_DES_KEY_ONLY bit can be on for a minimum amount of time for during a targeted account hijack to make risk of detection as low as possible.

Destroying DCs

As this method of Kerberoasting allows for the recovery of any account key due to a full address space brute forcing being possible, there are 3 types of accounts for which DES encrypted ST’s cannot be retrieved, even with the USE_DES_KEY_ONLY UAC bit set. These are the krbtgt account, trust accounts (INTERDOMAIN_TRUST_ACCOUNT) and DC accounts (SERVER_TRUST_ACCOUNT).

For science, I decided to see what could be done when write access to a DC is possible, partly because this is a semi-common situation where unprivileged users are joining the servers to the domain before a domain administrator (DA) promotes it to a DC, leaving the unprivileged user as the CREATOR OWNER.

I do not advise performing this attack on a live environment as the potential to cause disruption to the infrastructure is real, but it is, however, an interesting attack path.

So, in this example, the user one has GenericWrite over the DC DC2:

Figure 19: GenericWrite permission

While GenericWrite was chosen for ease of demonstration, this will also all work with CREATOR OWNER.

For this to be a useful attack path, it is assumed all the admin accounts are protected from delegation, as shown below:

Figure 20: Administrator marked as “Sensitive and cannot be delegated”

If this was not the case, it would be possible to use Resource-Based Constrained Delegation (RBCD) to escalate. It is also assumed that PKINIT is not configured, otherwise shadow credentials could be used to escalate.

Initially the DC2 accounts UAC value is usual for a DC:

Figure 21: DC2 UAC value

Enabling the USE_DES_KEY_ONLY UAC bit:

Figure 22: Enable DES for DC2

It can be demonstrated that a DES encrypted ST cannot be requested:

Figure 23: ETYPE_NOTSUPP error

So, to exploit this SERVER_TRUST_ACCOUNT (8192) needs to be changed to WORKSTATION_TRUST_ACCOUNT (4096), to do this the UAC field can be XOR’d with 12288:

Figure 24: Downgrade DC from SERVER_WORKSTATION_ACCOUNT to WORKSTATION_TRUST_ACCOUNT

Now that the DC2 account has been downgraded to a normal workstation account, it is possible to request a DES encrypted ST:

Figure 25: Request DES encrypted ST for DC account

Request a U2U ticket to get the valid plain text of the first block (as described previously):

Figure 26: Request U2U ticket to get plain text of first block

Use Rubeus’ describe, with the /desplaintext to get the crackable DES hash:

Figure 27: Rubeus describe returns crackable DES hash

As with the user hash in the previous example, this hash can be used to brute force the DES key for the DC2 account using the following hashcat command:

hashcat -a 3 -m 14000 31812F50B7AC7122:41299994A5D5ADB3 -1 charsets/DES_full.charset --hex-charset ?1?1?1?1?1?1?1?1

Figure 28: Brute forcing DES key

Using this DES key, a TGT can be requested for the DC2 account:

Figure 29: Request TGT for DC2 account

Now a TGT has been retrieved, DES needs to be disabled again:

Figure 30: Disable DES on the DC2 account

The DC2 account also needs to be escalated back to a SERVER_TRUST_ACCOUNT, unfortunately while this level of access grants the ability to downgrade from SERVER_TRUST_ACCOUNT to WORKSTATION_TRUST_ACCOUNT, it does not allow for the reverse:

Figure 31: Fail to escalate DC2 account

Escalation within the domain is required to escalate the DC2 account back to a SERVER_TRUST_ACCOUNT. This can be done by requesting a S4U2self ticket for DC2 as an administrative user (Administrator in this case):

Figure 32: S4U2self ticket for Administrator to DC2

This ticket can be used with impacket to DCsync the AES key for the Administrator from DC2:

Figure 33: DCsync Administrator AES key

This key can be used to request a TGT as the Administrator user:

Figure 34: Request Administrator TGT

Finally, the DC2 account can be escalated back to a SERVER_TRUST_ACCOUNT:

Figure 35: Escalate DC2 account

Again, I’d like to reiterate, I do not advise performing this attack path on a live environment. There is a LOT of room for real disruption.

DES In The Middle (TGT Session Roasting)

Anyone that was paying attention earlier and understood my previous research on AS-REQ ST’s may realise that there is another potential abuse here. In my previous post, in the section called “Proof of concept: RoastInTheMiddle” I demonstrate that it is possible to capture, modify and replay AS-REQ’s. In the section “Domain Controller Configuration” above, I showed that it is possible to request a TGT with a DES session key providing the DC supports DES and without changing the account in any way to support DES. This is done by only including the DES-CBC-MD5 etype within the req-body inside the AS-REQ.

This means if a valid AS-REQ with pre authentication can be captured, it could be modified to only include the DES-CBC-MD5 etype, replayed to a DC supporting DES which will result in a TGT with a DES session key being returned. All that is required to use this TGT is the session key. As the key used to encrypt the pre authentication data is unknown, it is not possible to decrypt the enc-part of the AS-REP to retrieve the session key and use the TGT immediately, it is possible however to request an ST that is encrypted with the TGT session key, using U2U. This is done by providing a TGT for an account for which credentials have been obtained and providing the TGT with a DES session key as the additional ticket. This will result in a ST encrypted with the DES session key but for the user of the account for which credentials have been obtained.

For example, User1’s credentials are known.

  1. Request a valid TGT for User1.
  2. Send U2U with User1’s TGT as both authentication and additional tickets to extract known plain text of first block.
  3. Man-in-the-Middle (MitM) is performed.
  4. AS-REQ for Computer1 is captured.
  5. AS-REQ modified to only include the DES-CBC-MD5 etype.
  6. Forward AS-REQ to a DC that supports DES.
  7. Extract TGT for Computer1 from AS-REP.
  8. Send U2U with User1’s TGT as the authentication ticket and Computer1’s TGT as the additional ticket to get an ST encrypted with Computer1’s TGT’s session key.
  9. Create a DES hash from U2U ST encrypted with Computer1’s TGT’s session key.
  10. Create KERB_CRED from Computer1’s TGT and known information, missing the session key.
  11. Crack the DES hash back to the TGT session key.
  12. Insert the TGT session key into the KERB_CRED.
  13. Use the TGT to authenticate as Computer1.

I modified my RoastInTheMiddle tool to perform steps 2-10 automatically:

Figure 36: RoastInTheMiddle session roasting

RoastInTheMiddle performs an ARP poison MitM of 192.168.74.13 and takes a TGT using the /tgt argument, this can be a valid TGT for any account. What it does next is shown in the following wireshark output:

Figure 37: RoastInTheMiddle wireshark output

So here, the first 2 (TGS-REQ/TGS-REP) is the initial U2U using the TGT passed in as the /tgt argument to retrieve the plaintext (displayed in the output with the line “Got usable known plain text:”).

The next 4 is a genuine authentication of the DSP computer account, normally Windows sends an AS-REQ without pre authentication first, this is to retrieve the correct salt for the key. These are passed along to the DC (192.168.74.11) as usual to avoid interrupting the authentication, but the AS-REQ that contains pre authentication is stored (this is shown in the output on the line that says “Got AS-REQ for user [email protected] to service krbtgt/DES.LAB”).

The last AS-REQ/AS-REP is the modified AS-REQ, containing only the DES-CBC-MD5 etype, being sent to a DC (192.168.74.12) and the TGT being sent back.

Lastly the U2U request is sent using the TGT passed in as the /tgt argument as the authentication ticket and the TGT retrieved in the previous step as the additional ticket is sent which results in the DC (192.168.74.12) sending back an ST encrypted with the session key of the DSP computer accounts TGT.

In the RoastInTheMiddle output above, the DES hash for the TGT session key is printed along with the KERB_CRED containing the DSP computer accounts TGT without the session key (making it currently unusable).

The next step is cracking the DES hash of the TGT session key, it is important however that this key is cracked within the lifetime of the TGT (by default 10 hours), otherwise the TGT will be unusable anyway, the following hashcat command was used for this:

hashcat -a 3 -m 14000 EF746FE49C358B28:DF4C43FC01762CA3 -1 charsets/DES_full.charset --hex-charset ?1?1?1?1?1?1?1?1

Figure 38: TGT session key recovered

Now the session key needs to be inserted into the KERB_CRED output by RoastInTheMiddle earlier, for this I added a kirbi command to Rubeus, this command can eventually be used to manipulate KERB_CRED’s further but for now it just supports modifying/inserting a session key with the /sessionkey and /sessionetype arguments:

Figure 39: Insert session key into KERB_CRED

Lastly, this TGT can now be used to authenticate as the DSP machine account and retrieve ST’s:

Figure 40: Use TGT to request ST

DES AS-REP Roasting

Some of you may be asking “what about AS-REP Roasting?” and after essentially writing this whole post, that was the same question that I asked myself. If “AS-REP Roasting” is new to you, check out Will’s post on the topic, as I won’t be going over all of the details here.

The first step was to see if a TGT could be requested without pre authentication using the DES key:

Figure 41: Request TGT without preauth using DES key

The last thing needed is to know the first block of plain text of the enc-part within the AS-REP, the decrypted enc-part is shown below, with the first block highlighted:

Figure 42: Decrypted AS-REP enc-part

The first block this time consists of the first 3 tag headers:

Figure 43: First 3 Asn.1 Tags

Let’s look first at the third tag here (CONTEXT SPECIFIC) which is the last 2 bytes of the plain text (A0 13):

Figure 44: Context Specific Tag

Here it’s clear that A0 is the tag number for CONTEXT SPECIFIC and 13 (or 19 in decimal) is the length. This tag contains only the session key, which will always be DES-CBC-MD5, so the length will always be the same. This means the last 2 bytes of plain text will always be A0 13.

The first 3 bytes of plain text (79 81 CF) is the APPLICATION tag:

Figure 45: Application Tag

Here CF is the length of 207 which is the whole enc-part of the AS-REQ. Lastly, there is the next 3 bytes (30 81 CC), which is the SEQUENCE tag:

Figure 46: Sequence Tag

Here the length (CC or 204) is the length in the APPLICATION tag (207) minus 3 (the length of the tag header). So, the only problem with being able to predict the plain text for the first block of the enc-part of an AS-REP is the ability to predict the length of the APPLICATION tag. To see how this may be possible, let’s look at the whole enc-part:

Figure 47: AS-REP enc-part

Looking at the Asn.1 above, it’s clear that the only elements that will vary in length is the srealm and realm within the sname, everything else should remain a fixed length. With this in mind, it should be relatively easy to calculate the correct length.

The domain name used in this example is “des.lab” which is 7 characters, this means the domain name takes up 14 bytes of the enc-part. The APPLICATION tag is 207 bytes, so:

207 – 14 = 193 bytes

To request a TGT without pre authentication, the domain name has to be known, so by doing the following, the proper length of the APPLICATION tag can be calculated:

193 + (length of domain name * 2)

Then to get the length of the SEQUENCE tag, just minus 3.

Using this method, I modified Rubeus’ asreproast command to support roasting DES enabled accounts when /aes isn’t passed:

Figure 48: AS-REP Roasting using LDAP

It is also possible to manually specify the account, rather than searching LDAP and requires including the /des switch:

Figure 49: AS-REP Roast without LDAP

It’s worth noting here that /format:hashcat is required because john the ripper doesn’t support cracking this type of hash. Using the following hashcat command, it is possible to crack this hash back to the account long term DES key:

hashcat -a 3 -m 14000 3DE9FBEA34F9851B:5B1E49C5BD8A76DF -1 charsets/DES_full.charset --hex-charset ?1?1?1?1?1?1?1?1

Figure 50: DES key cracked

This key can then be used to request a usable TGT:

Figure 51: Request TGT

Remediation/Detection

The best option to avoid these issues is to ensure that all DC’s have DES disabled in the relevant registry key:

HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\parameters\SupportedEncryptionTypes

The bit that represents ‘2’ has to be off. If this is the case, no DES attacks are possible on the domain.

Windows events can also be used to detect attacks that take advantage of DES encryption. The 4769 event can detect Kerberoast attacks that request DES ST’s:

Figure 52: 4769 requesting DES ticket

The 4768 event can be used to detect both the TGT session roasting MitM as well as the AS-REP roasting attacks:

Figure 53: 4768 requesting DES encryption

Conclusion

While DES is disabled by default in AD, it is still very easy to enable and when enabled puts the whole AD infrastructure at much higher risk. It should, therefore, be of utmost importance for sysadmins to ensure that DES is fully disabled on all DCs, as only a single DC supporting DES is all that is required to enable all of these attacks.