Setting Service Principal Names to roast accounts
2nd November 2017, by
As a continuation of our previous post, we wanted to discuss another technique that can help during an red team engagement where the intention is to usually stay under the radar when compromising high value accounts.
Before we begin, it is important to understand the fundamentals of how Kerberos is used in Active Directory when attempting to authenticate against a service.
Introduction to SPN's
When a user authenticates to a service (such as SMB or HTTP) via Kerberos, the user (or more likely, the OS) will begin to make a request to a Key Distribution Centre (KDC). If the user is able to prove their identity to the Authentication Server (AS), a "Ticket Granting Ticket" (TGT) will be returned. Then, when the user wishes to authenticate with a service, the TGT is presented to the Ticket Granting Service (TGS) and a ticket is provided. This ticket can then be issued to the remote service which uses the information contained within the ticket to allow or deny the user's request.
When attempting to identify a service to authenticate to, the Kerberos client looks up a "Service Principal Name" (or SPN). A SPN consists of:
[service class]/[host]:[port]/[service name]
For example, if we wanted to connect to a Microsoft SQL Server instance using Kerberos, the SPN would look like:
In Active Directory, an SPN is bound to an account via an LDAP attribute named (surprisingly) "servicePrincipalName". If we take a look at such an account within Active Directory, we can see that this value is as follows:
When a value is set against this attribute, the account essentially becomes a "service account". What this means is that when a user attempts to request a ticket from the TGS for a service, Active Directory will look up the SPN that is requested, and the TGS will return a ticket encrypted with the credentials of the service account. Then, when the user presents the ticket to the service, only a legitimate service with access to the same credentials stored in Active Directory is able to decrypt the ticket, providing mutual authentication.
This is a very simple overview of the components of Kerberos needed to understand the below attack. If you are interested in learning more, there are a number of good resources such as Lynn Root's guide here.
So, what does this mean to us as an attacker? Well this is where an attack called "Kerberoasting" comes into play. Kerberoasting is a technique which exploits the fact that the ticket returned from the TGS is encrypted with the service account's credentials. This allows any user or machine permitted to authenticate against Active Directory to request a ticket for a service, and bruteforce the service account credentials offline.
We won't go into the full details of this technique here (I have recently documented this attack on my blog here if you are interested), however this attack opens up some interesting opportunities to us during an engagement.
Over to Bloodhound
One thing that you may see in Bloodhound after a recent release is the start of ACLs being enumerated for objects. For example, below we can see a common link in Bloodhound in which a group has permission to modify an access control list on an Active Directory object:
One option in this scenario is to grant our account permission to update the user's password and take control of the account. If this is within the scope of your assessment, you may consider this, although if the account is in active use, this may set off a red flag when the user comes to authenticate. Alternatively, if the account is used by an application rather than a user, resetting a password may actually cause an issue on the customer's network which could result in downtime for the customer.
A workaround for this is to utilise the "Service Principal Name" field mentioned above to turn the account into a service account. This then allows us to use the same Kerberoasting attack to crack the password offline, and then authenticate as the user... reducing the risk of alerting the user!
To demo this attack, let's set up a quick lab with the following network layout:
Then we want to set up Active Directory as follows:
- A group named “Trusted Servers” containing our IIS.LAB.LOCAL server.
- A user named “DA” which is a member of the Domain Admins group.
- An access control entry on the "DA" user to provide "Modify Permissions" and "Modify Owner" privileges to the “Trusted Servers” group.
- A user named "hacker" with no special privileges.
You may have noticed that this lab setup actually grants the machine account "IIS.LAB.LOCAL" permission to modify the "DA" user ACL... this is purely because I wanted to mix things up a bit and continue to explore the power of machine accounts :)
When this has been set up, we can run Sharphound using the following command:
When we import Sharphound's results, we can chart a path from "IIS.LAB.LOCAL" to "Domain Admins", and we see that we are given a route as follows:
Here we can see that Bloodhound has picked up on our security "misconfiguration" and shows that we have "WriteOwner" and "WriteDacl" permissions to the "DA" user, which will allow us to elevate to a Domain Admin.
To exploit this path, we will want to update the DA user object permissions so we can add new attributes. To do this, we can use Harmj0y's recently introduced PowerView command "Add-DomainObjectACL":
Now, if you execute this as a user logged onto the IIS server, you will quickly see something similar to this:
Note the "Access is denied" error. This is because it is the machine account that has permission to modify the ACL for the "DA" user.
The quickest way I have found to launch commands as the machine account is to simply spawn a SYSTEM process. There are a few ways to do this (create/modify a service, named pipe impersonation etc..), however my new favourite way is to use James Forshaw's "New-Win32Process" commandlet in Powershell, setting the parent process as "lsass.exe":
OK, now if we execute the "Add-DomainObjectACL" command again under the account NT AUTHORITY\SYSTEM, we will grant the "hacker" account user full control over the "DA" user:
Now that we have the ability to modify the DA user account, we want to add an SPN to this user account. The easiest way to do this on a Windows OS is via the built in "setspn.exe" tool, which can be executed as follows:
setspn -s http/iis.lab.local LAB\hacker
Some important caveats about the above command. First, the hostname must actually exist, otherwise when Kerberos attempts to request a ticket, the TGS will return an error. Second, the SPN must be unique. This means that you cannot have 2 accounts registered against "http/iis.lab.local" otherwise the TGS will return an error.
Once your victim account has been updated, we are free to Kerberoast as normal. Simply fire up your favourite Kerberoasting tool (I usually opt for impacket's GetUserSPNs.py) to request the service ticket for the user account:
Once you have recovered the TGS ticket, you can unset the SPN attribute using the following command:
setspn -d http/iis.lab.local LAB\hacker
All that is left is to fire up Hashcat or John the Ripper Jumbo, and crack the password offline:
Protecting your domain
So how do you protect your domain from this kind of attack?
First and foremost, always ensure that your domain does not have any groups capable of setting the SPN fields against arbitraty accounts. Although tools like Bloodhound are paving the way for red teams, there is absolutely no reason that a blue team should not be running the same tools to identify these kinds of holes in Active Directory. It is important to understand that if an account has the WriteACL or WriteOwner permissions on an object, they own that object anyway, as a malicious attacker could just as easily reset the password to take over the account.
Secondly, ensuring that a strong password policy is in place will help to prevent bruteforcing attacks. If your domain admins are forced to set lengthy complex passwords, no amount of offline bruteforcing is going to reveal the account credentials.