The evolving phishing threat is relentless and continues to grow each year. Attackers have been changing their tactics, techniques, and procedures, moving from traditional phishing to more advanced techniques. Especially, AiTM is a great example of how attack techniques have evolved over the past few years and have spread globally.
AiTM attack refers to "Adversary-in-The-Middle" phishing technique where attackers intercept communication between an end-user and a legitimate website, stealing passwords and session cookies to gain unauthorized access and perform fraudulent activities such as controlling exchange online. Subsequently, they launch Business Email Compromised (BEC) campaign after compromising the account. (Figure 1)
Figure 1. Overview of AiTM phishing campaign and follow-on BEC
In this blog, I am excited to share four essential points for threat hunting, focusing on how to track "potential" AiTM/BEC activities using Kusto Query Language (KQL) in Microsoft 365 Defender and Azure Data Explorer:
1. Tracking the initial access - Suspicious User-Clicked URLs
As the attacker aims to successfully lure the target to a phishing site and avoid detection by email security tools, they primally rely on HTML file attachment, or they might use URL redirection. Therefore, the initial access will be divided into two cases : HTML file and URL.
This query will identify inbound emails with HTML attachments and compile a list of all associated URLs.
let HTMLfile = (EmailAttachmentInfo | where FileType =~ "html"); let HTMLurl = (EmailUrlInfo | where UrlLocation == "Attachment" | summarize HTMLfile_URL_list = make_list(Url) by NetworkMessageId); let Emailurl = (EmailUrlInfo | where UrlLocation == "Body" | summarize Email_URL_list = make_list(Url) by NetworkMessageId); EmailEvents | where EmailDirection == "Inbound" | join kind = inner HTMLfile on NetworkMessageId | join kind = inner HTMLurl on NetworkMessageId | join kind = leftouter Emailurl on NetworkMessageId | project Timestamp, ReportId, NetworkMessageId, SenderFromAddress, RecipientEmailAddress, FileName, FileType, ThreatTypes, ThreatNames, HTMLfile_URL_list, Email_URL_list
This query will identify users who received suspicious emails and clicked on URLs from those emails.
let UserClickedLink = (UrlClickEvents | where Workload == "Email" | where ActionType == "ClickAllowed" or IsClickedThrough != "0"); EmailEvents | where EmailDirection == "Inbound" | where ThreatTypes has_any ("Phish", "Malware") | join kind = inner UserClickedLink on NetworkMessageId | project Timestamp, ReportId, NetworkMessageId, SenderFromAddress, RecipientEmailAddress, ActionType, IsClickedThrough, Url
Additionally, If the device has been onboarded to Microsoft Defender for Endpoint, this query will be able to identify not only users who received suspicious emails and clicked on URLs from those emails but also the associated devices.
let ClickedURL = EmailEvents | where EmailDirection == "Inbound" | where ThreatTypes has_any ("Phish", "Malware") | join (UrlClickEvents | where Workload == "Email" | where ActionType == "ClickAllowed" or IsClickedThrough != "0") on NetworkMessageId | project Timestamp, AccountUpn, Url, UrlChain, UrlCount, DeliveryAction; DeviceEvents | where ActionType == "BrowserLaunchedToOpenUrl" | where InitiatingProcessFileName =~ "outlook.exe" | join kind = inner ClickedURL on $left.RemoteUrl == $right.Url | project Timestamp, DeviceId, DeviceName, AccountUpn, Url, UrlChain, UrlCount
2. Tracking Suspicious User-Clicked URLs based on IoCs
As Indicators of Compromise (IOCs) are shared by some security blogs, leveraging the externaldata operator is one of the ways to determine whether end-users clicked on AiTM-related URLs or not within the organization.
As an example, the process has been summarized into three steps.
let Zscaler_IoC = externaldata(Type:string, Value:string) [@'https://raw.githubusercontent.com/LearningKijo/KQL/main/KQL-XDR-Hunting/ThreatHunting/IOCs-Folder/AiTM-Zscaler-IoC.csv'] with (format='csv', ignorefirstrecord = true); let Microsoft_IoC = externaldata(Type:string, Value:string) [@'https://raw.githubusercontent.com/LearningKijo/KQL/main/KQL-XDR-Hunting/ThreatHunting/IOCs-Folder/AiTM-Microsoft-IoC.csv'] with (format='csv', ignorefirstrecord = true); let Zscaler = (Zscaler_IoC | project Value); let Microsoft = (Microsoft_IoC | project Value); UrlClickEvents | where Workload == "Email" | where ActionType == "ClickAllowed" or IsClickedThrough != "0" | where Url has_any (Zscaler) or Url has_any (Microsoft) | extend IoC = case(Url has_any (Zscaler), "Zscaler", Url has_any (Microsoft), "Microsoft", "N/A") | project Timestamp, NetworkMessageId, ReportId, AccountUpn, Url, ThreatTypes, DetectionMethods, IoC
As Step 2 describes 'an external storage,' GitHub aacount(LearningKijo) was used in the above query, but when you write your own query, you can use others such as your personal GitHub storage space, Azure Blob storage, and so on.
3. Visualizing the Targeted User's Access with Geolocation Map
To enhance geolocation visibility, start by checking suspicious logins from multiple countries. Once discovered the targeted user, filter the user's location and create a map using Azure Data Explorer.
Here are steps:
Step 1 : By using this query, it appears that "Malware M365D" has multiple logons across different countries.
AADSignInEventsBeta | where Timestamp > ago(7d) | where ApplicationId == "4765445b-32c6-49b0-83e6-1d93765276ca" | where ClientAppUsed == "Browser" | where LogonType has "interactiveUser" | summarize Countries = make_set(Country) by AccountObjectId, AccountDisplayName
Step 2 : In order to obtain additional insights for geolocation, such as Latitude, Longitude, and Country, filter with "Malware M365D"
AADSignInEventsBeta | where Timestamp > ago(7d) | where ApplicationId == "4765445b-32c6-49b0-83e6-1d93765276ca" | where ClientAppUsed == "Browser" | where LogonType has "interactiveUser" | where AccountUpn == "email@example.com"
Step 3 : Based on the insights from Step 2, in the US, there are multiple logins from different states.
datatable (Latitude:real, Longitude:real, Country:string, Timestamp:datetime)[ 35.69628,139.7386,"JP",datetime(2023-07-03), 22.24831,114.1524,"HK",datetime(2023-07-03), 38.73078,-78.17197,"US",datetime(2023-07-07), 51,9,"DE",datetime(2023-07-04), 47.791939,264.887962,"US",datetime(2023-07-03), 31.289113,261.269105,"US",datetime(2023-07-04), 45.415804,262.925967,"US",datetime(2023-07-05), 38.934844,249.760892,"US",datetime(2023-07-06) ] | project Longitude, Latitude, Timestamp | render scatterchart with (kind = map)
At this time, datatable (data is from AADSignInEventBeta) is used to generate a geolocation map.
Geospatial data can be visualized using the render operator in Kusto Desktop Explorer or the Azure Data Explorer web UI. To download Kusto Desktop Explorer, see Kusto.Explorer installation and user interface.
4. Capturing Suspicious EXO Activities for the Targeted User
After successfully stealing the session, the attacker proceeds with exfiltration and BEC campaigns. To detect potential suspicious activities, we filter Exchange Online action types that could be used for exfiltration and BEC. This allows us to investigate whether the targeted end-user is involved in any suspicious actions.
Here are some common techniques attackers use during exfiltration in Exchange Online.
CloudAppEvents | where Timestamp > ago(30d) | extend parsed = parse_json(RawEventData) | where Application == "Microsoft Exchange Online" and ActionType in ("Add-MailboxPermission", "New-ManagementRoleAssignment", "Add-MailboxFolderPermission", "New-InboxRule", "Set-InboxRule", "Set-Mailbox", "New-TransportRule", "Set-TransportRule") and not(parsed.UserId has_any ('NT AUTHORITY\\SYSTEM (Microsoft.Exchange.ServiceHost)', 'NT AUTHORITY\\SYSTEM (w3wp)', 'devilfish-applicationaccount')) | extend parsed = parse_json(RawEventData) | extend UPN = tostring(parsed.UserId) | where UPN == "firstname.lastname@example.org" | extend Parameters = parsed.Parameters | mv-expand Parameters | extend Name = tostring(Parameters.Name) | extend Value = tostring(Parameters.Value) | extend packed = pack(Name, Value) | summarize PackedInfo = make_bag(packed), ActionType=any(ActionType) by ReportId, UPN | evaluate bag_unpack(PackedInfo)
I hope these threat hunting insights will be beneficial for anyone involved in security operations.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.