Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/234: Added check to verify exchange operation enabled for audit. #241

Merged
merged 5 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Hawk/Hawk.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
'Get-HawkTenantEXOAdmin',
'Get-HawkTenantMailItemsAccessed',
'Get-HawkUserMailItemsAccessed',
'Get-HawkUserExchangeSearchQuery',
'Get-HawkTenantAppAndSPNCredentialDetail',
'Get-HawkTenantEntraIDUser',
'Get-HawkTenantDomainActivity',
Expand Down
2 changes: 2 additions & 0 deletions Hawk/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,6 @@
- Added functionality to expand detect M365 license type to determine max log retention time
- Added ability to expand search up to 365 days
- Added search of mail items accessed to the User Investigation (Get-HawkUserMailItemsAccessed)
- Added search of Exchange Search Queries to the User Investigation (Get-HawkUserExchangeSearchQuery)
- Implemented check to verify that an Exchange operation is enabled for auditing before attempting to pull logs

95 changes: 95 additions & 0 deletions Hawk/functions/User/Get-HawkUserExchangeSearchQuery.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
Function Get-HawkUserExchangeSearchQuery {
<#
.SYNOPSIS
This will export SearchQueryInitiatedExchange operations from the Unified Audit Log (UAL). Must be connected to Exchange Online
using the Connect-EXO or Connect-ExchangeOnline module. M365 E5 or G5 license is required for this function to work.
This telemetry will ONLY be availabe if Advanced Auditing is enabled for the M365 user.
.DESCRIPTION
This function queries for searches performed in Exchange, providing visibility into what users are searching for and potential
indications of insider threats or data exploration during incidents.
.PARAMETER UserPrincipalName
Specific user(s) to be investigated
.EXAMPLE
Get-HawkUserExchangeSearchQuery -UserPrincipalName bsmith@contoso.com
Returns search queries from Unified Audit Log (UAL) that correspond to the UserPrincipalName that is provided
.OUTPUTS
ExchangeSearchQueries_bsmith@contoso.com.csv /json
Simple_ExchangeSearchQueries_bsmith@contoso.com.csv/json

.LINK
https://www.microsoft.com/security/blog/2020/12/21/advice-for-incident-responders-on-recovery-from-systemic-identity-compromises/

.NOTES
"Operation Properties" and "Folders" will return "System.Object" as they are nested JSON within the AuditData field.
You will need to conduct individual log pull and review via PowerShell or other SIEM to determine values
for those fields.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[array]$UserPrincipalName
)

BEGIN {
# Check if Hawk object exists and is fully initialized
if (Test-HawkGlobalObject) {
Initialize-HawkGlobalObject
}
Out-LogFile "Starting Unified Audit Log (UAL) search for 'SearchQueryInitiatedExchange'" -Action
Out-LogFile "Please be patient, this can take a while..." -Information
Test-EXOConnection
}#End Begin

PROCESS {

#Verify UPN input
[array]$UserArray = Test-UserObject -ToTest $UserPrincipalName

foreach($UserObject in $UserArray) {
[string]$User = $UserObject.UserPrincipalName

# Verify that user has operation enabled for auditing. Otherwise, move onto next user.
if (Test-OperationEnabled -User $User -Operation 'SearchQueryInitiated') {
Out-LogFile "Operation 'SearchQueryInitiated' verified enabled for $User." -Information
try {
#Retrieve all audit data for mailitems accessed
$SearchCommand = "Search-UnifiedAuditLog -Operations 'SearchQueryInitiatedExchange' -UserIds $User"
$ExchangeSearches = Get-AllUnifiedAuditLogEntry -UnifiedSearch $SearchCommand

if ($ExchangeSearches.Count -gt 0){

#Define output directory path for user
$UserFolder = Join-Path -Path $Hawk.FilePath -ChildPath $User

#Create user directory if it doesn't already exist
if (-not (Test-Path -Path $UserFolder)) {
New-Item -Path $UserFolder -ItemType Directory -Force | Out-Null
}

#Compress raw data into more simple view
$ExchangeSearchesSimple = $ExchangeSearches | Get-SimpleUnifiedAuditLog

#Export both raw and simplistic views to specified user's folder
$ExchangeSearches | Select-Object -ExpandProperty AuditData | Convertfrom-Json | Out-MultipleFileType -FilePrefix "ExchangeSearchQueries_$User" -User $User -csv -json
$ExchangeSearchesSimple | Out-MultipleFileType -FilePrefix "Simple_ExchangeSearchQueries_$User" -User $User -csv -json
} else {
Out-LogFile "Get-HawkUserExchangeSearchQuery completed successfully" -Information
Out-LogFile "No items found for $User." -Information
}
} catch {
Out-LogFile "Error processing Exchange Search Queries for $User : $_" -isError
Write-Error -ErrorRecord $_ -ErrorAction Continue
}
} else {
Out-LogFile "Operation 'SearchQueryInitiated' is not enabled for $User." -Information
Out-LogFile "No data recorded for $User." -Information
}
}

}#End Process

