Detection rules › Sublime MQL
Sublime MQL rules: fake
Brand impersonation: Canada Revenue Agency
#Detects messages impersonating the Canada Revenue Agency (CRA) in English or French that contain credential theft indicators. The rule identifies senders claiming to be CRA through display names or subject line references, uses natural language understanding to detect credential theft intent, and excludes legitimate senders with proper authentication.
Threat classification
Sublime's own taxonomy (not MITRE ATT&CK).
| Category | Values |
|---|---|
| Attack types | BEC/Fraud, Credential Phishing |
| Tactics and techniques | Impersonation: Brand, Social engineering |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Sublime | Inbound email message |
Message attributes
Rule body
type.inbound
and (
// cred theft
any(ml.nlu_classifier(body.current_thread.text).intents,
.name == "cred_theft" and .confidence != 'low'
)
// contains a link not to the domain "canda.ca"
or any(body.current_thread.links,
.href_url.domain.root_domain not in ('canada.ca')
)
// contains any attachment that isn't an image
or any(attachments, .file_type not in $file_types_images)
)
// sender claims to be CRA
and (
strings.icontains(sender.display_name,
'canada revenue agency',
'agence du revenu du canada'
)
or (
regex.icontains(sender.display_name, '\bcra\b')
// limit it to samples that mention CRA by name in the body
and regex.icontains(body.current_thread.text,
'(?:canada revenue|revenu du canada)'
)
)
)
// not a high trust or cra-arc.gc.ca
and not (
(
// negate highly trusted sender domains
sender.email.domain.root_domain in $high_trust_sender_root_domains
// negate legit senders from legitimate cra
or sender.email.domain.root_domain == "cra-arc.gc.ca"
)
// enforce authentication
and coalesce(headers.auth_summary.dmarc.pass, false)
)
Detection logic
Scope: inbound message.
Detects messages impersonating the Canada Revenue Agency (CRA) in English or French that contain credential theft indicators. The rule identifies senders claiming to be CRA through display names or subject line references, uses natural language understanding to detect credential theft intent, and excludes legitimate senders with proper authentication.
- inbound message
any of:
any of
ml.nlu_classifier(body.current_thread.text).intentswhere all hold:- .name is 'cred_theft'
- .confidence is not 'low'
any of
body.current_thread.linkswhere:- .href_url.domain.root_domain not in ('canada.ca')
any of
attachmentswhere:- .file_type not in $file_types_images
any of:
sender.display_name contains any of 2 patterns
canada revenue agencyagence du revenu du canada
all of:
- sender.display_name matches '\\bcra\\b'
- body.current_thread.text matches '(?:canada revenue|revenu du canada)'
not:
all of:
any of:
- sender.email.domain.root_domain in $high_trust_sender_root_domains
- sender.email.domain.root_domain is 'cra-arc.gc.ca'
- coalesce(headers.auth_summary.dmarc.pass)
Inspects: attachments[].file_type, body.current_thread.links, body.current_thread.links[].href_url.domain.root_domain, body.current_thread.text, headers.auth_summary.dmarc.pass, sender.display_name, sender.email.domain.root_domain, type.inbound. Sensors: ml.nlu_classifier, regex.icontains, strings.icontains. Reference lists: $file_types_images, $high_trust_sender_root_domains.
Indicators matched (5)
| Field | Match | Value |
|---|---|---|
ml.nlu_classifier(body.current_thread.text).intents[].name | equals | cred_theft |
strings.icontains | substring | canada revenue agency |
strings.icontains | substring | agence du revenu du canada |
regex.icontains | regex | \bcra\b |
regex.icontains | regex | (?:canada revenue|revenu du canada) |
Stages and Predicates
Stage 1: mql_rule
and
or
any(body.current_thread.links)
not
body.current_thread.links.href_url.domain.root_domain eq "canada.ca"
any(ml.nlu_classifier(body.current_thread.text).intents)
and
ml.nlu_classifier(body.current_thread.text).intents.confidence ne "low"
ml.nlu_classifier(body.current_thread.text).intents.name eq "cred_theft"
any(attachments)
macro "attachments[].file_type not in file_types_images"
not
and
or
sender.email.domain.root_domain eq "cra-arc.gc.ca"
macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
coalesce func_call "coalesce(headers.auth_summary.dmarc.pass)"
or
and
body.current_thread.text regex_match "(?:canada revenue|revenu du canada)"
sender.display_name regex_match "\\bcra\\b"
sender.display_name contains "agence du revenu du canada"
sender.display_name contains "canada revenue agency"
type.inbound eq "true"Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
sender.email.domain.root_domain | eq | cra-arc.gc.ca | excludes:sender.email.domain.root_domain field:"sender.email.domain.root_domain" value:"cra-arc.gc.ca" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
body.current_thread.text | regex_match |
| field:"body.current_thread.text" kind:regex_match value:"(?:canada revenue|revenu du canada)" |
sender.display_name | contains |
| field:"sender.display_name" kind:contains |
sender.display_name | regex_match |
| field:"sender.display_name" kind:regex_match value:"\bcra\b" |
type.inbound | eq |
| field:"type.inbound" kind:eq value:"true" |
Fake request for tax preparation
#Unknown sender requesting assistance with tax preparation. This is associated with known threat actor activity, TA576.
Threat classification
Sublime's own taxonomy (not MITRE ATT&CK).
| Category | Values |
|---|---|
| Attack types | BEC/Fraud, Malware/Ransomware |
| Tactics and techniques | Social engineering |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Sublime | Inbound email message |
Message attributes
Rule body
type.inbound
and length(body.current_thread.text) < 1250
and any(beta.ml_topic(body.current_thread.text).topics,
.name == "Financial Communications"
)
// there are no links, all the links are to aka.ms, or an extraction from a warning banner that match the senders domain
and (
length(body.links) == 0
or length(filter(body.links,
(
.display_text is null
and .display_url.url == sender.email.domain.root_domain
)
or .href_url.domain.domain == "aka.ms"
or network.whois(.display_url.domain).days_old < 30
)
) == length(body.links)
)
and length(attachments) == 0
and (strings.ilike(subject.subject, "*tax*") or length(subject.subject) < 15)
and strings.icontains(body.current_thread.text, "tax")
and (
strings.like(body.current_thread.text,
"*return*",
"*record*",
"*CPA*",
"*filing*",
"*extension*"
)
or strings.ilike(body.current_thread.text,
"*tax preparer*",
"*tax*processing*"
)
)
and (
strings.ilike(body.current_thread.text,
"*necessary documents*",
"*required documents*",
"*paperwork*",
"*in search of*",
"*tax service*",
"*professional help*",
"*prepare*tax return*",
"*service*tax return*",
"*seeking*tax preparer*",
"*assist*processing*tax*",
"*schedule*call*",
"*zoom meeting*",
"*discuss*fees*",
"*W2*",
"*CPA*"
)
// suspicious patterns
or (
strings.icontains(body.current_thread.text, sender.display_name)
and 2 of (
(
length(headers.reply_to) > 0
and all(headers.reply_to,
.email.domain.root_domain != sender.email.domain.root_domain
)
),
(
headers.return_path.email is not null
and headers.return_path.email != sender.email.email
),
headers.return_path.domain.root_domain in ("amazonses.com")
)
)
)
and (
(
profile.by_sender().prevalence in ("new", "outlier")
and not profile.by_sender().solicited
)
or (
profile.by_sender().any_messages_malicious_or_spam
and not profile.by_sender().any_messages_benign
)
)
and not profile.by_sender().any_messages_benign
Detection logic
Scope: inbound message.
Unknown sender requesting assistance with tax preparation. This is associated with known threat actor activity, TA576.
- inbound message
- length(body.current_thread.text) < 1250
any of
beta.ml_topic(body.current_thread.text).topicswhere:- .name is 'Financial Communications'
any of:
- length(body.links) is 0
- length(filter(body.links, .display_text is null and .display_url.url == sender.email.domain.root_domain or .href_url.domain.domain == 'aka.ms' or network.whois(.display_url.domain).days_old < 30)) is length(body.links)
- length(attachments) is 0
any of:
- subject.subject matches '*tax*'
- length(subject.subject) < 15
- body.current_thread.text contains 'tax'
any of:
body.current_thread.text matches any of 5 patterns
*return**record**CPA**filing**extension*
body.current_thread.text matches any of 2 patterns
*tax preparer**tax*processing*
any of:
body.current_thread.text matches any of 15 patterns
*necessary documents**required documents**paperwork**in search of**tax service**professional help**prepare*tax return**service*tax return**seeking*tax preparer**assist*processing*tax**schedule*call**zoom meeting**discuss*fees**W2**CPA*
all of:
- strings.icontains(body.current_thread.text)
at least 2 of:
all of:
- length(headers.reply_to) > 0
all of
headers.reply_towhere:- .email.domain.root_domain is not sender.email.domain.root_domain
all of:
- headers.return_path.email is set
- headers.return_path.email is not sender.email.email
- headers.return_path.domain.root_domain in ('amazonses.com')
any of:
all of:
- profile.by_sender().prevalence in ('new', 'outlier')
not:
- profile.by_sender().solicited
all of:
- profile.by_sender().any_messages_malicious_or_spam
not:
- profile.by_sender().any_messages_benign
not:
- profile.by_sender().any_messages_benign
Inspects: body.current_thread.text, body.links, body.links[].display_text, body.links[].display_url.domain, body.links[].display_url.url, body.links[].href_url.domain.domain, headers.reply_to, headers.reply_to[].email.domain.root_domain, headers.return_path.domain.root_domain, headers.return_path.email, sender.display_name, sender.email.domain.root_domain, sender.email.email, subject.subject, type.inbound. Sensors: beta.ml_topic, network.whois, profile.by_sender, strings.icontains, strings.ilike, strings.like.
Indicators matched (27)
| Field | Match | Value |
|---|---|---|
beta.ml_topic(body.current_thread.text).topics[].name | equals | Financial Communications |
body.links[].href_url.domain.domain | equals | aka.ms |
strings.ilike | substring | *tax* |
strings.icontains | substring | tax |
strings.like | substring | *return* |
strings.like | substring | *record* |
strings.like | substring | *CPA* |
strings.like | substring | *filing* |
strings.like | substring | *extension* |
strings.ilike | substring | *tax preparer* |
strings.ilike | substring | *tax*processing* |
strings.ilike | substring | *necessary documents* |
15 more
strings.ilike | substring | *required documents* |
strings.ilike | substring | *paperwork* |
strings.ilike | substring | *in search of* |
strings.ilike | substring | *tax service* |
strings.ilike | substring | *professional help* |
strings.ilike | substring | *prepare*tax return* |
strings.ilike | substring | *service*tax return* |
strings.ilike | substring | *seeking*tax preparer* |
strings.ilike | substring | *assist*processing*tax* |
strings.ilike | substring | *schedule*call* |
strings.ilike | substring | *zoom meeting* |
strings.ilike | substring | *discuss*fees* |
strings.ilike | substring | *W2* |
strings.ilike | substring | *CPA* |
headers.return_path.domain.root_domain | member | amazonses.com |
Stages and Predicates
Stage 1: mql_rule
and
or
and
or
and
headers.reply_to length_compare "0"
macro "all(headers.reply_to)"
and
headers.return_path.email cross_field_compare "sender.email.email"
headers.return_path.email is_not_null
headers.return_path.domain.root_domain eq "amazonses.com"
strings.icontains func_call "strings.icontains(body.current_thread.text)"
body.current_thread.text match "CPA"
body.current_thread.text match "W2"
body.current_thread.text match "in search of"
body.current_thread.text match "necessary documents"
body.current_thread.text match "paperwork"
body.current_thread.text match "professional help"
body.current_thread.text match "required documents"
body.current_thread.text match "tax service"
body.current_thread.text match "zoom meeting"
body.current_thread.text wildcard "*assist*processing*tax*"
body.current_thread.text wildcard "*discuss*fees*"
body.current_thread.text wildcard "*prepare*tax return*"
body.current_thread.text wildcard "*schedule*call*"
body.current_thread.text wildcard "*seeking*tax preparer*"
body.current_thread.text wildcard "*service*tax return*"
or
and
not
profile.by_sender func_call "profile.by_sender().any_messages_benign"
profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
and
not
profile.by_sender func_call "profile.by_sender().solicited"
profile.by_sender func_call "profile.by_sender().prevalence in (new, outlier)"
any(beta.ml_topic(body.current_thread.text).topics)
beta.ml_topic(body.current_thread.text).topics.name eq "Financial Communications"
or
body.current_thread.text match "CPA"
body.current_thread.text match "extension"
body.current_thread.text match "filing"
body.current_thread.text match "record"
body.current_thread.text match "return"
body.current_thread.text match "tax preparer"
body.current_thread.text wildcard "*tax*processing*"
or
body.links length_compare "0"
length func_call "length(filter(body.links, .display_text is null and .display_url.url == sender.email.domain.root_domain or .href_url.domain.domain == 'aka.ms' or network.whois(.display_url.domain).days_old < 30)) == length(body.links)"
not
profile.by_sender func_call "profile.by_sender().any_messages_benign"
or
subject.subject length_compare "15"
subject.subject match "tax"
attachments length_compare "0"
body.current_thread.text contains "tax"
body.current_thread.text length_compare "1250"
type.inbound eq "true"Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
body.current_thread.text | contains |
| field:"body.current_thread.text" kind:contains value:"tax" |
body.current_thread.text | wildcard |
| field:"body.current_thread.text" kind:wildcard |
headers.return_path.domain.root_domain | in |
| field:"headers.return_path.domain.root_domain" kind:in value:"amazonses.com" |
headers.return_path.email | cross_field_compare |
| field:"headers.return_path.email" kind:cross_field_compare value:"sender.email.email" |
headers.return_path.email | is_not_null | field:"headers.return_path.email" kind:is_not_null | |
subject.subject | wildcard |
| field:"subject.subject" kind:wildcard value:"*tax*" |
type.inbound | eq |
| field:"type.inbound" kind:eq value:"true" |
Fake scan-to-email message
#Message resembles an email from a scan-to-email service or device, but does not contain any attachments, instead linking to an unknown domain.
Threat classification
Sublime's own taxonomy (not MITRE ATT&CK).
| Category | Values |
|---|---|
| Attack types | Credential Phishing |
| Tactics and techniques | Free file host, Social engineering |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Sublime | Inbound email message |
Message attributes
Rule body
type.inbound
and length(attachments) == 0
and 0 < length(body.links) < 3
and strings.ilike(subject.subject, "*message from*")
and (
(
strings.ilike(body.current_thread.text, "*scan date*", "*was sent from*")
and not strings.ilike(body.current_thread.text, "*unmonitored*")
)
or (
//
// This rule makes use of a beta feature and is subject to change without notice
// using the beta feature in custom rules is not suggested until it has been formally released
//
strings.ilike(beta.ocr(file.message_screenshot()).text,
"*scan date*",
"*was sent from*"
)
and not strings.ilike(beta.ocr(file.message_screenshot()).text,
"*unmonitored*"
)
)
)
and (
any(body.links, .href_url.domain.root_domain in~ $free_file_hosts)
or any(body.links, .display_url.url != .href_url.url)
or any(body.links, .display_url.url is null)
)
and all(body.links,
.href_url.domain.domain not in~ $org_domains
and .href_url.domain.domain not in ("aka.ms")
)
and sender.email.domain.domain not in~ $org_domains
and (
not profile.by_sender().solicited
or (
profile.by_sender().any_messages_malicious_or_spam
and not profile.by_sender().any_messages_benign
)
)
and not profile.by_sender().any_messages_benign
Detection logic
Scope: inbound message.
Message resembles an email from a scan-to-email service or device, but does not contain any attachments, instead linking to an unknown domain.
- inbound message
- length(attachments) is 0
all of:
- length(body.links) > 0
- length(body.links) < 3
- subject.subject matches '*message from*'
any of:
all of:
body.current_thread.text matches any of 2 patterns
*scan date**was sent from*
not:
- body.current_thread.text matches '*unmonitored*'
all of:
beta.ocr(file.message_screenshot()).text matches any of 2 patterns
*scan date**was sent from*
not:
- beta.ocr(file.message_screenshot()).text matches '*unmonitored*'
any of:
any of
body.linkswhere:- .href_url.domain.root_domain in $free_file_hosts
any of
body.linkswhere:- .display_url.url is not .href_url.url
any of
body.linkswhere:- .display_url.url is missing
all of
body.linkswhere all hold:- .href_url.domain.domain not in $org_domains
- .href_url.domain.domain not in ('aka.ms')
- sender.email.domain.domain not in $org_domains
any of:
not:
- profile.by_sender().solicited
all of:
- profile.by_sender().any_messages_malicious_or_spam
not:
- profile.by_sender().any_messages_benign
not:
- profile.by_sender().any_messages_benign
Inspects: body.current_thread.text, body.links, body.links[].display_url.url, body.links[].href_url.domain.domain, body.links[].href_url.domain.root_domain, body.links[].href_url.url, sender.email.domain.domain, subject.subject, type.inbound. Sensors: beta.ocr, file.message_screenshot, profile.by_sender, strings.ilike. Reference lists: $free_file_hosts, $org_domains.
Indicators matched (3)
| Field | Match | Value |
|---|---|---|
strings.ilike | substring | *message from* |
strings.ilike | substring | *scan date* |
strings.ilike | substring | *was sent from* |
Stages and Predicates
Stage 1: mql_rule
and
or
and
or
beta.ocr(file.message_screenshot()).text match "scan date"
beta.ocr(file.message_screenshot()).text match "was sent from"
not
beta.ocr(file.message_screenshot()).text match "unmonitored"
and
or
body.current_thread.text match "scan date"
body.current_thread.text match "was sent from"
not
body.current_thread.text match "unmonitored"
or
and
not
profile.by_sender func_call "profile.by_sender().any_messages_benign"
profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
not
profile.by_sender func_call "profile.by_sender().solicited"
or
any(body.links)
body.links.display_url.url cross_field_compare "body.links.href_url.url"
any(body.links)
body.links.display_url.url is_null
any(body.links)
macro "body.links[].href_url.domain.root_domain in free_file_hosts"
not
profile.by_sender func_call "profile.by_sender().any_messages_benign"
attachments length_compare "0"
body.links length_compare "0"
body.links length_compare "3"
subject.subject match "message from"
type.inbound eq "true"
macro "all(body.links)"
macro "sender.email.domain.domain not in org_domains"Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
beta.ocr(file.message_screenshot()).text | wildcard |
| field:"beta.ocr(file.message_screenshot()).text" kind:wildcard |
body.current_thread.text | wildcard |
| field:"body.current_thread.text" kind:wildcard |
subject.subject | wildcard |
| field:"subject.subject" kind:wildcard value:"*message from*" |
type.inbound | eq |
| field:"type.inbound" kind:eq value:"true" |
Fake thread with suspicious indicators
#Fake thread contains suspicious indicators, which can lead to BEC, credential phishing, and other undesirable outcomes.
Threat classification
Sublime's own taxonomy (not MITRE ATT&CK).
| Category | Values |
|---|---|
| Attack types | BEC/Fraud, Credential Phishing, Spam |
| Tactics and techniques | Evasion, Social engineering |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Sublime | Inbound email message |
Message attributes
Rule body
type.inbound
// fake thread check
and (length(headers.references) == 0 or headers.in_reply_to is null)
and (
subject.is_reply
or subject.is_forward
// fake thread, but no indication in the subject line
// current_thread pulls the recent thread, but the full body contains the fake "original" email
or (
not (subject.is_reply or subject.is_forward)
and any([body.current_thread.text, body.html.display_text, body.plain.raw],
3 of (
strings.icontains(., "from:"),
strings.icontains(., "to:"),
strings.icontains(., "sent:"),
strings.icontains(., "date:"),
strings.icontains(., "cc:"),
strings.icontains(., "subject:")
)
)
and length(body.current_thread.text) + 100 < length(coalesce(body.html.display_text,
body.plain.raw
)
)
)
)
// negating bouncebacks
and not any(attachments,
.content_type in ("message/delivery-status", "message/rfc822")
)
// negating Google Calendar invites
and (
(
headers.return_path.domain.domain is not null
and headers.return_path.domain.domain != 'calendar-server.bounces.google.com'
)
or headers.return_path.domain.domain is null
)
// not mimecast secure message from internal source
and not (
strings.istarts_with(headers.message_id, '<Mimecast.')
and strings.iends_with(headers.message_id, '.mimecast.lan>')
and headers.hops[0].received.server.raw == "relay.mimecast.com"
and strings.icontains(headers.hops[0].received.source.raw, 'mimecast.lan')
)
// and not solicited
and not profile.by_sender().solicited
and 4 of (
// language attempting to engage
(
any(ml.nlu_classifier(body.current_thread.text).entities,
.name == "request"
)
and any(ml.nlu_classifier(body.current_thread.text).entities,
.name == "financial"
)
),
// invoicing language
(
any(ml.nlu_classifier(body.current_thread.text).tags, .name == "invoice")
or any(ml.nlu_classifier(body.current_thread.text).entities,
.text == "invoice"
)
),
// urgency request
any(ml.nlu_classifier(body.current_thread.text).entities, .name == "urgency"),
// cred_theft detection
any(ml.nlu_classifier(body.current_thread.text).intents,
.name == "cred_theft" and .confidence in~ ("medium", "high")
),
// subject has multiple re: tags and suspicious indicators
(
regex.icontains(subject.subject, '\bre:\s+re:')
and (
regex.icontains(subject.base,
'(?:action.{0,10}required|invest(?:ment|ing)?|enforcement|opportunit|urgent|verif|confirm|suspend|terminat)'
)
)
),
// commonly abused sender TLD
strings.ilike(sender.email.domain.tld, "*.jp"),
// headers traverse abused TLD
any(headers.domains, strings.ilike(.tld, "*.jp")),
// known suspicious pattern in the URL path
any(body.links, regex.match(.href_url.path, '\/[a-z]{3}\d[a-z]')),
// link display text is in all caps
any(body.links, regex.match(.display_text, '[A-Z ]+')),
// link display text contains invisible characters (U+200F)
any(body.links, strings.contains(.display_text, "\u{200F}")),
// Low reputation link with display text ending in a document extension
any(body.links,
.href_url.domain.root_domain not in $tranco_1m
and .href_url.domain.valid
and .href_url.domain.root_domain not in $org_domains
and .href_url.domain.root_domain not in $high_trust_sender_root_domains
and (
any($file_extensions_macros, strings.ends_with(..display_text, .))
or strings.ends_with(.display_text, 'pdf')
)
),
// display name contains an email
regex.contains(sender.display_name, '[a-z0-9]+@[a-z]+'),
// Sender domain is empty
sender.email.domain.domain == "",
// sender domain matches no body domains
all(body.links,
.href_url.domain.root_domain != sender.email.domain.root_domain
),
// body contains name of VIP
(
any($org_vips, strings.icontains(body.html.inner_text, .display_name))
or any($org_vips, strings.icontains(body.plain.raw, .display_name))
),
// new body domain
any(body.links, network.whois(.href_url.domain).days_old < 30),
// new sender domain
network.whois(sender.email.domain).days_old < 30,
// new sender
profile.by_sender().days_known < 7,
// excessive whitespace
(
regex.icontains(body.html.raw, '((<br\s*/?>\s*){20,}|\n{20,})')
or regex.icontains(body.html.raw, '(<p[^>]*>\s*<br\s*/?>\s*</p>\s*){30,}')
or regex.icontains(body.html.raw,
'(<p class=".*?"><span style=".*?"><o:p>&nbsp;</o:p></span></p>\s*){30,}'
)
or regex.icontains(body.html.raw, '(<p>&nbsp;</p>\s*){7,}')
or regex.icontains(body.html.raw, '(<p>&nbsp;</p><br>\s*){7,}')
or regex.icontains(body.html.raw, '(<p[^>]*>\s*&nbsp;<br>\s*</p>\s*){5,}')
or regex.icontains(body.html.raw, '(<p[^>]*>&nbsp;</p>\s*){7,}')
),
// body contains recipient SLD
any(recipients.to,
strings.icontains(body.current_thread.text, .email.domain.sld)
),
(
// bec
any(ml.nlu_classifier(body.current_thread.text).intents,
.name == "bec" and .confidence != "low"
)
// previous thread contains matching domain but mismatch local part in current thread
and any(body.previous_threads,
.sender.email.domain.root_domain == recipients.to[0].email.domain.root_domain
and .sender.email.email != recipients.to[0].email.email
)
)
)
// 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
)
and not profile.by_sender().any_messages_benign
Detection logic
Scope: inbound message.
Fake thread contains suspicious indicators, which can lead to BEC, credential phishing, and other undesirable outcomes.
- inbound message
any of:
- length(headers.references) is 0
- headers.in_reply_to is missing
any of:
- subject.is_reply
- subject.is_forward
all of:
none of:
- subject.is_reply
- subject.is_forward
any of
[body.current_thread.text, body.html.display_text, body.plain.raw]where:at least 3 of 6: . contains any of 6 patterns
from:to:sent:date:cc:subject:
- length(body.current_thread.text) + 100 < length(coalesce(body.html.display_text, body.plain.raw))
not:
any of
attachmentswhere:- .content_type in ('message/delivery-status', 'message/rfc822')
any of:
all of:
- headers.return_path.domain.domain is set
- headers.return_path.domain.domain is not 'calendar-server.bounces.google.com'
- headers.return_path.domain.domain is missing
not:
all of:
- headers.message_id starts with '<Mimecast.'
- headers.message_id ends with '.mimecast.lan>'
- headers.hops[0].received.server.raw is 'relay.mimecast.com'
- headers.hops[0].received.source.raw contains 'mimecast.lan'
not:
- profile.by_sender().solicited
at least 4 of:
all of:
any of
ml.nlu_classifier(body.current_thread.text).entitieswhere:- .name is 'request'
any of
ml.nlu_classifier(body.current_thread.text).entitieswhere:- .name is 'financial'
any of:
any of
ml.nlu_classifier(body.current_thread.text).tagswhere:- .name is 'invoice'
any of
ml.nlu_classifier(body.current_thread.text).entitieswhere:- .text is 'invoice'
any of
ml.nlu_classifier(body.current_thread.text).entitieswhere:- .name is 'urgency'
any of
ml.nlu_classifier(body.current_thread.text).intentswhere all hold:- .name is 'cred_theft'
- .confidence in ('medium', 'high')
all of:
- subject.subject matches '\\bre:\\s+re:'
- subject.base matches '(?:action.{0,10}required|invest(?:ment|ing)?|enforcement|opportunit|urgent|verif|confirm|suspend|terminat)'
- sender.email.domain.tld matches '*.jp'
any of
headers.domainswhere:- .tld matches '*.jp'
any of
body.linkswhere:- .href_url.path matches '\\/[a-z]{3}\\d[a-z]'
any of
body.linkswhere:- .display_text matches '[A-Z ]+'
any of
body.linkswhere:- .display_text contains '\\u{200F}'
any of
body.linkswhere all hold:- .href_url.domain.root_domain not in $tranco_1m
- .href_url.domain.valid
- .href_url.domain.root_domain not in $org_domains
- .href_url.domain.root_domain not in $high_trust_sender_root_domains
any of:
any of
$file_extensions_macroswhere:- strings.ends_with(.display_text)
- .display_text ends with 'pdf'
- sender.display_name matches '[a-z0-9]+@[a-z]+'
- sender.email.domain.domain is ''
all of
body.linkswhere:- .href_url.domain.root_domain is not sender.email.domain.root_domain
any of:
any of
$org_vipswhere:- strings.icontains(body.html.inner_text)
any of
$org_vipswhere:- strings.icontains(body.plain.raw)
any of
body.linkswhere:- network.whois(.href_url.domain).days_old < 30
- network.whois(sender.email.domain).days_old < 30
- profile.by_sender().days_known < 7
body.html.raw matches any of 7 patterns
((<br\s*/?>\s*){20,}|\n{20,})(<p[^>]*>\s*<br\s*/?>\s*</p>\s*){30,}(<p class=".*?"><span style=".*?"><o:p>&nbsp;</o:p></span></p>\s*){30,}(<p>&nbsp;</p>\s*){7,}(<p>&nbsp;</p><br>\s*){7,}(<p[^>]*>\s*&nbsp;<br>\s*</p>\s*){5,}(<p[^>]*>&nbsp;</p>\s*){7,}
any of
recipients.towhere:- strings.icontains(body.current_thread.text)
all of:
any of
ml.nlu_classifier(body.current_thread.text).intentswhere all hold:- .name is 'bec'
- .confidence is not 'low'
any of
body.previous_threadswhere all hold:- .sender.email.domain.root_domain is recipients.to[0].email.domain.root_domain
- .sender.email.email is not recipients.to[0].email.email
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
not:
- profile.by_sender().any_messages_benign
Inspects: attachments[].content_type, body.current_thread.text, body.html.display_text, body.html.inner_text, body.html.raw, body.links, body.links[].display_text, body.links[].href_url.domain, body.links[].href_url.domain.root_domain, body.links[].href_url.domain.valid, body.links[].href_url.path, body.plain.raw, body.previous_threads, body.previous_threads[].sender.email.domain.root_domain, body.previous_threads[].sender.email.email, headers.auth_summary.dmarc.pass, headers.domains, headers.domains[].tld, headers.hops[0].received.server.raw, headers.hops[0].received.source.raw, headers.in_reply_to, headers.message_id, headers.references, headers.return_path.domain.domain, recipients.to, recipients.to[0].email.domain.root_domain, recipients.to[0].email.email, recipients.to[].email.domain.sld, sender.display_name, sender.email.domain, sender.email.domain.domain, sender.email.domain.root_domain, sender.email.domain.tld, subject.base, subject.is_forward, subject.is_reply, subject.subject, type.inbound. Sensors: ml.nlu_classifier, network.whois, profile.by_sender, regex.contains, regex.icontains, regex.match, strings.contains, strings.ends_with, strings.icontains, strings.iends_with, strings.ilike, strings.istarts_with. Reference lists: $file_extensions_macros, $high_trust_sender_root_domains, $org_domains, $org_vips, $tranco_1m.
Indicators matched (31)
| Field | Match | Value |
|---|---|---|
strings.icontains | substring | from: |
strings.icontains | substring | to: |
strings.icontains | substring | sent: |
strings.icontains | substring | date: |
strings.icontains | substring | cc: |
strings.icontains | substring | subject: |
ml.nlu_classifier(body.current_thread.text).entities[].name | equals | request |
ml.nlu_classifier(body.current_thread.text).entities[].name | equals | financial |
ml.nlu_classifier(body.current_thread.text).tags[].name | equals | invoice |
ml.nlu_classifier(body.current_thread.text).entities[].text | equals | invoice |
ml.nlu_classifier(body.current_thread.text).entities[].name | equals | urgency |
ml.nlu_classifier(body.current_thread.text).intents[].name | equals | cred_theft |
19 more
ml.nlu_classifier(body.current_thread.text).intents[].confidence | member | medium |
ml.nlu_classifier(body.current_thread.text).intents[].confidence | member | high |
regex.icontains | regex | \bre:\s+re: |
regex.icontains | regex | (?:action.{0,10}required|invest(?:ment|ing)?|enforcement|opportunit|urgent|verif|confirm|suspend|terminat) |
strings.ilike | substring | *.jp |
regex.match | regex | \/[a-z]{3}\d[a-z] |
regex.match | regex | [A-Z ]+ |
strings.contains | substring | \u{200F} |
strings.ends_with | suffix | pdf |
regex.contains | regex | [a-z0-9]+@[a-z]+ |
sender.email.domain.domain | equals | |
regex.icontains | regex | ((<br\s*/?>\s*){20,}|\n{20,}) |
regex.icontains | regex | (<p[^>]*>\s*<br\s*/?>\s*</p>\s*){30,} |
regex.icontains | regex | (<p class=".*?"><span style=".*?"><o:p>&nbsp;</o:p></span></p>\s*){30,} |
regex.icontains | regex | (<p>&nbsp;</p>\s*){7,} |
regex.icontains | regex | (<p>&nbsp;</p><br>\s*){7,} |
regex.icontains | regex | (<p[^>]*>\s*&nbsp;<br>\s*</p>\s*){5,} |
regex.icontains | regex | (<p[^>]*>&nbsp;</p>\s*){7,} |
ml.nlu_classifier(body.current_thread.text).intents[].name | equals | bec |
Stages and Predicates
Stage 1: mql_rule
and
or
any(body.links)
and
or
any($file_extensions_macros)
strings.ends_with func_call "strings.ends_with(body.links[].display_text)"
body.links.display_text ends_with "pdf"
body.links.href_url.domain.valid eq "true"
macro "body.links[].href_url.domain.root_domain not in high_trust_sender_root_domains"
macro "body.links[].href_url.domain.root_domain not in org_domains"
macro "body.links[].href_url.domain.root_domain not in tranco_1m"
and
any(body.previous_threads)
and
body.previous_threads.sender.email.domain.root_domain cross_field_compare "recipients.to[0].email.domain.root_domain"
body.previous_threads.sender.email.email cross_field_compare "recipients.to[0].email.email"
any(ml.nlu_classifier(body.current_thread.text).intents)
and
ml.nlu_classifier(body.current_thread.text).intents.confidence ne "low"
ml.nlu_classifier(body.current_thread.text).intents.name eq "bec"
and
any(ml.nlu_classifier(body.current_thread.text).entities)
ml.nlu_classifier(body.current_thread.text).entities.name eq "financial"
any(ml.nlu_classifier(body.current_thread.text).entities)
ml.nlu_classifier(body.current_thread.text).entities.name eq "request"
any(ml.nlu_classifier(body.current_thread.text).intents)
and
ml.nlu_classifier(body.current_thread.text).intents.confidence in ["high", "medium"]
ml.nlu_classifier(body.current_thread.text).intents.name eq "cred_theft"
any(body.links)
body.links.display_text contains "\\u{200F}"
any(body.links)
body.links.display_text regex_match "[A-Z ]+"
any(body.links)
body.links.href_url.path regex_match "\\/[a-z]{3}\\d[a-z]"
any(headers.domains)
headers.domains.tld ends_with ".jp"
any(ml.nlu_classifier(body.current_thread.text).entities)
ml.nlu_classifier(body.current_thread.text).entities.name eq "urgency"
any(ml.nlu_classifier(body.current_thread.text).entities)
ml.nlu_classifier(body.current_thread.text).entities.text eq "invoice"
any(ml.nlu_classifier(body.current_thread.text).tags)
ml.nlu_classifier(body.current_thread.text).tags.name eq "invoice"
any(body.links)
network.whois func_call "network.whois(body.links[].href_url.domain).days_old < 30"
any(recipients.to)
strings.icontains func_call "strings.icontains(body.current_thread.text)"
any($org_vips)
strings.icontains func_call "strings.icontains(body.html.inner_text)"
any($org_vips)
strings.icontains func_call "strings.icontains(body.plain.raw)"
and
subject.base regex_match "(?:action.{0,10}required|invest(?:ment|ing)?|enforcement|opportunit|urgent|verif|confirm|suspend|terminat)"
subject.subject regex_match "\\bre:\\s+re:"
body.html.raw regex_match "((<br\\s*/?>\\s*){20,}|\\n{20,})"
body.html.raw regex_match "(<p class=\".*?\"><span style=\".*?\"><o:p>&nbsp;</o:p></span></p>\\s*){30,}"
body.html.raw regex_match "(<p>&nbsp;</p><br>\\s*){7,}"
body.html.raw regex_match "(<p>&nbsp;</p>\\s*){7,}"
body.html.raw regex_match "(<p[^>]*>&nbsp;</p>\\s*){7,}"
body.html.raw regex_match "(<p[^>]*>\\s*&nbsp;<br>\\s*</p>\\s*){5,}"
body.html.raw regex_match "(<p[^>]*>\\s*<br\\s*/?>\\s*</p>\\s*){30,}"
network.whois func_call "network.whois(sender.email.domain).days_old < 30"
profile.by_sender func_call "profile.by_sender().days_known < 7"
sender.display_name regex_match "[a-z0-9]+@[a-z]+"
sender.email.domain.domain eq ""
sender.email.domain.tld ends_with ".jp"
macro "all(body.links)"
or
and
any([body.current_thread.text, body.html.display_text, body.plain.raw])
or
[body.current_thread.text, body.html.display_text, body.plain.raw] contains "cc:"
[body.current_thread.text, body.html.display_text, body.plain.raw] contains "date:"
[body.current_thread.text, body.html.display_text, body.plain.raw] contains "from:"
[body.current_thread.text, body.html.display_text, body.plain.raw] contains "sent:"
[body.current_thread.text, body.html.display_text, body.plain.raw] contains "subject:"
[body.current_thread.text, body.html.display_text, body.plain.raw] contains "to:"
not
or
subject.is_forward eq "true"
subject.is_reply eq "true"
macro "(length(body.current_thread.text) + 100) < length(coalesce(body.html.display_text, body.plain.raw))"
subject.is_forward eq "true"
subject.is_reply eq "true"
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"
not
any(attachments)
attachments.content_type in ["message/delivery-status", "message/rfc822"]
not
and
headers.hops[0].received.server.raw eq "relay.mimecast.com"
headers.hops[0].received.source.raw contains "mimecast.lan"
headers.message_id ends_with ".mimecast.lan>"
headers.message_id starts_with "<Mimecast."
or
and
headers.return_path.domain.domain is_not_null
headers.return_path.domain.domain ne "calendar-server.bounces.google.com"
headers.return_path.domain.domain is_null
or
headers.in_reply_to is_null
headers.references length_compare "0"
not
profile.by_sender func_call "profile.by_sender().any_messages_benign"
not
profile.by_sender func_call "profile.by_sender().solicited"
type.inbound eq "true"Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
attachments | array_any | excludes:attachments | |
headers.hops[0].received.server.raw | eq | relay.mimecast.com | excludes:headers.hops[0].received.server.raw field:"headers.hops[0].received.server.raw" value:"relay.mimecast.com" |
headers.hops[0].received.source.raw | contains | mimecast.lan | excludes:headers.hops[0].received.source.raw field:"headers.hops[0].received.source.raw" value:"mimecast.lan" |
headers.message_id | ends_with | .mimecast.lan> | excludes:headers.message_id field:"headers.message_id" value:".mimecast.lan>" |
headers.message_id | starts_with | <Mimecast. | excludes:headers.message_id field:"headers.message_id" value:"<Mimecast." |
Indicators
These rows show field, operator, and value matches.
Impersonation: Fake product discount promotion
#Detects messages containing fake product discount offers that leads to a googleapis.com domain.
Threat classification
Sublime's own taxonomy (not MITRE ATT&CK).
| Category | Values |
|---|---|
| Attack types | BEC/Fraud |
| Tactics and techniques | Social engineering, Free file host |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Sublime | Inbound email message |
Message attributes
Rule body
type.inbound
and strings.icontains(body.current_thread.text,
"hi {email}",
"participation is voluntary",
"limit one discount",
"limited time offer",
"code",
"survey"
)
and (
regex.icontains(body.current_thread.text, 'claim \d+% off')
or regex.icontains(body.current_thread.text, '\d+ question')
)
and any(body.current_thread.links,
.href_url.domain.root_domain == "googleapis.com"
)
and any(ml.nlu_classifier(body.current_thread.text).topics,
.name in ("Advertising and Promotions")
)
// negate highly trusted sender domains unless they fail DMARC authentication
and not (
sender.email.domain.root_domain in $high_trust_sender_root_domains
and coalesce(headers.auth_summary.dmarc.pass, false)
)
Detection logic
Scope: inbound message.
Detects messages containing fake product discount offers that leads to a googleapis.com domain.
- inbound message
body.current_thread.text contains any of 6 patterns
hi {email}participation is voluntarylimit one discountlimited time offercodesurvey
any of:
- body.current_thread.text matches 'claim \\d+% off'
- body.current_thread.text matches '\\d+ question'
any of
body.current_thread.linkswhere:- .href_url.domain.root_domain is 'googleapis.com'
any of
ml.nlu_classifier(body.current_thread.text).topicswhere:- .name in ('Advertising and Promotions')
not:
all of:
- sender.email.domain.root_domain in $high_trust_sender_root_domains
- coalesce(headers.auth_summary.dmarc.pass)
Inspects: body.current_thread.links, body.current_thread.links[].href_url.domain.root_domain, body.current_thread.text, headers.auth_summary.dmarc.pass, sender.email.domain.root_domain, type.inbound. Sensors: ml.nlu_classifier, regex.icontains, strings.icontains. Reference lists: $high_trust_sender_root_domains.
Indicators matched (10)
| Field | Match | Value |
|---|---|---|
strings.icontains | substring | hi {email} |
strings.icontains | substring | participation is voluntary |
strings.icontains | substring | limit one discount |
strings.icontains | substring | limited time offer |
strings.icontains | substring | code |
strings.icontains | substring | survey |
regex.icontains | regex | claim \d+% off |
regex.icontains | regex | \d+ question |
body.current_thread.links[].href_url.domain.root_domain | equals | googleapis.com |
ml.nlu_classifier(body.current_thread.text).topics[].name | member | Advertising and Promotions |
Stages and Predicates
Stage 1: mql_rule
and
not
and
coalesce func_call "coalesce(headers.auth_summary.dmarc.pass)"
macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
any(body.current_thread.links)
body.current_thread.links.href_url.domain.root_domain eq "googleapis.com"
or
body.current_thread.text contains "code"
body.current_thread.text contains "hi {email}"
body.current_thread.text contains "limit one discount"
body.current_thread.text contains "limited time offer"
body.current_thread.text contains "participation is voluntary"
body.current_thread.text contains "survey"
or
body.current_thread.text regex_match "\\d+ question"
body.current_thread.text regex_match "claim \\d+% off"
any(ml.nlu_classifier(body.current_thread.text).topics)
ml.nlu_classifier(body.current_thread.text).topics.name eq "Advertising and Promotions"
type.inbound eq "true"Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
body.current_thread.text | contains |
| field:"body.current_thread.text" kind:contains |
body.current_thread.text | regex_match |
| field:"body.current_thread.text" kind:regex_match |
type.inbound | eq |
| field:"type.inbound" kind:eq value:"true" |