Detection rules › Sublime MQL
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 |
Event coverage
Rule body MQL
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")
),
// 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')
- 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.is_forward, subject.is_reply, 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 (35)
| 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: |
attachments[].content_type | member | message/delivery-status |
attachments[].content_type | member | message/rfc822 |
strings.istarts_with | prefix | <Mimecast. |
strings.iends_with | suffix | .mimecast.lan> |
headers.hops[0].received.server.raw | equals | relay.mimecast.com |
strings.icontains | substring | mimecast.lan |
23 more
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 |
ml.nlu_classifier(body.current_thread.text).intents[].confidence | member | medium |
ml.nlu_classifier(body.current_thread.text).intents[].confidence | member | high |
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 |