So this was another request by a reader; he has MFA configured for all his users, but still wants to know when the failed logon count increases. Mostly so he can warn his users that a possible spear-phising attempt might also be imminent. We know that when brute force does not work, focussed bad actors will often try the next avenue of attack.
At his request i’ve made the following scripts, one will monitor all possible locations using your partner credentials. The other will monitor only one tenant. I personally like the latter better as I’ve integrated this into my RMM so it can run and alert per client, Also it’s a little faster.
The Script
The script is designed to run at least every 4 hours, but can be run even on a 5-10 minute basis. It will get all info for the previous 4 hours, If you want to decrease on increase this you can edit line 13. Getting the logs is based on Elliot’s script to get the unified logs here. To connect with MFA, use my other blog here to generate your Secure App Model credentials.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
$credential = Get-Credential
Connect-MsolService -Credential $credential
$customers = Get-msolpartnercontract -All
$FilteredLogs = @()
$FailedLogonCount = 0
foreach ($customer in $customers) {
$InitDomain = $customer.DefaultDomainName
$DelegatedOrgURL = "https://outlook.office365.com/powershell-liveid?DelegatedOrg=" + $InitDomain
write-host "Connecting to $($customer.Name) Security Center"
$s = New-PSSession -ConnectionUri $DelegatedOrgURL -Credential $credential -Authentication Basic -ConfigurationName Microsoft.Exchange -AllowRedirection
Import-PSSession $s -CommandName Search-UnifiedAuditLog -AllowClobber
write-host "Getting last 30 minutes of logs for $($customer.Name)"
$startDate = (Get-Date).addhours(-4)
$endDate = (Get-Date)
$Logs = @()
Write-Host "Retrieving logs for $($customer.name)" -ForegroundColor Blue
do {
$logs += Search-unifiedAuditLog -SessionCommand ReturnLargeSet -SessionId $customer.name -ResultSize 5000 -StartDate $startDate -EndDate $endDate -Operations userloginfailed #-SessionId "$($customer.name)"
Write-Host "Retrieved $($logs.count) logs" -ForegroundColor Yellow
}while ($Logs.count % 5000 -eq 0 -and $logs.count -ne 0)
$FilteredLogs += $logs.auditdata | convertfrom-json -ErrorAction SilentlyContinue | Select-Object UserID, ClientIP | Group-Object -Property UserID
Foreach($item in $FilteredLogs){
if($item.name -ne $null){ $FailedLogonCount += $item.count; $FailedLogon += "$($Item.name) has $($item.count) failed logons from the following IPs: $($Item.group.ClientIP) `n" }
}
}
if(!$FailedLogonCount){ $FailedLogon = "Healthy"}
|
Get failed logins for only one tenant
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
$TenantName = "TenantDomain.onmicrosoft.com"
$credential = Get-Credential
Connect-MsolService -Credential $credential
$FilteredLogs = @()
$FailedLogonCount = 0
$customer = Get-msolpartnercontract | Where-Object {$_.DefaultDomainName -eq $TenantName}
$InitDomain = $customer.DefaultDomainName
$DelegatedOrgURL = "https://outlook.office365.com/powershell-liveid?DelegatedOrg=" + $InitDomain
write-host "Connecting to $($customer.Name) Security Center"
$s = New-PSSession -ConnectionUri $DelegatedOrgURL -Credential $credential -Authentication Basic -ConfigurationName Microsoft.Exchange -AllowRedirection
Import-PSSession $s -CommandName Search-UnifiedAuditLog -AllowClobber
write-host "Getting last 30 minutes of logs for $($customer.Name)"
$startDate = (Get-Date).addhours(-4)
$endDate = (Get-Date)
$Logs = @()
Write-Host "Retrieving logs for $($customer.name)" -ForegroundColor Blue
do {
$logs += Search-unifiedAuditLog -SessionCommand ReturnLargeSet -SessionId $customer.name -ResultSize 5000 -StartDate $startDate -EndDate $endDate -Operations userloginfailed #-SessionId "$($customer.name)"
Write-Host "Retrieved $($logs.count) logs" -ForegroundColor Yellow
}while ($Logs.count % 5000 -eq 0 -and $logs.count -ne 0)
$FilteredLogs += $logs.auditdata | convertfrom-json -ErrorAction SilentlyContinue | Select-Object UserID, ClientIP | Group-Object -Property UserID
Foreach($item in $FilteredLogs){
if($item.name -ne $null){ $FailedLogonCount += $item.count; $FailedLogon += "$($Item.name) has $($item.count) failed logons from the following IPs: $($Item.group.ClientIP) `n" }
}
if(!$FailedLogonCount){ $FailedLogon = "Healthy"}
|
You can choose to alert just on the FailedLogon variable, or alert based on the actual count via FailedLogonCount. As always, Happy Powershelling.