Roasting AS-REPs
Last November, I published a post titled “Kerberoasting Without Mimikatz” that detailed new developments with PowerView and Tim Medin’s Kerberoasting attack. This started me down the path of looking at Kerberos just a bit more closely. Then a few weeks ago, my coworker Lee Christensen found an interesting presentation from Geoff Janjua of Exumbra Operations titled “Kerberos Party Tricks: Weaponizing Kerberos Protocol Flaws”, slides and toolkit located here. One of the interesting points that Geoff mentioned, and that his Python-based “Party Trick” toolkit executes, was abusing user accounts that don’t require Kerberos preauthentication.
I recently dove much deeper into this topic and wanted to share what I was able to learn and develop. This post will give some detailed background on the aspect of Kerberos we’re abusing, what the precise issue is, how to easily enumerate accounts that don’t need preauth, how to extract crackable hashes in these situations, and finally how to crack these retrieved hashes efficiently. There is also an associated PowerShell toolkit, ASREPRoast, that is now live on GitHub.
tl;dr — if you can enumerate any accounts in a Windows domain that don’t require Kerberos preauthentication, you can now easily request a piece of encrypted information for said accounts and efficiently crack the material offline, revealing the user’s password.
Note: this isn’t anything revolutionary, and obviously isn’t as useful as Kerberoasting, as accounts have to have DONT_REQ_PREAUTH explicitly set for them to be vulnerable — you’re still reliant upon weak password complexity for the attack to work. However, this setting still exists on some accounts in some environments, we’re just not sure as to the frequency as it’s not something we normally looked for before. Our guess is that it’s likely enabled for older accounts, specifically Unix-related ones. If you happen to find it “in the wild”, we’d love to hear from you ;) (@harmj0y or will [at] harmj0y.net).
[Edit] if you have GenericWrite/GenericAll rights over a target user, you can maliciously modify their userAccountControl to not require preauth, use ASREPRoast, and then reset the value ;)
Background
I’m not going to go through all aspects of Kerberos, as people like Sean Metcalf have already done a great job of this. If terms like AS-REQ and AS-REP are completely foreign to you, I would recommend reading Sean’s post for some basic background first. The aspect we care for the purposes of this post is something called Kerberos preauthentication.
Under normal operations in a Windows Kerberos environment, when you initiate a TGT request for a given user (Kerberos AS-REQ, message type 10) you have to supply a timestamp encrypted with that user’s key/password. This structure is PA-ENC-TIMESTAMP and is embedded in PA-DATA (preauthorization data) of the AS-REQ — both of these structure are described in detail on page 60 of RFC4120 and were introduced in Kerberos Version 5. The KDC then decrypts the timestamp to verify if the subject making the AS-REQ really is that user, and then returns the AS-REP and continues with normal authentication procedures.
Note: the KDC does increase the badpwdcount attribute for any incorrect PA-ENC-TIMESTAMP attempts, so we can’t use this as a method to online brute-force account passwords :(
The reason for Kerberos preauthentication is to prevent offline password guessing. While the AS-REP ticket itself is encrypted with the service key (in this case the krbtgt hash) the AS-REP “encrypted part” is signed with the client key, i.e. the key of the user we send an AS-REQ for. If preauthentication isn’t enabled, an attacker can send an AS-REQ for any user that doesn’t have preauth required and receive a bit of encrypted material back that can be cracked offline to reveal the target user’s password.
This is something that has been known for a long time, after all, it’s the reason preauth was implemented in Kerberos! In modern Windows environments, all user accounts require Kerberos preauthentication, but interestingly enough, by default Windows attempts the AS-REQ/AS-REP exchange without preauthentication first, falling back to supplying the encrypted timestamp on the second submission:
I have no idea why this behavior happens ¯\_(ツ)_/¯
[Edit] @munmap pointed out on Twitter that this behavior is due to the client not knowing the supported ETYPES ahead of time, something explicitly detailed in section 2.2 of RFC6113.
However, Windows offers a way to manually disable this protection for specific accounts through a useraccountcontrol modification:
If you’re already an authenticated (but otherwise unprivileged) user, you can easily enumerate what users in the domain have this setting with the LDAP filter (userAccountControl:1.2.840.113556.1.4.803:=4194304). PowerView’s Get-DomainUser already has this implemented with the -PreauthNotRequired parameter:
So now we know what the issue is and how to identify vulnerable users. While people have executed brute-forcing of the AS-REQ’s PA-ENC-TIMESTAMP component of Kerberos exchanges for well over a decade (the hash format is even in Hashcat, -m 7500/ ‘Kerberos 5 AS-REQ Pre-Auth’) the only toolset I’ve seen that attacks RC4 AS-REPs is Geoff’s Python toolkit. We wanted something that was Windows based that also didn’t need administrative privileges on a machine to allow us flexibility in our attack workflow. We also wanted a faster way to crack these hashes.
ASREPRoast
My first hope was to find something in .NET that exposed the raw bytes of the AS-REP similar to the Kerberoasting approach. I spent a while searching for any .NET method that would allow access to the raw byte response of the AS-REP and unfortunately came up short. Though I can’t say definitively if this is impossible, my gut feeling is that it’s likely an abstraction level too deep for us to access easily through .NET. Even if there was, we would still have one complication, as modern Windows Kerberos environments default to the the AES256-CTS-HMAC-SHA1–96 encryption in the AS-REP instead of the much quicker ARCFOUR-HMAC-MD5/RC4 approach. RC4-HMAC is significantly quicker to crack, so we prefer it if possible.
The approach I ended up taking was to construct the AS-REQ by hand in order to control the necessary parameters, and parsing the KDC’s AS-REP response in order to determine success/failure and extract the encrypted material. Here was another roadblock- Kerberos uses ASN.1 encoding for its structures, something that .NET does not have built in encoders or decoders for. Luckily, there is an open source C# version of the Bouncy Castle crypto library that features, among many, many other things, robust capability for ASN.1 encoding and decoding.
Unfortunately, I don’t have time to give a full ASN.1 tutorial, but I will share a few pointers that helped me while developing this tool. The specifications we care about for the AS-REQ are laid out on page 55 of RFC1510 and page 74 of RFC4120. Benjamin Delpy also documents all these ASN.1 structures amazingly in his Kekeo project. Here’s the structure description:
AS-REQ ::= [APPLICATION 10] KDC-REQ
KDC-REQ ::= SEQUENCE {
pvno[1] INTEGER,
msg-type[2] INTEGER,
padata[3] SEQUENCE OF PA-DATA OPTIONAL,
req-body[4] KDC-REQ-BODY
}
PA-DATA ::= SEQUENCE {
padata-type[1] INTEGER,
padata-value[2] OCTET STRING,
-- might be encoded AP-REQ
}
KDC-REQ-BODY ::= SEQUENCE {
kdc-options[0] KDCOptions,
cname[1] PrincipalName OPTIONAL,
-- Used only in AS-REQ
realm[2] Realm, -- Server's realm
-- Also client's in AS-REQ
sname[3] PrincipalName OPTIONAL,
from[4] KerberosTime OPTIONAL,
till[5] KerberosTime,
rtime[6] KerberosTime OPTIONAL,
nonce[7] INTEGER,
etype[8] SEQUENCE OF INTEGER, -- EncryptionType,
-- in preference order
addresses[9] HostAddresses OPTIONAL,
enc-authorization-data[10] EncryptedData OPTIONAL,
-- Encrypted AuthorizationData encoding
additional-tickets[11] SEQUENCE OF Ticket OPTIONAL
}
Another thing that helped me a lot was to Wireshark legitimate Kerberos exchanges, export the Kerberos packet bytes, and visualize the data using this JavaScript ASN.1 decoder:
This particularly helped during the next part, which was learning how to use Bouncy Castle through PowerShell to construct a proper ASN.1 encoded AS-REQ. But after a few struggles with tagging and finding the correct data structures, I came up with New-ASReq, which takes a user/domain name, builds the properly nested components, and returns the raw bytes for the request.
And because we’re building this by hand, we can include or omit anything we want. So we can include just the ARCFOUR-HMAC-MD5 etype instead of all supported encryption etypes. This type and its use in Windows Kerberos auth is explained in detail in RFC4757. What’s especially nice is that section 3 includes the message types for different uses of the algorithm. While the AS-REP ticket uses type 2 like a TGS-REP ticket (i.e. kerberoasting) this component of the response is encrypted with the service key, which in this case is the krbtgt hash and therefore not crackable. However, the AS-REP encrypted part, which is the section we can essentially ‘downgrade’ to RC4-HMAC, is the same algorithm but of message type 8. This will come into play later during the cracking section.
A second function in ASREPRoast, Get-ASREPHash, wraps New-ASReq to generate the appropriate AS-REQ for a specific user/domain, enumerates a domain controller for the passed domain, sends the crafted AS-REQ, and receives the response bytes. Bouncy Castle is used to decode the response, checking whether it is a KRB-ERROR response or a proper AS-REP. If the request succeeded, we can extract out the enc-part section that’s RC4-HMAC encrypted using the specified user’s hash and return it in a nice format:
The final useful function in ASREPRoast is Invoke-ASREPRoast. If run from a domain authenticated, but otherwise unprivileged, user context in a Windows Kerberos environment, this function will first enumerate all users who have “Do not require Kerberos preauthentication” set in their user account control settings by using the LDAP filter (userAccountControl:1.2.840.113556.1.4.803:=4194304). For each user returned Get-ASREPHash is used to return a crackable hash:
Cracking The Hashes
We now have a nice set hash representations of RC4-HMAC AS-REPs, each of which are encrypted with a user’s password. We should now be able to crack these offline à la Kerberosting (krb5tgs format in John the Ripper), but remember that despite using the same algorithm and approach as the existing TGS-REP format, the message type here is 8 instead of 2.
This unfortunately means that existing plugins won’t work, but luckily for us, all we have to do is change this line to an 8 instead of a 2, remove some of the specific TGS ASN.1 speedups, and change the format naming. I have a included a tweaked version of this krb5_asrep_fmt_plug.c plugin with the ASREPRoast project. Simply drop it into the source folder for Magnumripper, run the normal build instructions, and you’d good to go for cracking the output of ASREPRoast.ps1:
I believe that it should be simple to modify Hashcat’s existing TGS-REP format as well in a similar way, but I haven’t attempted it yet. Also, because this is the same algorithm as the krb5tgs/Kerberoasting format, just with a tweak in key material, performance should be similar to the existing modules.
Closing Thoughts
As I mentioned at the beginning, this obviously isn’t as useful as the Kerberoasting attack, as accounts have to have DONT_REQ_PREAUTH explicitly set for them to be vulnerable, and you’re still reliant upon weak password complexity for the attack to work. However, this setting is sometimes present in some environments, often on aging accounts for backwards compatibility reasons, and we feel that the toolset will be operationally useful in some situations at least.
Defensively, the same protections outlined for Kerberoasting apply here, specifically have really long passwords for these types of accounts and alert when abnormal hosts are sent an AS-REP for the account. Also, audit what accounts have this setting, which is easy with PowerView (Get-DomainUser -PreauthNotRequired) or other LDAP toolsets with the (userAccountControl:1.2.840.113556.1.4.803:=4194304) filter. Carefully consider whether accounts with this setting truly are needed.
[Edit] also for the defensive side, @munmap suggested investigating Kerberos FAST pre-authentication and/or Public Key Cryptography for Initial Authentication in Kerberos (PKINIT).
Originally published at harmj0y.