Icewolf.EXO.SpamAnalyze.psm1
############################################################################## # Invoke-SpamAnalyze.ps1 # Get SPAM Detail Info for Specific MessageTraceID in MessageTrace # V2.0.0 04.05.2021 - Andres Bohren / Initial Version # V2.0.1 07.05.2021 - Andres Bohren / Bugfixes # V2.0.2 09.05.2021 - Andres Bohren / Bugfixes # V2.0.3 16.06.2021 - Andres Bohren / Bugfixes Improvements # V2.0.4 30.09.2021 - Andres Bohren / Bugfixes Improvements # V2.0.5 23.12.2021 - Andres Bohren / Bugfixes Improvements # V2.0.6 01.03.2022 - Andres Bohren / Added dependency Module ExchangeOnlineManagement # V2.0.7-Alpha 10.03.2022 - Andres Bohren / Added Error Handling (Try / Catch) for DNS over HTTPS Querys # - DNS Query changed from Cloudflare to Google # https://developers.google.com/speed/public-dns/docs/doh # https://developers.google.com/speed/public-dns/docs/doh/json # - Subject is Limited to 20 Characters, so the MessageTraceId is still visible at long Subjects # V2.0.7-Beta # - Added diffrent Error Handling (Try / Catch) for DNS over HTTPS Querys # V2.0.7-Gamma # - Added Info for Message Event "Send External" # - Fixed Issue with DNS over HTTPS Querys ############################################################################## #Requires -Modules ExchangeOnlineManagement Function Invoke-SpamAnalyze { <# .SYNOPSIS .DESCRIPTION Get SPAM Detail Info for Specific MessageTraceID in MessageTrace .PARAMETER Recipientaddress The Emailaddress of the Recipient .PARAMETER SenderAddress The Emailadress of the Sender .EXAMPLE .\SpamAnalyze.ps1 -SenderAddress SenderAddress@domain.tld -RecipientAddress RecipientAddress@domain.tld .LINK #> Param( [parameter(Mandatory=$true)][String]$RecipientAddress, [parameter(Mandatory=$true)][String]$SenderAddress ) Begin { ############################################################################## # Connect to Exchange Online ############################################################################## Function Connect-EXO { If ($Null -eq (Get-PsSession | Where-Object {$_.ComputerName -eq "outlook.office365.com"})) { Write-Host "Connect to Exchange Online..." -f Gray Connect-ExchangeOnline -ShowBanner:$false If ($Null -eq (Get-PsSession | Where-Object {$_.ComputerName -like "*compliance.protection.outlook.com"})) { Write-Host "Connect to Security and Compliance..." -f Gray Connect-IPPSSession -WarningAction Silentlycontinue } else { Write-Host "Connection to Security and Compliance already exists" -ForegroundColor Green } } Else { Write-Host "Connection to Exchange Online already exists" -ForegroundColor Green If ($Null -eq (Get-PsSession | Where-Object {$_.ComputerName -like "*compliance.protection.outlook.com"})) { Write-Host "Connect to Security and Compliance..." -f Gray Connect-IPPSSession -WarningAction Silentlycontinue } else { Write-Host "Connection to Security and Compliance already exists" -ForegroundColor Green } } } ############################################################################## # Disconnect from Exchange Online ############################################################################## Function Disconnect-EXO { Write-Host "Disconnect from Exchange Online" -f Gray #fWrite-Log -fLogtext "Disconnect from Exchange Online" Get-PSSession | Where-Object {($_.ComputerName -eq "outlook.office365.com") -AND ($_.ConfigurationName -eq "Microsoft.Exchange")} | Remove-PSSession Get-PSSession | Where-Object {($_.ComputerName -like "compliance.protection.outlook.com") -AND ($_.ConfigurationName -eq "Microsoft.Exchange")} | Remove-PSSession } ############################################################################## # Check MessageTraceDetail ############################################################################## Function Get-SPAMinfo { Param( [parameter(Mandatory=$false)][String]$RecipientAddress, [parameter(Mandatory=$false)][String]$SenderAddress, [parameter(Mandatory=$true)][String]$MessageTraceId ) $Start = (Get-Date).AddDays(-10) $End = (Get-Date) Write-Host "Message events:" -ForegroundColor Magenta $MTDetail = Get-MessageTraceDetail -MessageTraceId $MessageTraceId -RecipientAddress $RecipientAddress -SenderAddress $SenderAddress -StartDate $Start -EndDate $End | Sort-Object Date $MTEventFail = $MTDetail | Where-Object {$_.event -eq "Failed"} If ($Null -ne $MTEventFail) { Write-Host "Failed-Event: " -ForegroundColor Magenta Write-Host (" Action: " +$MTEventFail.action ) Write-Host } $MTEventMal = $MTDetail | Where-Object {$_.event -eq "Malware"} If ($Null -ne $MTEventMal) { Write-Host "Malware-Event: " -ForegroundColor Magenta Write-Host (" Action: " +$MTEventMal.action ) Write-Host } #Send External / Extern senden $MTEventExternal = $MTDetail | Where-Object {$_.event -match "Extern"} If ($Null -ne $MTEventExternal) { Foreach ($SendExternal in $MTEventExternal) { Write-Host "Send External: " -ForegroundColor Magenta [xml]$xmlE =$MTEventExternal.Data #$xmlE.root.MEP $Items = $xmle.root.MEP.Count -1 for ($i=0; $i -lt$Items; $i++) { $Item = $xmle.root.MEP.Item($i) #$Item = $xmle.root.MEP.Item(1) $ItemProperty = ($item | Get-Member | Where-Object {$_.MemberType -eq "Property" -and $_.Name -ne "Name"} | Select Name).Name $ItemName = $Item.Name $ItemValue = $Item.$ItemProperty If ($ItemName -eq "CustomData") { $Blob = $ItemValue.Split(";") foreach ($Line in $blob) { Write-Host " $Line" } } else { Write-Host " $ItemName : $ItemValue" } } } } $MTEventSPM = $MTDetail | Where-Object {$_.event -eq "Spam"} | Select-Object -uniq # SPAM Detail If ($Null -ne $MTEventSPM) { Write-Host "SPAM-Event: " -ForegroundColor Magenta Write-Host (" Action: " +$MTEventSPM.action ) Write-Host Write-Host "SPAM-Event Details:" -ForegroundColor Magenta Write-Host "Anti-spam message headers: https://docs.microsoft.com/en-us/microsoft-365/security/office-365-security/anti-spam-message-headers" -f Cyan Write-Host "Spam confidence levels: https://docs.microsoft.com/en-us/microsoft-365/security/office-365-security/spam-confidence-levels" -f Cyan [xml]$xmlS = $MTEventSPM.Data $RcptCount = ($xmlS.root.MEP | Where-Object {$_.Name -eq "RcptCount"}) $DI = ($xmlS.root.MEP | Where-Object {$_.Name -eq "DI"}) $SCL = ($xmlS.root.MEP | Where-Object {$_.Name -eq "SCL"}) $Score = ($xmlS.root.MEP | Where-Object {$_.Name -eq "Score"}) $SFV = ($xmlS.root.MEP | Where-Object {$_.Name -eq "SFV"}) $ClientIP = ($xmlS.root.MEP | Where-Object {$_.Name -eq "CIP"}) $Country = ($xmlS.root.MEP | Where-Object {$_.Name -eq "Ctry"}) $HeloString = ($xmlS.root.MEP | Where-Object {$_.Name -eq "H"}) $ReturnPath = ($xmlS.root.MEP | Where-Object {$_.Name -eq "ReturnPath"}) $Language = ($xmlS.root.MEP | Where-Object {$_.Name -eq "Language"}) Write-Host (" RecipientCount: " +$RcptCount.Integer) switch ($DI.String) { "SB" { $DI = "(SB) The sender of the message was blocked" } "SQ" { $DI = "(SQ) The message was quarantined" } "SD" { $DI = "(SD) The message was deleted" } "SJ" { $DI = "(SJ) The message was sent to the recipient's Junk Email folder" } "SN" { $DI = "(SN) The message was routed through the higher risk delivery pool" } "SO" { $DI = "(SO) The message was routed through the normal outbound delivery pool" } } Write-Host (" DI: " +$DI) # Color for SCL switch ($SCL.Integer) { -1 { $cSCL = "Green"; $Folder = "Inbox" } 0 { $cSCL = "Green"; $Folder = "Inbox" } 1 { $cSCL = "Green"; $Folder = "Inbox" } 2 { $cSCL = "Green"; $Folder = "Inbox" } 3 { $cSCL = "Green"; $Folder = "Inbox" } 4 { $cSCL = "Green"; $Folder = "Inbox" } 5 { $cSCL = "Yellow"; $Folder = "Junk-E-Mail" } 6 { $cSCL = "Yellow"; $Folder = "Junk-E-Mail" } 7 { $cSCL = "Red"; $Folder = "Quarantaine" } 8 { $cSCL = "Red"; $Folder = "Quarantaine" } 9 { $cSCL = "Red"; $Folder = "Quarantaine" } } Write-Host (" SpamConfidenceLevel (SCL): "+$SCL.Integer +" Deliver to: " +$Folder +")") -f $cSCL Write-Host (" SpamScoreLevel (Score): "+$Score.Integer ) switch ($SFV.String) { "BLK" { $SFV = "(BLK) Filtering was skipped and the message was blocked because it originated from a blocked sender" } "NSPM" { $SFV = "(NSPM) The message was marked as non-spam and was sent to the intended recipients" } "SFE" { $SFV = "(SFE) Filtering was skipped and the message was allowed because it was sent from an address in a user's Safe Senders list"} "SKA" { $SFV = "(SKA) The message skipped spam filtering and was delivered to the Inbox because the sender was in the allowed senders list or allowed domains list in an anti-spam policy"} "SKB" { $SFV = "(SKB) The message skipped spam filtering and was delivered to the Inbox because the sender was in the allowed senders list or allowed domains list in an anti-spam policy"} "SKI" { $SFV = "(SKI) Similar to SFV:SKN, the message skipped spam filtering for another reason (for example, an intra-organizational email within a tenant)"} "SKN" { $SFV = "(SKN) The message was marked as non-spam prior to being processed by spam filtering. For example, the message was marked as SCL -1 or Bypass spam filtering by a mail flow rule"} "SKQ" { $SFV = "(SKQ) The message was released from the quarantine and was sent to the intended recipients"} "SKS" { $SFV = "(SKS) The message was marked as spam prior to being processed by the content filter" } "SPM" { $SFV = "(SPM) The message was marked as spam by spam filtering" } } Write-Host (" SpamFilterVerdikt (SFV): " +$SFV) Write-Host (" SenderClientIP (CIP): " +$ClientIP.String) Write-Host (" Country (CTRY): " +$Country.String) Write-Host (" HeloString (H): " +$HeloString.String) Write-Host (" ReturnPath: " +$ReturnPath.String) Write-Host (" Language: " +$Language.String) Write-Host } Else { Write-Host "SPAM-Event: " -ForegroundColor Magenta Write-Host (" INFO: This mail contains no 'Spam' event ") -f Cyan Write-Host } } } Process { #Set Window and Bufferzite $pshost = get-host $pswindow = $pshost.ui.rawui $LanguageMode = $ExecutionContext.SessionState.LanguageMode If ($LanguageMode -eq "Fulllanguage"){ if ($pswindow.WindowSize.Width -lt 220){ $newsize = $pswindow.buffersize $newsize.height = 8000 $newsize.width = 220 $pswindow.buffersize = $newsize $newsize = $pswindow.windowsize $newsize.width = 180 $newsize.height = 60 $pswindow.windowsize = $newsize } } #Call Function to Connect to Exchange Online Connect-EXO #Check if Messagetrace is available Try { Get-Command Get-MessageTrace -ErrorAction Stop | Out-Null } catch { Write-Host "No Permission for the Command: Get-MessageTrace. Stopping script." #exit Break } #Set Start- and Enddate for Messagetrace $Start = ((Get-Date).AddDays(-10)) $End = Get-Date #Messagetrace depending on Parameters If ($SenderAddress -ne $Null) { If ($RecipientAddress -ne $Null) { $MT = Get-MessageTrace -StartDate (get-date).AddDays(-10) -EndDate (get-date) -SenderAddress $SenderAddress -RecipientAddress $RecipientAddress } else { $MT = Get-MessageTrace -StartDate (get-date).AddDays(-10) -EndDate (get-date) -SenderAddress $SenderAddress } } else { #SenderAddress = $Null / RecipientAddress populated $MT = Get-MessageTrace -StartDate (get-date).AddDays(-10) -EndDate (get-date) -RecipientAddress $RecipientAddress } #$MT | Format-Table Received, SenderAddress, RecipientAddress, Subject, Status, MessageTraceID $MT | Select-Object Received, SenderAddress, RecipientAddress, @{label='Subject';expression={$_.Subject.Substring(0,20)}}, Status, MessageTraceID | Format-Table If ($Null -eq $MT) { Write-Host "No Results in Message Trace found" } else { #Input MessageTraceID $readhost = Read-Host "MessageTraceID?" If ($readhost -eq "") { Write-Host "Not a MessageTraceID... Stopping Script" } else { #Write-Host "DEBUG: Readhost: $readhost" Foreach ($Line in $MT) { If ($readhost -eq $Line.MessageTraceId) { #Write-Host "DEBUG: MessageTraceID: $($Line.MessageTraceId)" #Write-Host "DEBUG: Sender: $($Line.Senderaddress)" #Write-Host "DEBUG: Recipient: $($Line.RecipientAddress)" $MessageTraceId = $Line.MessageTraceId $MTSenderAddress = $Line.Senderaddress $MTRecipientAddress = $Line.RecipientAddress $MTStatus = $Line.Status $MTSubject = $Line.Subject $MTReceived = $Line.Received $MTMessageID = $Line.MessageID #Infos from Message Trace Write-Host Write-Host "E-Mail Detail:" -ForegroundColor Magenta Write-Host " Message ID: $MTMessageID" Write-Host " Received: $MTReceived" Write-Host " Sender: $MTSenderAddress" Write-Host " Recipient: $MTRecipientAddress" Write-Host " Subject: $MTSubject" Write-Host " Status: $MTStatus" Write-Host #Check Recipient $ExoRecipient = Get-Recipient -Identity $MTRecipientAddress #$ExoRecipient $RecipientTypeDetails = $ExoRecipient.RecipientTypeDetails Write-Host "Recipient Details" -ForegroundColor Magenta Write-Host " RecipientTypeDetails: $RecipientTypeDetails" Write-Host #JunkMailConfiguration of Mailbox $SenderDomain = ($SenderAddress.Split("@")[1]) If ($RecipientTypeDetails -like "*Mailbox") { $JMC = Get-MailboxJunkEmailConfiguration -Identity $MTRecipientAddress If ($NULL -ne $JMC) { Write-Host "Recipient JunkMailConfiguration" -ForegroundColor Magenta Write-Host " TrustedListsOnly: $($JMC.TrustedListsOnly)" Write-Host " ContactsTrusted: $($JMC.ContactsTrusted)" Write-Host Write-Host "Check if $MTSenderAddress exists in MAILBOX ($MTRecipientAddress) Trusted-/BlockedSenders list: " -ForegroundColor Magenta If ($JMC.TrustedSendersAndDomains -contains $MTSenderAddress) { Write-Host " USER Junk-E-Mail Config: Found in 'TrustedSendersAndDomains'" -f Green } Else { Write-Host " USER Junk-E-Mail Config: Not found in 'TrustedSendersAndDomains'" -f White } If ($JMC.BlockedSendersAndDomains -contains $MTSenderAddress) { Write-Host " USER Junk-E-Mail Config: Found in 'BlockedSendersAndDomains'" -f Red } Else { Write-Host " USER Junk-E-Mail Config: Not found in 'BlockedSendersAndDomains'" -f White } Write-Host Write-Host "Check if $SenderDomain exists in MAILBOX ($MTRecipientAddress) Trusted-/BlockedSenders list: " -ForegroundColor Magenta If ($JMC.TrustedSendersAndDomains -contains $SenderDomain) { Write-Host " USER Junk-E-Mail Config: Found in 'TrustedSendersAndDomains'" -f Green } Else { Write-Host " USER Junk-E-Mail Config: Not found in 'TrustedSendersAndDomains'" -f White } If ($JMC.BlockedSendersAndDomains -contains $SenderDomain) { Write-Host " USER Junk-E-Mail Config: Found in 'BlockedSendersAndDomains'" -f Red } Else { Write-Host " USER Junk-E-Mail Config: Not found in 'BlockedSendersAndDomains'" -f White } Write-Host } } #GLOBALConfig Write-Host "Check if $MTSenderAddress exists in GLOBAL Trusted-/BlockedSender list: " -ForegroundColor Magenta $GLOBALJunkConfig = Get-HostedContentFilterPolicy #Allowed Senders If ($GLOBALJunkConfig.AllowedSenders -match $MTSenderAddress) { Write-Host " GLOBAL EAC SpamFilter: Found in 'AllowedSenders'" -f Green } Else { Write-Host " GLOBAL EAC SpamFilter: Not found in 'AllowedSenders'" -f White } #Blocked Senders If ($GLOBALJunkConfig.BlockedSenders -match $MTSenderAddress) { Write-Host " GLOBAL EAC SpamFilter: Found in 'BlockedSenders'" -f Red } Else { Write-Host " GLOBAL EAC SpamFilter: Not found in 'BlockedSenders'" -f White } Write-Host Write-Host "Check if $SenderDomain exists in GLOBAL Allowed-/BlockedSenderDomain list: " -ForegroundColor Magenta #Allowed Domains If ($GLOBALJunkConfig.AllowedSenderDomains.Domain -contains $SenderDomain) { Write-Host " GLOBAL EAC SpamFilter: Found in 'AllowedSenderDomains'" -f Green } Else { Write-Host " GLOBAL EAC SpamFilter: Not found in 'AllowedSenderDomains'" -f White } #Allowed Senders If ($GLOBALJunkConfig.BlockedSenderDomains.Domain -contains $SenderDomain) { Write-Host " GLOBAL EAC SpamFilter: Found in 'BlockedSenderDomains'" -f Red } Else { Write-Host " GLOBAL EAC SpamFilter: Not found in 'BlockedSenderDomains'" -f White } Write-Host Get-SPAMinfo -RecipientAddress $MTRecipientAddress -SenderAddress $MTSenderAddress -MessageTraceId $MessageTraceId #DNS Records Write-Host "DNS Records of $SenderDomain" -ForegroundColor Magenta #Test DNS over HTTP $json = Invoke-RestMethod -URI "https://dns.google/resolve?name=google.com&type=NS" If (($json | Get-Member -MemberType NoteProperty).count -lt 3) { Write-Host "This System does not Support DNS over HTTPS" -ForegroundColor Yellow } else { #NS Write-Host "NS" -ForegroundColor Magenta try { $json = Invoke-RestMethod -URI "https://dns.google/resolve?name=$SenderDomain&type=NS" [string]$NS = $json.Answer.data $NS } catch { if($_.ErrorDetails.Message) { Write-Host $_.ErrorDetails.Message } else { Write-Host $_ } } #MX Write-Host "MX" -ForegroundColor Magenta try { $json = Invoke-RestMethod -URI "https://dns.google/resolve?name=$SenderDomain&type=MX" [string]$MX = $json.Answer.data $MX } catch { if($_.ErrorDetails.Message) { Write-Host $_.ErrorDetails.Message } else { Write-Host $_ } } #SPF Write-Host "SPF" -ForegroundColor Magenta try { $json = Invoke-RestMethod -URI "https://dns.google/resolve?name=$SenderDomain&type=TXT" $TXT = $json.Answer.data $TXT = $TXT | Where-Object {$_ -match "v=spf1"} $SPF = $TXT If ($Null -eq $SPF) { Write-Host "NO SPF Record found" -ForegroundColor Yellow } else { $SPF } } catch { if($_.ErrorDetails.Message) { Write-Host $_.ErrorDetails.Message } else { Write-Host $_ } } #DKIM Write-Host "DKIM (Only checking for: selector1/selector2)" -ForegroundColor Magenta try { $json = Invoke-RestMethod -URI "https://dns.google/resolve?name=Selector1._domainkey.$SenderDomain&type=CNAME" $DKIM1 = $json.Answer.data $json = Invoke-RestMethod -URI "https://dns.google/resolve?name=Selector2._domainkey.$SenderDomain&type=CNAME" $DKIM2 = $json.Answer.data [string]$DKIM = "$DKIM1 $DKIM2" If ($DKIM -eq " ") { Write-Host "NO DKIM Record found" -ForegroundColor Yellow } else { $DKIM } } catch { if($_.ErrorDetails.Message) { Write-Host $_.ErrorDetails.Message } else { Write-Host $_ } } #DMARC Write-Host "DMARC" -ForegroundColor Magenta try { $json = Invoke-RestMethod -URI "https://dns.google/resolve?name=_dmarc.$SenderDomain&type=TXT" $DMARC = $json.Answer.data If ($Null -eq $DMARC) { Write-Host "NO DMARC Record found" -ForegroundColor Yellow } else { $DMARC } } catch { if($_.ErrorDetails.Message) { Write-Host $_.ErrorDetails.Message } else { Write-Host $_ } } } } } } } } End { #Disconnect from Exchange Online and #Disconnect-EXO } } |