Detection rules › Sublime MQL
Sublime MQL rules: francais
Business Email Compromise (BEC) attempt from untrusted sender (French/Français)
#Detects potential Business Email Compromise (BEC) attacks by searching for common French BEC language within the email body from first-time senders.
Threat classification
Sublime's own taxonomy (not MITRE ATT&CK).
| Category | Values |
|---|---|
| Attack types | BEC/Fraud |
| Tactics and techniques | Social engineering |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Sublime | Inbound email message |
Message attributes
Rule body
type.inbound
and length(body.links) == 0
and ml.nlu_classifier(body.current_thread.text).language == "french"
and 1 of (
regex.icontains(subject.subject,
'(mise (a|à) jour|changé|changement).{0,20}(bancaire|de banque)'
),
regex.icontains(body.current_thread.text,
'(changement|changé) de (banque)|changement bancaire|coordonnées.{0,20}(compte|banque|bancaire|salaire)',
),
(regex.icontains(body.current_thread.text, 'parler.{0,20}confiance'))
)
// negating legit replies
and not (
(
strings.istarts_with(subject.subject, "RE:")
// out of office auto-reply
// the NLU model will handle these better natively soon
or strings.istarts_with(subject.subject, "Automatic reply:")
or regex.imatch(subject.subject,
'(\[[^\]]+\]\s?){0,3}(re|fwd?|automat.*)\s?:.*'
)
)
and (length(headers.references) > 0 or headers.in_reply_to is not null)
)
and (
not profile.by_sender().solicited
or (
profile.by_sender().any_messages_malicious_or_spam
and not profile.by_sender().any_messages_benign
)
)
// 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 potential Business Email Compromise (BEC) attacks by searching for common French BEC language within the email body from first-time senders.
- inbound message
- length(body.links) is 0
- ml.nlu_classifier(body.current_thread.text).language is 'french'
at least 1 of:
- subject.subject matches '(mise (a|à) jour|changé|changement).{0,20}(bancaire|de banque)'
- body.current_thread.text matches '(changement|changé) de (banque)|changement bancaire|coordonnées.{0,20}(compte|banque|bancaire|salaire)'
- body.current_thread.text matches 'parler.{0,20}confiance'
not:
all of:
any of:
- subject.subject starts with 'RE:'
- subject.subject starts with 'Automatic reply:'
- subject.subject matches '(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?|automat.*)\\s?:.*'
any of:
- length(headers.references) > 0
- headers.in_reply_to is set
any of:
not:
- profile.by_sender().solicited
all of:
- profile.by_sender().any_messages_malicious_or_spam
not:
- profile.by_sender().any_messages_benign
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.current_thread.text, body.links, headers.auth_summary.dmarc.pass, headers.in_reply_to, headers.references, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, regex.icontains, regex.imatch, strings.istarts_with. Reference lists: $high_trust_sender_root_domains.
Indicators matched (3)
| Field | Match | Value |
|---|---|---|
regex.icontains | regex | (mise (a|à) jour|changé|changement).{0,20}(bancaire|de banque) |
regex.icontains | regex | (changement|changé) de (banque)|changement bancaire|coordonnées.{0,20}(compte|banque|bancaire|salaire) |
regex.icontains | regex | parler.{0,20}confiance |
Stages and Predicates
Stage 1: mql_rule
and
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
and
or
headers.in_reply_to is_not_null
headers.references length_compare "0"
or
subject.subject regex_match "(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?|automat.*)\\s?:.*"
subject.subject starts_with "Automatic reply:"
subject.subject starts_with "RE:"
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
body.current_thread.text regex_match "(changement|changé) de (banque)|changement bancaire|coordonnées.{0,20}(compte|banque|bancaire|salaire)"
body.current_thread.text regex_match "parler.{0,20}confiance"
subject.subject regex_match "(mise (a|à) jour|changé|changement).{0,20}(bancaire|de banque)"
body.links length_compare "0"
ml.nlu_classifier func_call "ml.nlu_classifier(body.current_thread.text).language == french"
type.inbound eq "true"Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
headers.in_reply_to | is_not_null | excludes:headers.in_reply_to | |
headers.references | length_compare | 0 | excludes:headers.references field:"headers.references" value:"0" |
subject.subject | regex_match | ([[^]]+]\s?){0,3}(re|fwd?|automat.*)\s?:.* | excludes:subject.subject field:"subject.subject" value:"([[^]]+]\s?){0,3}(re|fwd?|automat.*)\s?:.*" |
subject.subject | starts_with | Automatic reply: | excludes:subject.subject field:"subject.subject" value:"Automatic reply:" |
subject.subject | starts_with | RE: | excludes:subject.subject field:"subject.subject" value:"RE:" |
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 |
subject.subject | regex_match |
| field:"subject.subject" kind:regex_match value:"(mise (a|à) jour|changé|changement).{0,20}(bancaire|de banque)" |
type.inbound | eq |
| field:"type.inbound" kind:eq value:"true" |