END{
Out-Logfile "Completed exporting Search Query logs" -Information
}#End End

}
54 changes: 31 additions & 23 deletions Hawk/functions/User/Get-HawkUserMailItemsAccessed.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -49,34 +49,42 @@ Function Get-HawkUserMailItemsAccessed {

foreach($UserObject in $UserArray) {
[string]$User = $UserObject.UserPrincipalName
try {
#Retrieve all audit data for mailitems accessed
$SearchCommand = "Search-UnifiedAuditLog -Operations 'MailItemsAccessed' -UserIds $User"
$MailboxItemsAccessed = Get-AllUnifiedAuditLogEntry -UnifiedSearch $SearchCommand

if ($MailboxItemsAccessed.Count -gt 0){

# Verify that user has operation enabled for auditing. Otherwise, move onto next user.
if (Test-OperationEnabled -User $User -Operation 'MailItemsAccessed') {
Out-LogFile "Operation 'MailItemsAccessed' verified enabled for $User." -Information
try {
#Retrieve all audit data for mailitems accessed
$SearchCommand = "Search-UnifiedAuditLog -Operations 'MailItemsAccessed' -UserIds $User"
$MailboxItemsAccessed = Get-AllUnifiedAuditLogEntry -UnifiedSearch $SearchCommand

#Define output directory path for user
$UserFolder = Join-Path -Path $Hawk.FilePath -ChildPath $User
if ($MailboxItemsAccessed.Count -gt 0){

#Define output directory path for user
$UserFolder = Join-Path -Path $Hawk.FilePath -ChildPath $User

#Create user directory if it doesn't already exist
if (-not (Test-Path -Path $UserFolder)) {
New-Item -Path $UserFolder -ItemType Directory -Force | Out-Null
}
#Create user directory if it doesn't already exist
if (-not (Test-Path -Path $UserFolder)) {
New-Item -Path $UserFolder -ItemType Directory -Force | Out-Null
}

#Compress raw data into more simple view
$MailboxItemsAccessedSimple = $MailboxItemsAccessed | Get-SimpleUnifiedAuditLog
#Compress raw data into more simple view
$MailboxItemsAccessedSimple = $MailboxItemsAccessed | Get-SimpleUnifiedAuditLog

#Export both raw and simplistic views to specified user's folder
$MailboxItemsAccessed | Select-Object -ExpandProperty AuditData | Convertfrom-Json | Out-MultipleFileType -FilePrefix "MailItemsAccessed_$User" -User $User -csv -json
$MailboxItemsAccessedSimple | Out-MultipleFileType -FilePrefix "Simple_MailItemsAccessed_$User" -User $User -csv -json
} else {
Out-LogFile "Get-HawkUserMailItemsAccesed completed successfully" -Information
Out-LogFile "No items found for $User." -Information
#Export both raw and simplistic views to specified user's folder
$MailboxItemsAccessed | Select-Object -ExpandProperty AuditData | Convertfrom-Json | Out-MultipleFileType -FilePrefix "MailItemsAccessed_$User" -User $User -csv -json
$MailboxItemsAccessedSimple | Out-MultipleFileType -FilePrefix "Simple_MailItemsAccessed_$User" -User $User -csv -json
} else {
Out-LogFile "Get-HawkUserMailItemsAccesed completed successfully" -Information
Out-LogFile "No items found for $User." -Information
}
} catch {
Out-LogFile "Error processing mail items accessed for $User : $_" -isError
Write-Error -ErrorRecord $_ -ErrorAction Continue
}
} catch {
Out-LogFile "Error processing mail items accessed for $User : $_" -isError
Write-Error -ErrorRecord $_ -ErrorAction Continue
} else {
Out-LogFile "Operation 'MailItemsAccessed' is not enabled for $User." -Information
Out-LogFile "No data recorded for $User." -Information
}
}

Expand Down
58 changes: 58 additions & 0 deletions Hawk/internal/functions/Test-OperationEnabled.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Function Test-OperationEnabled {
<#
.SYNOPSIS
Tests if a specified audit operation is enabled for a given user.

.DESCRIPTION
An internal helper function that verifies whether a specific audit operation
is enabled in a user's mailbox auditing configuration. This function queries
the mailbox settings using Get-Mailbox and checks the AuditOwner property
for the specified operation.

.PARAMETER User
The UserPrincipalName of the user to check auditing configuration for.

.PARAMETER Operation
The specific audit operation to check for (e.g., 'SearchQueryInitiated').

.EXAMPLE
$result = Test-OperationEnabled -User "user@contoso.com" -Operation "SearchQueryInitiated"

Checks if the SearchQueryInitiated operation is enabled for user@contoso.com's mailbox.
Returns True if enabled, False if not enabled.

.EXAMPLE
if (Test-OperationEnabled -User $userUpn -Operation 'MailItemsAccessed') {
# Proceed with mail items access audit
}

Shows how to use the function in a conditional check before performing
an audit operation that requires specific permissions.

.OUTPUTS
System.Boolean
Returns True if the operation is enabled for the user, False otherwise.

.NOTES
Internal Function
Author: Jonathan Butler
Requirements: Exchange Online PowerShell session with appropriate permissions
#>
[CmdletBinding()]
[OutputType([bool])]
param(
[Parameter(Mandatory=$true)]
[string]$User,
[Parameter(Mandatory=$true)]
[string]$Operation
)

# Verify the provided User has the specified Operation enabled
$TestResult = Get-Mailbox -Identity $User | Where-Object -Property AuditOwner -eq $Operation

if ($null -eq $TestResult) {
return $false
} else {
return $true
}
}
Loading