Detecting B64 encoded UPNs in Clicked URLs with KQL
- Damien van der Linden
- Oct 14, 2024
- 2 min read
Updated: Dec 5, 2024
Attackers love to hide their tracks by obfuscating user details (like email addresses) in URLs, often using base64 encoding to make the URL look less suspicious. This trick is common in phishing attacks where the URL might include hidden, encoded user info. But don’t worry—we can catch them!
In this post, I’ll show you how to use KQL in XDR to detect these sneaky URLs. We’ll focus on spotting phishing attempts where the URL contains encoded versions of a user’s email (UPN) or domain.
What Are We Doing?
We're going to:
Grab user emails and domains from logs.
Encode them in base64 (just like attackers do).
Look for URLs that have been clicked and contain these encoded values.
Let’s break down the query that does this.
The Query
UrlClickEvents
| where Timestamp > ago(2d)
| extend AccountUpnLower = tolower(AccountUpn)
| extend AccountDomain = tostring(split(AccountUpnLower, "@")[1])
| where isnotempty(AccountDomain)
| extend ShortUpn = substring(base64_encode_tostring(AccountUpnLower), 0, 20)
| extend ShortDomain = substring(base64_encode_tostring(AccountDomain), 4, strlen(base64_encode_tostring(AccountDomain)) - 8)
| where UrlChain contains ShortUpn or UrlChain contains ShortDomain
Step-by-Step Breakdown
Filter the Time Range
| where Timestamp > ago(2d)
We start by grabbing data from the last 2 days, change this to your needs. This helps keep our search focused on recent records.
Extract and Normalize User Info
| extend AccountUpnLower = tolower(AccountUpn)
| extend AccountDomain = tostring(split(AccountUpnLower, "@")[1])
We convert the user's email (UPN) to lowercase with the tolower() function.
Then, we extract the domain part (the part after the @) using split().
Encode UPN and Domain in Base64
| extend ShortUpn = substring(base64_encode_tostring(AccountUpnLower), 0, 20)
| extend ShortDomain = substring(base64_encode_tostring(AccountDomain), 4, strlen(base64_encode_tostring(AccountDomain)) - 8)
Now we base64-encode both the UPN and domain:
ShortUpn takes the first 20 characters of the encoded email.
ShortDomain grabs a part of the base64-encoded domain, starting from the 4th character to ignore any base64 prefix.
Match the Encoded Data in URLs
| where UrlChain contains ShortUpn or UrlChain contains ShortDomain
Finally, we check if the encoded email or domain shows up in any URL clickedin the URLClickEvents table. Be wary that this is not exclusive to phishing campagins and is regularly used in legitimate activation links, use this for hunting and not as a detection as it can generate false-positives. We can chain this query to look for anomalous login events after the URL click, blog post for later? This query wasn't built alone, I had some help from Gianni of KustoWorks, he perfected the query and have to mention him!
If you have any questions or suggestions for improvements, please reach out! I'm still learning and feedback is always welcome!
Comentários