Query Details

KQL Techniques For Email URL Redirect Hunting

Query

**[IC] - KQL Techniques for Email URL Redirect Hunting**

#### MITRE ATT&CK Technique(s)

| Technique ID | Title    |
| ---  | --- |
| T1566.002 | Phishing: Spearphishing Link  |


| Author | Sergio Albea (07/12/2025)   |
| ---  | --- |

**Description:** This KQL Query helps to understand all the URL Redirections from URLs received by Email using the UrlChainId identifier on KQL that groups together all URLs that belong to the same “click/redirect chain”.
When an message contains a URL, that URL may redirect through several intermediate URLs (trackers, shorteners, phishing, etc.) before land on the final site. KQL builds a URL chain from the original URL in the email through each redirect hop.
All URLs that are part of a single chain share the same UrlChainId and for each hop. On the other hand, UrlChainPosition register the position of every URL, the original one = value 0 and the other redirections tagged with next numbers(1, 2, etc. )
The following query includes different options to classify the insights into four categories:

- Tracking Domains 
- Redirecting to Different Countries
- Redirecting to Different Domains
- Number of Redirections 

```
// Sergio Albea 07/12/2025 ©️
let TrackingDomains = dynamic(["click/","bit.ly","bitly.com","tinyurl.com","goo.gl","t.co","ow.ly","is.gd","buff.ly","lnk.click","lnk.to","mjt.lu","mailjet.com","list-manage.com","mailchimp.com","mcsv.net","mailgun.org","sendgrid.net","sendgrid.com","createsend.com","createsend1.com","rs6.net","campaignsapp.com","mlsend.com","sendinblue.com","sibautomation.com","mailerlite.com","hs-sites.com","hscta.net","hubspot.com","hubspotlinks.com","mkto.com","mktg.com","pardot.com","eloqua.com","eloquaservice.com","activehosted.com","acems.ac","getvero.com","iterable.com","inflection.io","bnc.lt","app.link","substack.com","substackcdn.com","getrevue.co","shop.app","shopifyemail.com","klaviyo-mail.com","klaviyo.com","knowbe4.com","kb4.io","phishme.com","cofense.com"]);
EmailEvents
| where Timestamp > ago(1d)
| extend IPSender = iff(isnotempty( SenderIPv4),SenderIPv4,SenderIPv6)
| extend GeoIPData = tostring(geo_info_from_ip_address(IPSender).country)
| join kind=inner ( EmailUrlInfo  | where isnotempty(UrlChainId)) on NetworkMessageId
// Pivot URLs by position
| summarize
    UrlPos_0   = anyif(Url, UrlChainPosition == 0),UrlPos_1= anyif(Url, UrlChainPosition == 1),UrlPos_2= anyif(Url, UrlChainPosition == 2),UrlPos_3   = anyif(Url, UrlChainPosition == 3),
    IP_Url_0   = anyif(IPSender, UrlChainPosition == 0),IP_Url_1= anyif(IPSender, UrlChainPosition == 1),IP_Url_2= anyif(IPSender, UrlChainPosition == 2),IP_Url_3= anyif(IPSender, UrlChainPosition == 3),
    C_Url_0   = anyif(GeoIPData, UrlChainPosition == 0),C_Url_1= anyif(GeoIPData, UrlChainPosition == 1),C_Url_2= anyif(GeoIPData, UrlChainPosition == 2),C_Url_3= anyif(GeoIPData, UrlChainPosition == 3),
    URL_Redirections  = dcount(UrlChainPosition),Senders=dcount(SenderFromAddress),Recipients = dcount(RecipientEmailAddress),IP_Country= make_set(strcat(SenderIPv4, " ", GeoIPData)) by UrlChainId,ThreatTypes
| where isnotempty(UrlPos_0)
| extend Tracking_Marketing_URL0 = (iff(UrlPos_0 has_any (TrackingDomains),"Yes","Not")),Tracking_Marketing_URL1 =(iff(UrlPos_1 has_any (TrackingDomains),"Yes","Not")),Tracking_Marketing_URL2 = (iff(UrlPos_2 has_any (TrackingDomains),"Yes","Not")),Tracking_Marketing_URL3 = (iff(UrlPos_3 has_any (TrackingDomains),"Yes","Not"))
| summarize  by Senders,URL_Redirections,UrlPos_0,IP_Url_0,C_Url_0,Tracking_Marketing_URL0, UrlPos_1,IP_Url_1,C_Url_1,Tracking_Marketing_URL1, UrlPos_2,IP_Url_2,C_Url_2,Tracking_Marketing_URL2, UrlPos_3,IP_Url_3,C_Url_3,Tracking_Marketing_URL3,Recipients,tostring(IP_Country),UrlChainId,ThreatTypes
| extend Domain_URL0 = tostring(parse_url(UrlPos_0).Host), Domain_URL1 = tostring(parse_url(UrlPos_1).Host), Domain_URL2 = tostring(parse_url(UrlPos_2).Host), Domain_URL3 = tostring(parse_url(UrlPos_3).Host)
| extend Info="ℹ️",0_ = "0️⃣",1_ = "1️⃣",2_ = "2️⃣",3_ ="3️⃣" //Icons
//Search for specific URL | extend URL = "office" | where UrlPos_0 contains URL or UrlPos_1 contains URL or UrlPos_2 contains URL or UrlPos_3 contains URL
//Search for URL on distinct countries | where (C_Url_0 !has C_Url_1 or C_Url_0 !has C_Url_2 or C_Url_0 !has C_Url_3 or C_Url_1 !has C_Url_2 or C_Url_1 !has C_Url_3 or C_Url_2 !has C_Url_3) 
//Search for URL that contains specific countries | where   (C_Url_0  !in ("Switzerland","Germany")) or (C_Url_1 in ("Russia","Singapore"))
//Search for URL on distinct domains | where (Domain_URL0 !has Domain_URL1 or Domain_URL0 !has Domain_URL1 or Domain_URL0 !has Domain_URL3 or Domain_URL1 !has Domain_URL2 or Domain_URL1 !has Domain_URL3 or Domain_URL2 !has Domain_URL3) 
//Search for number of redirections | where URL_Redirections > 2
| project  Info,URL_Redirections,ThreatTypes,Senders, Recipients,0_,Domain_URL0,UrlPos_0, IP_Url_0,C_Url_0, Tracking_Marketing_URL0,1_,Domain_URL1,UrlPos_1,IP_Url_1,C_Url_1,Tracking_Marketing_URL1,2_,Domain_URL2,UrlPos_2,IP_Url_2,C_Url_2, Tracking_Marketing_URL2,3_,Domain_URL3,UrlPos_3,IP_Url_3,C_Url_3,Tracking_Marketing_URL3
| order by ThreatTypes
```

