Skip to content

Instantly share code, notes, and snippets.

@glides
Created February 4, 2020 09:14
Show Gist options
  • Save glides/5f866268906f7176a8a8955ef9709cfc to your computer and use it in GitHub Desktop.
Save glides/5f866268906f7176a8a8955ef9709cfc to your computer and use it in GitHub Desktop.
Find all out of country logins Office 365 Unified Audit Log
########## Connect to o365 ##########
## Change $username and $pass to match your environment
## See: https://blogs.technet.microsoft.com/robcost/2008/05/01/powershell-tip-storing-and-using-password-credentials/ for how to create a cred file.
$username = "[email protected]"
$pass = gc "F:\PSScripts\Office365-PowershellCreds.txt" | convertto-securestring
$creds = New-Object -typename System.Management.Automation.PSCredential `
-argumentlist $username, $pass
$MESession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $Creds -Authentication Basic -AllowRedirection
Import-PSSession $MESession -AllowClobber
Connect-MsolService -Credential $creds
########################################
########## Setup global vars ##########
[Array]$TotalLogins = @()
[Array]$OutsideIPs = @()
$StartDate = Get-Date (Get-Date).AddHours(-1.25) -Format g # The UAL (Unified Audit Log) is in UTC time, keep that in mind
$EndDate = Get-Date (Get-Date).AddHours(-1) -Format g # The UAL (Unified Audit Log) is in UTC time, keep that in mind
$DateStamp = Get-date -Format yyyy-MM-dd_HH-mm-ss
$OutputFile = "<PATH>\$DateStamp-BadLogins.csv"
## E-mail notifications, logging
$SMTPserver = "smtpout.local"
$SMTPNotifySender = "[email protected]"
[Array]$SMTPNotifyRecipients = @('[email protected]','[email protected]','[email protected]')
########################################
## Function - Get-IPGeolocation | Takes an IP as an argument and returns geographic data for the IP
function Get-IPGeolocation
{
Param
(
[string]$IPAddress
)
$request = Invoke-RestMethod -Method Get -Uri "http://geoip.nekudo.com/api/$IPAddress"
[PSCustomObject]@{
IP = $request.IP
City = $request.City
Country = $request.Country.Name
Code = $request.Country.Code
Location = $request.Location.Latitude
Longitude = $request.Location.Longitude
TimeZone = $request.Location.Time_zone
}
}
# Provide feedback if ran from the console
Write-Host -ForegroundColor Yellow $(Get-Date -format 'G') "Gathering login information from the Unified Audit Log."
## This loop gathers all logins between the StartDate and EndDate then extracts the json data so we can find the IP
## We use -SessionCommand ReturnLargeSet because there is a 5,000 result limit on ReturnNextPreviewPage and 50,000 on ReturnLargeSet but we must use a SessionId and continue to check the results until they are null
Do {
$Logins = Search-UnifiedAuditLog -SessionId $DateStamp -SessionCommand ReturnLargeSet -StartDate $StartDate -EndDate $EndDate -Operations "MailboxLogin","UserLoggedIn","PasswordLogonInitialAuthUsingPassword" | Select-Object -ExpandProperty AuditData | ConvertFrom-Json
$TotalLogins += $Logins
}
until ($Logins -eq $null) # One this is true, we've found all the logins for the given time period...time to move on
#Remove duplicates (using ReturnLargeSet above can contain dupes) and select the fields we are interested in
$Data = $TotalLogins | sort CreationTime, UserId -Unique | Select-Object CreationTime, UserId, Operation, ResultStatus, ClientIPAddress, ClientIP, ClientInfoString
# Provide feedback if ran from the console
write-host -ForegroundColor Green $(Get-Date -format 'G') $Data.Count "logins to check for between" $StartDate "and" $EndDate
write-host -ForegroundColor Yellow $(Get-Date -format 'G') "Checking for IPs outside of the US."
## This loop checks the IPs against a web API @ geoip.nekudo.com to get geographic information about the IP
## The two IF statements contain -notlike conditions to remove known good IP subnets so they are not checked, this speeds up the script
## The reason there are two IF statements is because MailboxLogin,UserLoggedIn, PasswordLogonInitialAuthUsingPassword don't store the IP in the same variable. ClientIPAddress is used in some and just Client.IP in the others
#!#!#!#!#!#!#
# You will need to modify or remove the "-notlike" conditions to match your environment
#!#!#!#!#!#!#
foreach ($ip in $Data) {
if ($ip.ClientIPAddress -And $ip.ClientIPAddress -notlike "###.###.*" -And $ip.ClientIPAddress -notlike "###.###.###.*"){
$geoInfo = Get-IPGeolocation $ip.ClientIPAddress
if ($geoInfo.Code -notlike "US") {
$ip | Add-Member -MemberType NoteProperty -Name Country -Value $geoInfo.Country
$ip | Add-Member -MemberType NoteProperty -Name IP -Value $geoInfo.IP
$OutsideIps += $ip
Write-Host -ForegroundColor Red $(Get-Date -format 'G') "Outside of US:" $geoInfo.IP
}
}
if ($ip.ClientIPAddress -And $ip.ClientIPAddress -notlike "###.###.*" -And $ip.ClientIPAddress -notlike "###.###.###.*"){
$geoInfo = Get-IPGeolocation $ip.ClientIP
if ($geoInfo.Code -notlike "US") {
$ip | Add-Member -MemberType NoteProperty -Name Country -Value $geoInfo.Country
$ip | Add-Member -MemberType NoteProperty -Name IP -Value $geoInfo.IP
$OutsideIps += $ip
Write-Host -ForegroundColor Red $(Get-Date -format 'G') "Outside of US:" $geoInfo.IP
}
}
}
# Provide feedback if ran from the console
write-host -ForegroundColor Yellow $(Get-Date -format 'G') $OutsideIPs.Count "out of" $Data.Count "where found to be outside of the US."
# Write data to output file (specified in Global Vars)
$OutsideIps | sort CreationTime, UserId -Unique |select CreationTime, UserId, Operation, ResultStatus, IP, Country, ClientInfoString | Export-Csv $OutputFile -NoTypeInformation
# If there are any logins found outside of the US, send an email to the specified users (Email server and recipients configured in global vars)
if ($OutsideIPs.count -gt 0) {
Send-MailMessage -To $SMTPNotifyRecipients -Priority High -Subject "Report: Bad Logins | IP Audit" -Body "Report is attached. Questionable logins were found.`r`n`r`n[\\ADFSPV02\F$\PSScripts\INDYAD-IPAudit.cmd]" -SmtpServer $SMTPServer -From $SmtpNotifySender -Attachments $OutputFile
}
#TODO
#API is out of date now, needs updated
#Check the time between the last known good login and the Outside of Country one to see if it's possible to travel that quickly. If not, that's a known bad login.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment