Detection rules › Sublime MQL
Sublime MQL rules: unrelated
| Rule | Severity |
|---|---|
| Sharepoint link likely unrelated to sender | medium |
Sharepoint link likely unrelated to sender
#- Severity
medium- Type
rule- Source
- github.com/sublime-security/sublime-rules
Detects when a sender links to a Sharepoint file where the subdomain significantly differs from the sender's domain. The rule checks for OneNote, PDF, or unknown file types and includes various domain validation checks.
Threat classification
Sublime's own taxonomy (not MITRE ATT&CK).
| Category | Values |
|---|---|
| Attack types | BEC/Fraud, Credential Phishing |
| Tactics and techniques | Impersonation: Employee, Lookalike domain, OneNote, PDF, Social engineering |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Sublime | Inbound email message |
Message attributes
bodybody.htmlbody.linksheadersheaders.auth_summarysender.emailtype
Rule body
type.inbound
and any(beta.ml_topic(body.html.display_text).topics,
.name == "File Sharing and Cloud Services" and .confidence == "high"
)
and 0 < length(body.links) < 10
and length(body.html.display_text) < 2000
and any(filter(body.links, .href_url.domain.root_domain == 'sharepoint.com'),
// Normalize Levenshtein distance by string length (0 = identical, 0.7+ = different)
// Working with what we have in MQL, considering we dont have max() or any other forms of string distancing
(
(
strings.iends_with(.href_url.domain.subdomain,
'-my'
) // common Sharepoint subdomain suffix
and (
(
strings.ilevenshtein(.href_url.domain.subdomain,
sender.email.domain.sld
) - 3 // subtract aforementioned suffix for more accurate calculation
) / (
(
(length(.href_url.domain.subdomain) - 3) + length(sender.email.domain.sld
)
+ (
(
(length(.href_url.domain.subdomain) - 3) - length(sender.email.domain.sld
)
) + (
length(sender.email.domain.sld) - (
length(.href_url.domain.subdomain) - 3
)
)
)
) / 2.0 // to ensure we keep the result as a float
)
) > 0.7 // customizable threshold
)
or (
not strings.iends_with(.href_url.domain.subdomain,
'-my'
) // no suffix, continue with original calculation
and (
strings.ilevenshtein(.href_url.domain.subdomain,
sender.email.domain.sld
) / (
(
length(.href_url.domain.subdomain) + length(sender.email.domain.sld
)
+ (
(
length(.href_url.domain.subdomain) - length(sender.email.domain.sld
)
) + (
length(sender.email.domain.sld) - length(.href_url.domain.subdomain
)
)
)
) / 2.0 // to ensure we keep the result as a float
)
) > 0.7 // customizable threshold
)
)
and not strings.icontains(.href_url.path, sender.email.local_part)
and not any($org_slds,
strings.icontains(..href_url.domain.subdomain, .)
)
// it is either a OneNote or PDF file, or unknown
and regex.icontains(.href_url.path, '\/:[obu]:\/(?:p|g\/personal)')
)
// a way to negate long threads
// the full thread must be less than 6 times the length of the current thread
and length(body.html.inner_text) < 6 * length(body.current_thread.text)
and sender.email.domain.root_domain not in (
"sharepoint.com",
"sharepointonline.com"
)
// negate highly trusted sender domains unless they fail DMARC authentication
and (
(
sender.email.domain.root_domain in $high_trust_sender_root_domains
and not headers.auth_summary.dmarc.pass
)
or sender.email.domain.root_domain not in $high_trust_sender_root_domains
)
Detection logic
Scope: inbound message.
Detects when a sender links to a Sharepoint file where the subdomain significantly differs from the sender's domain. The rule checks for OneNote, PDF, or unknown file types and includes various domain validation checks.
- inbound message
any of
beta.ml_topic(body.html.display_text).topicswhere all hold:- .name is 'File Sharing and Cloud Services'
- .confidence is 'high'
all of:
- length(body.links) > 0
- length(body.links) < 10
- length(body.html.display_text) < 2000
any of
filter(body.links)where all hold:any of:
all of:
- .href_url.domain.subdomain ends with '-my'
- strings.ilevenshtein(.href_url.domain.subdomain) - 3 / length(.href_url.domain.subdomain) - 3 + length(sender.email.domain.sld) + length(.href_url.domain.subdomain) - 3 - length(sender.email.domain.sld) + length(sender.email.domain.sld) - length(.href_url.domain.subdomain) - 3 / 2.0 > 0.7
all of:
not:
- .href_url.domain.subdomain ends with '-my'
- strings.ilevenshtein(.href_url.domain.subdomain) / length(.href_url.domain.subdomain) + length(sender.email.domain.sld) + length(.href_url.domain.subdomain) - length(sender.email.domain.sld) + length(sender.email.domain.sld) - length(.href_url.domain.subdomain) / 2.0 > 0.7
not:
- strings.icontains(.href_url.path)
not:
any of
$org_sldswhere:- strings.icontains(.href_url.domain.subdomain)
- .href_url.path matches '\\/:[obu]:\\/(?:p|g\\/personal)'
- length(body.html.inner_text) < 6 * length(body.current_thread.text)
- sender.email.domain.root_domain not in ('sharepoint.com', 'sharepointonline.com')
any of:
all of:
- sender.email.domain.root_domain in $high_trust_sender_root_domains
not:
- headers.auth_summary.dmarc.pass
- sender.email.domain.root_domain not in $high_trust_sender_root_domains
Inspects: body.html.display_text, body.html.inner_text, body.links, body.links[].href_url.domain.root_domain, headers.auth_summary.dmarc.pass, sender.email.domain.root_domain, sender.email.local_part, type.inbound. Sensors: beta.ml_topic, regex.icontains, strings.icontains, strings.iends_with. Reference lists: $high_trust_sender_root_domains, $org_slds.
Indicators matched (5)
| Field | Match | Value |
|---|---|---|
beta.ml_topic(body.html.display_text).topics[].name | equals | File Sharing and Cloud Services |
beta.ml_topic(body.html.display_text).topics[].confidence | equals | high |
body.links[].href_url.domain.root_domain | equals | sharepoint.com |
strings.iends_with | suffix | -my |
regex.icontains | regex | \/:[obu]:\/(?:p|g\/personal) |
Stages and Predicates
Stage 1: mql_rule
and
any(filter(body.links))
and
or
and
not
filter(body.links).href_url.domain.subdomain ends_with "-my"
macro "(strings.ilevenshtein(filter(body.links)[].href_url.domain.subdomain) / (((length(filter(body.links)[].href_url.domain.subdomain) + length(sender.email.domain.sld)) + ((length(filter(body.links)[].href_url.domain.subdomain) - length(sender.email.domain.sld)) + (length(sender.email.domain.sld) - length(filter(body.links)[].href_url.domain.subdomain)))) / 2.0)) > 0.7"
and
filter(body.links).href_url.domain.subdomain ends_with "-my"
macro "((strings.ilevenshtein(filter(body.links)[].href_url.domain.subdomain) - 3) / ((((length(filter(body.links)[].href_url.domain.subdomain) - 3) + length(sender.email.domain.sld)) + (((length(filter(body.links)[].href_url.domain.subdomain) - 3) - length(sender.email.domain.sld)) + (length(sender.email.domain.sld) - (length(filter(body.links)[].href_url.domain.subdomain) - 3)))) / 2.0)) > 0.7"
not
any($org_slds)
strings.icontains func_call "strings.icontains(filter(body.links)[].href_url.domain.subdomain)"
not
strings.icontains func_call "strings.icontains(filter(body.links)[].href_url.path)"
filter(body.links).href_url.path regex_match "\\/:[obu]:\\/(?:p|g\\/personal)"
or
and
not
headers.auth_summary.dmarc.pass eq "true"
macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
macro "sender.email.domain.root_domain not in high_trust_sender_root_domains"
any(beta.ml_topic(body.html.display_text).topics)
and
beta.ml_topic(body.html.display_text).topics.confidence eq "high"
beta.ml_topic(body.html.display_text).topics.name eq "File Sharing and Cloud Services"
not
sender.email.domain.root_domain in ["sharepoint.com", "sharepointonline.com"]
body.html.display_text length_compare "2000"
body.links length_compare "0"
body.links length_compare "10"
type.inbound eq "true"
macro "length(body.html.inner_text) < (6 * length(body.current_thread.text))"Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
sender.email.domain.root_domain | in | sharepoint.com, sharepointonline.com | excludes:sender.email.domain.root_domain field:"sender.email.domain.root_domain" value:"sharepoint.com" field:"sender.email.domain.root_domain" value:"sharepointonline.com" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
type.inbound | eq |
| field:"type.inbound" kind:eq value:"true" |