Active Directory Penetration Testing
It’s nothing new, just my partial summary on active directory pentesting, which was presented during my internship back in 2020, and I just port it over here.
First of all, what is active directory? Active directory usually refers to Microsoft Active Directory Domain Services. Described by Microsoft:
it enables centralized, secure management of an entire network, which might span a building, a city or multiple locations throughout the world.
Stores information about objects on the network and makes it easily available to users and admins.
And here is a general structure of the active directory:
As you can see that it follows a hierarchy structure. The minimum element is an object, be it a machine, user etc, and Organizational Unit is a subdivision which you can place objects in it. Domain is the logical partitions within active directory. There can be a root domain as shown here test.com, and child domain like manager.test.com, sales.test.com. and Trees are collection of one of more domains. Lastly, forest is a collection of trees, which is joined by trust relationships. A trust in active directory is the relationship between two domains or forests which allows users of one domain or forest to access resources in the other domain or forest. Which is not the focus point in today’s content.
Now I will introduce two authentication protocols in active directory.
One is NTLM, or we say Net NTLM. Before I go to Net NTLM, I will briefly explain what is NTLM.
Usually when we open our laptop and key in our password in the login form, press enter and login to the system. We have gone through several steps. 1. The password was captured by winlogon.exe and passed to lsass.exe, then lsass.exe will convert our password into NTLM hash, and compare this hash to the hash in the SAM files, if it is the same, we will successfully login to the system. So NTLM is a protocol which is based on the NTLM hash. Let’s go back to Net NTLM. Let’s say we authenticate to the system remotely, via smb, in the early time, the SMB protocol is sending clear text password via network, but not anymore. Now we have NTLM, which is based on challenge and response. So when we are trying to authenticate to the remote system, the remote system will send us, the client back a random string, which is called challenge, and we are going to convert the password into NTLM hash, and use this NTLM hash to encrypt this string and send back to the remote system. On the remote system, it will also retrieve the NTLM hash, either from NTDS.dit from dc if the system is in the domain, or from the local SAM files, and encrypt the challenge using the hash. Then it will compare the encrypted result with the one from the client. If it is the same, we will be authenticated.
Now let’s talk about Kerberos.
Kerberos is named after the three head dog in Greek mythology, where each head represents a party involved in the Kerberos authentication. Client, Server and KDC (key distribution center). Usually KDC will be the domain controller so it has access to all the resources in the domain, which makes it easier for the authentication process. KDC is actually of two services, one is authentication service (AS), and another one is ticket granting service(TGS), they serve difference purposes. Now let’s say the client wants to access the service on the server.
First step, it will encrypt the timestamp using the client’s hash, which we called that an authenticator, and then send it to AS, this is the AS_REQ, and AS will check if the authenticator is valid by checking if it can be decrypted by client’s hash, and timestamp is in an acceptable range.
If So, it will send back the sessionkey for communication between client an KDC, this sessionkey is encrypted using client’s hash, and another session key together with a signed PAC, privilege attribute certificate, the whole thing is encrypted by the KDC’s password hash. The privilege attribute certificate contains the sid of the client and the group sid of the client. It indicates the permission of client A on other services in the domain. This is TGT, ticket granting ticket. And these two is called AS_REP.
the third step, client will send back the TGT to the TGS, together with the timestamp encrypted by the sessionkey between client and KDC as authenticator. This is TGS_REQ. KDC will decrypt the TGT first, using KDC’s hash, and then get the session key, then use this session key to decrypt the authenticator, and validate the timestamp.
If the timestamp is within an acceptable range, TGS will send back a sessionkey between client and the server, which is encrypted by the session key between client and KDC, and also a session key between client and the server together with a signed PAC, they are encrypted using the server’s hash. This is the service ticket which client will use to access the server. This process is called TGS_REP.
So after the client has get the service ticket from TGS, it will send it to the server, and together with a timestamp encrypted by the session key between client and server, as usual, this is the authenticator. This is AP_REQ.
After server receives the two information, it will first decrypt the service ticket using its own hash, and extract the session key and the PAC. Then using the session key to decrypt the Authenticator and get the timestamp to validate. Also it will check the PAC and decide whether client has enough permission to access the service on server B.
There is an optional step, where server will talk to the KDC and check if the PAC is valid and indeed signed by KDC.
This Kerberos authentication process is quite important and in the later part of the article, I will refer back to this many times. So, if you are not very clear, it does not matter.
Now let’s talk about a generic pentesting life cycle. We always start from the outside and do the recon, and hopefully we can get one low privilege shell in the intra network. From there, we are doing enumeration on the network and hope to gather as much information as possible. Meanwhile, we usually would like to escalate our privilege on the initial foothold machine so it would be easier for us to conduct the later operation. After we have escalated privileges, we might want to do admin recon and lateral movement and expand our shell to the other machines. The best thing is we could compromise a machine where the domain admin or any other high privilege account is on, so we can dump the credential and compromise the high privilege account. But sometimes, we might not even need domain admin privilege, it depends on our pentesting goal. If it is get the sensitive information, we might not need to get domain admin privilege, sometimes gaining admin privilege might even have the adverse effect. But anyway, we should try to achieve our goal as silent as possible.
Now let’s assume that we have compromised a machine in the domain and gain a low privilege shell. We will start to enumerate domain information.
For domain enumeration, we can use PowerShell. The reason for PowerShell is because it is built-in since server 2008. And to speed up the process, we can use PowerView. Here are some example commands:
But there are much more than this. You can refer to the PowerView github page
Here are some more special and useful commands:
Search for a particular string in a user’s attribute. sometimes user might put sensitive information there
Find computer where a domain admin has session on, this can guide our direction, if we want to gain domain admin priv, we might spend more time poking these machines.
To Find all machines in the current domain where the current user has local admin access. This would help us to do lateral movement and expand our privileges, but this is quite noisy as we are trying to access all the machines in the domain.
This has the same function as the last one but using WMI.
Bloodhound is a very famous tool for domain enumeration. It is using graph theory to map the relation between objects in the domain, and it can analyze the relationships, help us to find complex attack path which are hard to find ourselves. However, since it is enumerating all the objects in the domain, it is really noisy, and sometimes we might need to tune the parameters in order to avoid being spotted.
Here is a function which helps us find the shortest path to domain admins. And we can clearly see what potential attacking technique can be used.
For more usage on bloodhound, please check the GitHub page.
Now let’s move forward to local privilege escalation part:
Here are some methods to escalate our low priv shell to system privilege:
First one is missing patches. It usually occurs when administrators are too lazy to install the updates from Microsoft and thus the system is vulnerable to certain vulnerability which allows us to escalate privileges. We can use windows exploit suggestor, which will enumerate the patches installed on the machine, and compare with its database, and list out if any missing patches and associated exploits.
Second one is the token manipulation. When our low privilege shell have these privileges, we can abuse some interesting feature and escalate privilege to system. For example, the famous rotten potato, juicy potato and the recent one pipe potato are the exploits which abuse these privileges.
Misconfigured services might also lead to privileges escalation. For example, when we have the permission to modify the binary path of certain service, we can point the path to our own defined scripts, and when the service restarted, our scripts get executed.
Actually there are quite a lot techniques, and we can use some automatic scripts to help us to do the essential checks and see if we got chance to escalate our privileges.
As always, feel free to check the homepages of these tools, they are much more detailed than me.
Now suppose we have escalated our privilege to the local system on our foothold machine, and we would like to expand our shell and do the lateral movement.
Pass the hash is the technique that allows you to gain privilege on the remote machine without knowing the password of the victim.
If we refer back to the NTLM authentication which I explained just now. We would realize that during the authentication process, the cleartext password is not used at all. During challenge and response, it is the password hash being used. So if we managed to get the victim hash, we can use it for NTLM authentication and gain access on other machines.
But due to the KB2871997 patches from Microsoft, this technique can only be used on built-in local administrator account, whose sid is 500 or domain user. One applicable scenario is when I escalate my privilege to the system, we can extract the local administrator’s hash from the local SAM file, and use that hash to try to login to other machines. Here is an example, where I am using pth-toolkit to gain administrator privilege on other machines. This is relied on SMB, which run on port 445.
Another technique is called over pass the hash. Refer back to the Kerberos authentication:
we know that we need to get TGT first, and using that TGT to gain the service ticket to access the service on the server. During the first two steps, the authenticator is using the client hash to encrypt the timestamp, and once it passed validation from the AS, we can get the TGT, which we would use to get service ticket. Therefore, if we know the hash for a client, we can forge the authenticator, and get a TGT on behalf of that user, which would allow us to access other services as the victim. And here is an example:
Now move on to the kerberoasting, but before that, I want to give a brief introduction on SPN, service principle name first. So, what is a service principle name? it is a unique identifier of a service instance, which is used to associate a service instance with a service logon account. So that when client requesting service to TGS, the client doesn’t have to tell the TGS that under which account is this service running, and TGS can look up the SPN and associate with the respective account. And here is the format:
Now let’s refer back to the Kerberos authentication:
during TGS_REQ and TGS_REP, we would notice that the we have two copies for session key between client and server, one is encrypted by session key between client and KDC, which we have, one is encrypted by the server password, to be exact, is the password hash of the account running the service which we are trying to access. Therefore, we can do an offline bruteforce attack.
First, we can extract the session key between client and server, then try to bruteforce the service account password hash to decrypt the service ticket and see if we can get the same session key.
One thing we need to need to be careful is that we usually only attack those services under the user account, not the machine account. The reason is simple, the machine account password is insanely long, and it is changing every 30 days by default:
And here is an example. First we enumerate user’s SPN:
then we request service ticket from TGS:
and export it to the hard drive:
Using a tool to bruteforce it against a dictionary. Hopefully we can find the password:
There are quite a lot of tools for kerberoasting. kerberoast, is the initial implementation. autokerberoast has automate the process of enumerating user SPNs, requesting service ticket, and dump the ticket to the crackable hash. GetUserSPNs.py from impacket is doing the same thing as well. Rubeus is a tool which have almost all the techniques targeting at Kerberos protocol. But the author does not release the compiled version. Here is a demo on Rubeus:
AS-REP Roasting is another technique which seems quite similar to kerberoasting but targeting at difference stages of Kerberos authentication:
During the AS_REQ and AS_REP, for the Kerberos will first try to authenticate without preauth, this is mainly for backward compatibility:
So even AS_REQ is not correct, it will still send the sessionkey encrypted by the client’s hash. This also give us the opportunity to do offline crack.
However, currently, the accounts are set to require preauthentication by default, unless otherwise configured.
Here is a demo on as-rep roasting. First, we enumerate user which do not require preauthentication:
and use tool to send AS_REQ to the AS and get the AS_REP, extract the crackable hash out:
Use the hashcat to crack it and get the cleartext password.
Now I will move on to delegation.
First of all, what is delegation, delegation is a bit like impersonating, which allows a service to requesting resources on behalf of you. Here is an simple scenario:
If you are accessing HTTP service on a machine S1, and the S1 needs to get extra information from the database server to display to you. However, the http service can’t use his own identity to retrieve the information from the database server, because he is not sure if you even have the permission to access S2. In this case, delegation allows HTTP service to use your own identity and try to access the SQL service on S2.
First, we will start on unconstrained delegation.
As the name suggests, the unconstrained delegation means it is not restricting the service on which the middle service can use the client’s identity to access. In the previous example, HTTP service can use your identity to access SQL service, it can also use it to access other service as well.
The process is as follow:
AS_REQ, user request for forwardable TGT(TGT1)
AS_REP, KDC return TGT1
TGS_REQ, user request for forwarded TGT(TGT2) using TGT1
TGS_REP, KDC return TGT2
TGS_REQ, user request for TGS for accessing Service 1 using TGT1
TGS_REP, KDC return service ticket(TGS1)
AP_REQ, user present TGS1, TGT2 and session key signed by TGT2. Service 1 keeps TGT2.
TGS_REQ, service 1 request for TGS for accessing Service 2 using TGT2
TGS_REP, KDC return service ticket(TGS2), identify client as the user
AP_REQ, service 1 present TGS2, acting as the user
Service 2 responds
with response from service 2, service 1 respond to the request (7)
But because there are no constraints on usage of TGT2 for which service
Service 1 can get TGS for service N from KDC as user
Service 1 access service N as user
For unconstrained delegation, it is either machine account or user account can be configured:
And we can use powerview to enumerate those account which enables unconstrained delegation:
As we can see, the unconstrained delegation is dangerous. It can abuse the user’s tgt to access any service on behalf of the victim.
Here is an example:
From the enumeration, we know that dcorp-appsrv machine has configured unconstrained delegation:
And we have compromised that machine as well. So we first login to the the dcorp-appsrv and check if there is any TGT from domain admin:
Currently don’t have. So we can use powerview’s user-hunter function to monitor the session from the administrator to the dcorp-appsrv machine:
Once the administrator has accessed the service on dcorp-appsrv, because of the unconstrained delegation, administrator’s TGT will be passed on to the dcorp-appsrv, and we can dump it:
using mimikatz to inject into our own session. Then accessing any services in the domain, as the domain admin can do:
From the previous example, we can see that unconstrained delegation is not very easy to abuse, because we need to wait for the high privilege user account to access the service on the machine, it is quite passive. However, there is some way that we can make it into an active exploitation. By exploiting the Microsoft print system remote protocol, we can force any machine with print spooler running to authenticate to any target using Kerberos:
And what is good about this technique is that the printer Spooler service is enabled by default:
Bear in mind that the server to catch the incoming ticket should enable unconstrained delegation on the machine account, not the user account:
And here is a demo using the printer spool exploit to force the other machine to access a machine with unconstrained delegation enabled and using Rubeus to catch the incoming ticket:
After unconstrained delegation, let’s take a look at constrained delegation, which was introduced in windows server 2003.
But before that, let’s take a look at one extension for the Kerberos protocol, which is to facilitate the unconstrained delegation.
Service for User, short for S4U
There are two sub-extension in S4U:
- one is Service for user to self, this extension allows a service to obtain a service ticket to itself on behalf of a user. This is for the transition of the protocol. When user is trying to access a service via authentication other can Kerberos, then this service can request a service ticket to itself from the KDC, on behalf of the user, so it seems like the user is also access the service via Kerberos protocol. This extension is also important when requesting the service ticket via Service for user to proxy(S4U2proxy).
- Service for user to proxy(S4U2proxy) is a protocol which allows a service to obtain a service ticket to another service on behalf of a user.
Now let’s look at this scenario:
user authenticate to Service 1 via non-Kerberos protocol, for example, http form authentication
(assume Service 1 has authenticated with KDC, so it has its own TGT) Service 1 asks for service ticket to itself on behalf of user using user’s name and user’s realm name in S4U2self data
KDC returns a forwardable service ticket to Service 1 as if it was requested from the user using his TGT. Service ticket contains authorization data of the user
Service 1 respond to the user. S4U2self protocol restricts Service 1 to make requests for other service on behalf of user.
User makes request to Service 1, Service 1 needs to access Service 2 as user.
- Service 1 has authenticated with KDC and has valid TGT.(as in step 1)
- Service 1 has forwardable service ticket from user to Service 1.(returned from KDC in step 3)
Service 1 requests service ticket to Service 2 on behalf of user, identified by client name and client realm in service ticket for Service 1.
KDC validates PAC if applicable. Return service ticket for Service 2, with cname and crealm are that of the user
Service 1 uses service ticket to make a request to Service 2. Service 2 treats the request as if it is coming from the user, assuming user was authenticated by KDC
Service 2 responds to the request.
Service 1 responds to the user’s request in (5)
Throughout the process, there are two security check ups:
S4U2self process is only allowed if the requesting service has the TRUSTED_TO_AUTH_FOR_DELEGATION field set in their userAccountControl field (will be revised later):
We can use this command to enumerate the user which is configured with TRUSTED_TO_AUTH_FOR_DELEGATION in the userAccountControl field.
Get-DomainUser –TrustedToAuth -Properties distinguishedname,useraccountcontrol,msds-allowedtodelegateto | fl
S4u2proxy process is only allowed to use the forwardable ticket to request a service ticket for any SPN specified in msds-allowedtodelegateto field.
We can see that websvc can only delegate to CIFS service on dcorp-mssql.
During the authentication process, we can see that user’s credential is never required.
So If we have the service account password or hash, we can forge a service ticket from any user, like administrator to all the services listed in the msds-allowedtodelegateto field.
But not only this, the Sname in TGS_REQ, which indicates the the service name being accessed, is not being signed, encrypted, or even checked when KDC receive the TGS_REQ, it means we can any how replace the service name, as long as that service is also running under the same account.
Meaning that if ServiceX under UserY is in msds-allowedtodelegateto field, then all the services under UserY can be abused.
Here is an example extended from the last screenshot, where websvc can only delegate to CIFS service on dcorp-mssql.
First we are asking for a TGT for the websvc account. This is the presumption for the service 1 in the scenario which we go through.
Then we can ask for a service ticket on behalf of any user, here I am using domain administrator, and KDC returns a valid service ticket. Now I can dump this ticket and inject into my session, then use this ticket to access the CIFS service on dcorp-mssql as if I am the domain controller.
Here is another example:
We can see that dcorp-adminsrv is allow to delegate to the Time service under the machine account of the domain controller:
In this way, we can simply replace Time service by other service and requesting a valid service ticket to access that service:
In this case, I am generating a service ticket to the LDAP service on the domain controller.
After injecting this service ticket into my session, I an abuse the LDAP and perform the DCSync attack to dump the hash of any user:
I dumped the krbtgt’s hash so later I can use this hash to forge a golden ticket for persistence, which I won’t cover in this article.
There is another constrained delegation, which is introduced in server 2012, resource-based constrained delegation.
Unlike classical constrained delegation, where service A is only allowed to delegate to service B if service B is in the msDS-AllowedtoDelegeteTo field of the service A, in resource-based constrained delegation, service A is only allowed to delegate to Service B if Service A is in the msDS-AllowedToActOnBehalfOfOtherIdentity. So, we can see these two constrained delegations have the opposite direction of the constraint.
Ok, there are some information about S4U which I concluded before needs to be revised.
S4U2self process is only allowed if the requesting service has the TRUSTED_TO_AUTH_FOR_DELEGATION field set in their userAccountControl field. (false)
That is not true. S4U2self process is always available, when TRUSTED_TO_AUTH_FOR_DELEGATION is not set, service ticket returned is non-forwardable.
S4U2Proxy won’t work for non-forwardable service ticket. (false)
That is not accurate either. S4U2Proxy won’t work for non-forwardable service ticket in classical constrained delegation but will always work for resource-based constrained delegation, regardless of forwardable bit.
There is another difference between classical and resource-based constrained delegation:
In classical constrained delegation, Domain admin privilege is required to modify msds-allowedtodelegateto field, but for resourced based constrained delegation, computer account is allowed to modify AllowedToActOnBehalfOfOtherIdentity field. So, it is easier to modify who can be delegated to in resource-based constrained delegation, because a much lower privilege is required.
There is another interesting feature in active directory which can be abused when trying to abuse resource based constrained delegation: all the domain users is allowed to add 10 computer account to the domain.
So now let’s take a look at a possible attack scenario in resource-based constrained delegation, which would allow us to perform the local privilege escalation(There is one presumption: the low privileged user is able to modify msDS-AllowedToActOnBehalfOfOtherIdentity field of the machine.
User creates a computer account EvilPC with SPN registered
User configure msds-allowedtoactonbehalfofotheridentity of the machine and add EvilPC sid in
AS_REQ as EvilPC, get TGT(prerequisite for S4U2self)
S4U2self, get non-forwardable service ticket as administrator
S4U2proxy, get valid service ticket to High Priv Service as administrator (using the non-forwardable service ticket)
Use service ticket from (5) to access the High Priv Service
Here we might wonder, how applicable is this attack scenario in the real life, because the presumption requires the user being able to modify the msDS-AllowedToActOnBehalfOfOtherIdentity field of the machine.
First, we should know that when modifying the msDS-AllowedToActOnBehalfOfOtherIdentity field of the machine, it is accessing LDAP service on the domain controller
According to the Microsoft’s document, when network service account in a domain is trying to access the service outside the local machine, it will use the machine account to access. That also applies to Application Pool Identities.
What service is running under these accounts? Mssql is under network service, and iis-pool, which is the web service IIS.
So indeed, it is very applicable in real life. Once we have an iis webshell, or a low privilege account in mssql, it is possible for us to escalate our privilege to local administrator/system.
Actually, there are many other attacking techniques in resource-based constrained delegation, due to the time constraint, I will stop here, and if you are interested, you can take a look here.
So many referencing resources, I will just list out some:
[MS-SFU]: Kerberos Protocol Extensions: Service for User and Constrained Delegation Protocol: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sfu/3bff5864-8135-400e-bdd9-33b552051d94
harmj0y’s blog: https://blog.harmj0y.net/
Wagging the Dog: Abusing Resource-Based Constrained Delegation to Attack Active Directory: https://shenaniganslabs.io/2019/01/28/Wagging-the-Dog.html
Attacking and Defending Active Directory Lab from PentesterAcademy: https://www.pentesteracademy.com/activedirectorylab