The Art of Bypassing Kerberoast Detections with Orpheus
Back in May of 2018, I wrote a blog post detailing the steps I took to detect Kerberoast (T1558.003) attacks. This research allowed us to help organizations build a detection for when a threat actor requests the Kerberos ticket for accounts with a service principal name established. In this blog post, I am going to demonstrate how I was able to bypass my own detection, and the detections of many other systems, with slight modifications of the Kerberos request.
Later in the post is a demo if you don't want all the technical details and just want to see a video of the bypass technique in action.
Detecting Kerberoast (or so we thought)
In my 2018 blog post, I highlighted several factors for identifying a Kerberoast attack. These identifiers were as follows:
- Windows Event Code 4769
- Ticket Encryption Type of RC4 or 0x17
- Ticket Options with a value of 0x40810010
- Accounts that didn't end with a dollar sign ($)
- A count of the number of SPNs requested that goes over a specified threshold
One of the great things about working at TrustedSec on our Tactical Awareness and Countermeasures (TAC) team is that we get to be both offense and defense. Because of this, we are often presented with the opportunity to defeat our own detections.
This is when I began to dive deep into RFCs 4120 and 1510 (The Kerberos Network Authentication Service (V5)) to see if there was anything I could do to avoid my detections. I started with the encryption parts of the requests.
Encryption Types
The encryption types are defined by the MsDS-SupportedEncryptionTypes values in Group Policy Objects (GPO). The default Kerberos encryption type for Windows XP and Server 2003 is RC4, whereas Windows 7 and later and Windows Server 2008 and later are defaulted to AES-256.
In the Kerberos exchange, these show up as eTypes in the message. eType 18 (0x12) is AES-256, and eType 23 (0x17) is RC4. When looking at normal traffic on updated Windows systems, we typically see 0x12 as part of the normal encryption process for Kerberos activity.
RC4 is a weaker cryptographic algorithm than AES-256 and it is therefore easier to recover the NTLM password that was attached to the ticket. This is why tools like GetUserSPNs.py and others will attempt to downgrade the request to the Key Distribution Center (KDC) to use RC4 encryption. Windows Server 2019 still allows for encryption downgrade to RC4!
Several defensive tools will automatically detect when a Kerberos request is downgraded from AES-256 (0x12) to RC4 (0x17). You can see in the following screenshot where I requested a ticket for the trustedsec service account using RC4 encryption.
My first step in bypassing the detections was getting the Kerberoast request to show up as AES-256 (0x12). To use AES-256, the account(s) must be configured to permit authentication using that encryption type. This is in addition to the MsDS-SupportedEncryptionTypes in the GPO.
With a slight modification to Impacket's kerberosv5.py file, I was able to force the encryption to be AES-256. This changed my request and is closer to blending in with normal traffic.
With the encryption handled, it was on to the Ticket Options.
Ticket Options
This was incredibly confusing at first because when I studied binary to hexadecimal conversions, it was always done right to left (aka little endian), meaning that the rightmost bit was bit 0, and the leftmost bit was bit 7 (for 8 bits). When looking at the Ticket Options flag in the 4769 event, it was difficult to translate 0x40810010 to the appropriate flags as outlined by the Kerberos RFC.
That was when I came across this bit of information: the Ticket Options is encoded with big endian encoding. This means that the first bit is the leftmost bit. And with that, I was able to encode and decode the Ticket Options values.
The Ticket Options, or KDCOptions, is a 32-bit representation of flags. As of this writing, these are the flags that are present in the Ticket Options as outlined in RFC 4120:
Bit | Flag |
0 | Reserved |
1 | Forwardable |
2 | Forwarded |
3 | Proxiable |
4 | Proxy |
5 | Allow Postdate |
6 | Postedated |
7 | Unused |
8 | Renewable |
9 | Unused |
10 | Unused |
11 | Optional Hardware Authentication |
12 | Unused |
13 | Unused |
15 | Canonicalize |
16 - 25 | Undefined |
26 | Disable Transited Check |
27 | Renewable Ok |
28 | Encrypt Ticket in Server Key |
29 | Undefined |
30 | Renew |
31 | Validate |
So, if we take the current Ticket Options from the Impacket GetUserSPNs.py, which is 0x40810010, we can translate that to binary, which is 01000000 10000001 00000000 00010000. This means that if we read left to right, with the leftmost bit being bit 0, the following flags are on (or 1):
Bit | Flag |
1 | Forwardable |
8 | Renewable |
15 | Canonicalize |
27 | Renewable Ok |
Knowing this, I wanted to get my Ticket Options to look like the normal event traffic of 0x40810000. This meant I had to turn off the Renewable Ok flag in my request. I modified the Impacket kerberosv5.py even more and was able to send my request with the Ticket Options of 0x40810000. It is important to note that Rubeus uses these flag options by default.
I now have two methods to avoid the detections. In most cases, changing the Ticket Options was enough to avoid detections at most organizations. The encryption downgrade detection was added only at organizations with a high defensive security posture. This left the accounts ending with a dollar sign ($) and requesting SPNs beyond a specific count.
Accounts Ending With a Dollar Sign
There are several ways to get a machine account's NTLM. You can use relay attacks with ntlmrelayx.py, authentication coercion with tools like dementor.py or petitpotam.py, or even use Rubeus. This blog post is not meant to cover these techniques, but ignoring accounts ending with a dollar sign is a potential detection gap.
Single Account Request
Do not assume that a threat actor will request a ticket for every account that has an SPN configured. They may only request a single account. If your detection is relying upon this as a condition, know that it may be able to be defeated if an attacker only requests a single ticket for a single service account.
At this point, my previous detection was only good for catching the stock Impacket attacks. I could get around the encryption, the Ticket Options, the account names, and the count threshold. This technique needed a name and as Kerberos is named after the Greek mythological created Cerberus, I thought it only fitting to name this Orpheus.
Orpheus
Orpheus is a Greek musician who is said to have been one of two beings to get past Cerberus, who was guarding the entrance to Hades. As we are attempting to get past Kerberos detections, I thought Orheus was a fitting name.
Orpheus combines the Ticket Options and Encryption Type methods outlined above in an attempt to avoid detection. It uses a wrapper to call the modified Impacket GetUserSPNs.py and kerberosv5.py files to pass along the specified Ticket Option and encryption type. It will also tell you what flags are enabled based on the hexadecimal value of the Ticket Options.
In the screenshot above, you can see that the Forwardable, Proxiable, Renewable, Canonicalize, and Renewable Ok are all set. If I type the number 27 and press enter, it will turn off that flag and you can see the updated Ticket Options value is now 0x40810000. This eliminates your having to figure out what the hex value is or should be.
Here is a video of Orpheus in action and the resulting events that are generated on the Domain Controller.
Detecting the Undetectable
Great! So, what do we as defenders do now? This is where utilizing Honey SPN accounts is key. You must have Honey accounts, credentials, tickets, systems, etc. deployed in this day and age. You cannot rely on signatures as your primary line of defense. Signatures are easily defeated!
You can create a "fake" or Honey SPN account by using one of your old accounts and set it up like one of your current SPN accounts. In this example, I am going to use Dave Kennedy (rel1k)'s account to be a Honey SPN account.
I use setspn to add the SPN to Dave's rel1k account. It is important to note that Dave's account is also being set with a 50-character random password. Once this is done, it doesn't matter what encryption type, Ticket Options, account name, or number of SPNs is used. If someone requests a ticket for the rel1k account, we will have a detection.
As you can see in the screenshot above, I requested the SPN with the Ticket Options of 0x40AC0010 and I used AES-256 bit encryption (0x12). We encourage you to have as many Honey SPNs as you can to increase the chances that an attacker would request one of them to give you an early indicator of compromise.
It is important to note that if someone just modifies the Ticket Options and encryption type and requests all of the SPNs, your detection of counting the number of tickets requested by a single IP is still going to work. I can tell you that our Targeted Ops team will never request all of your SPNs—unless they have been asked to do so. 😊
Orpheus on GitHub
Orpheus is available on the TrustedSec GitHub page. Full details on how to get it up and running are in the README.md.