Explanation

This KQL (Kusto Query Language) script is designed to analyze email events to identify and understand URL redirections that occur when a user clicks on a link in an email. The query focuses on detecting potential phishing attempts and tracking activities by examining the chain of URLs that a single click might traverse.

Here's a simplified breakdown of what the query does:

  1. Data Collection: It starts by collecting email events from the last day, focusing on those that contain URLs with a UrlChainId. This identifier groups URLs that are part of the same redirection chain.

  2. URL Redirection Analysis: The query examines each URL in the redirection chain, noting their positions (e.g., original URL, first redirect, second redirect, etc.). It also captures the sender's IP address and geographical location.

  3. Classification: The URLs are classified into four categories:

    • Tracking Domains: Identifies if any URL in the chain is a known tracking or marketing domain (e.g., bit.ly, goo.gl).
    • Redirecting to Different Countries: Checks if the redirection chain involves URLs hosted in different countries.
    • Redirecting to Different Domains: Identifies if the URLs in the chain belong to different domains.
    • Number of Redirections: Counts how many redirections occur in the chain.
  4. Summarization: The query summarizes the data, providing insights into the sender, recipient, number of redirections, and whether any URLs are associated with tracking domains.

  5. Filtering Options: The script includes commented-out sections that allow users to filter results based on specific criteria, such as URLs containing certain keywords, redirections across different countries or domains, and chains with a specific number of redirections.

  6. Output: The final output is a table that presents the summarized information, including the domains and positions of URLs in the chain, sender and recipient details, and any identified threat types.

Overall, this query helps security analysts detect and investigate potential phishing attacks by analyzing the behavior of URLs in email communications.

Details

Sergio Albea profile picture

Sergio Albea

Released: December 8, 2025

Tables

EmailEventsEmailUrlInfo

Keywords

EmailUrlRedirectionsTrackingDomainsCountriesThreatTypesSendersRecipients

Operators

letdynamicagoiffisnotemptytostringgeo_info_from_ip_addressjoinsummarizeanyifdcountmake_setstrcathas_anyparse_urlprojectorder by

Actions