Detection rules › Sublime MQL
Business Email Compromise (BEC) with request for mobile number
This rule detects unsolicited messages with a small plain text body, that is attempting to solicit a mobile number.
Threat classification
Sublime's own taxonomy (not MITRE ATT&CK).
| Category | Values |
|---|---|
| Attack types | BEC/Fraud |
| Tactics and techniques | Social engineering |
Event coverage
| Message attribute |
|---|
| body.current_thread |
| headers (collection) |
| sender |
| subject |
| type |
Rule body MQL
type.inbound
and (
length(body.current_thread.text) < 500
or any(map(filter(ml.nlu_classifier(body.current_thread.text).entities,
.name == "disclaimer"
),
.text
),
(length(body.current_thread.text) - length(.)) < 500
)
)
and length(attachments) == 0
and regex.icontains(body.current_thread.text,
'(?:mobile|contact|current|reliable).{0,10}(?:phone|number|#|\bno)|whatsapp|\bcell|personalcell|(?:share|what).{0,25}number.{0,15}(?:connect|reach|text|message|contact|call)|(?:\bdrop|which|send.{0,5}your|best).{0,25}(?:number|\bnum\b|#).{0,15}(?:(?:connect|reach|contact|call).{0,5}you|text|message|works?\b|stay connected)|forward.{0,25}(?:\bnum\b|#)|get (?:your.{0,25}(?:number|\bnum\b|#)|in touch.{0,15}(?:via|by|through).{0,10}(?:text|phone|cell|sms|whatsapp))|(?:provide|confirm|reply.{0,15}with).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})?(?:number|\bnum\b|#|line)|(?:share|send).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})(?:number|\bnum\b|#|line)|(?:share|send).{0,25}preferred.{0,15}(?:text.?enabled.{0,15})?(?:number|\bnum\b|#|line)|(?:direct|preferred).{0,15}line.{0,15}(?:for|to|via).{0,10}(?:text|call|reach|contact|sms)|have.{0,15}preferred.{0,10}number'
)
and (
any(ml.nlu_classifier(body.current_thread.text).intents,
.name in ("bec", "advance_fee") and .confidence != "low"
)
or (
// confidence can be low on very short bodies
length(body.current_thread.text) < 550
and (
any(ml.nlu_classifier(body.current_thread.text).intents, .name == "bec")
or any(ml.nlu_classifier(sender.display_name).intents, .name == "bec")
or any(ml.nlu_classifier(body.current_thread.text).entities,
strings.icontains(.text, "kindly")
)
)
)
)
and (
(
(length(headers.references) > 0 or headers.in_reply_to is null)
and not (
(
strings.istarts_with(subject.subject, "RE:")
or strings.istarts_with(subject.subject, "RES:")
or strings.istarts_with(subject.subject, "R:")
or strings.istarts_with(subject.subject, "ODG:")
or strings.istarts_with(subject.subject, "答复:")
or strings.istarts_with(subject.subject, "AW:")
or strings.istarts_with(subject.subject, "TR:")
or strings.istarts_with(subject.subject, "FWD:")
or regex.imatch(subject.subject,
'(\[[^\]]+\]\s?){0,3}(re|fwd?|automat.*)\s?:.*'
)
)
)
)
or length(headers.references) == 0
)
and (
not profile.by_sender().solicited
or profile.by_sender().any_messages_malicious_or_spam
)
and not profile.by_sender().any_messages_benign
Detection logic
Scope: inbound message.
This rule detects unsolicited messages with a small plain text body, that is attempting to solicit a mobile number.
- inbound message
any of:
- length(body.current_thread.text) < 500
any of
map(...)where:- length(body.current_thread.text) - length(.) < 500
- length(attachments) is 0
- body.current_thread.text matches '(?:mobile|contact|current|reliable).{0,10}(?:phone|number|#|\\bno)|whatsapp|\\bcell|personalcell|(?:share|what).{0,25}number.{0,15}(?:connect|reach|text|message|contact|call)|(?:\\bdrop|which|send.{0,5}your|best).{0,25}(?:number|\\bnum\\b|#).{0,15}(?:(?:connect|reach|contact|call).{0,5}you|text|message|works?\\b|stay connected)|forward.{0,25}(?:\\bnum\\b|#)|get (?:your.{0,25}(?:number|\\bnum\\b|#)|in touch.{0,15}(?:via|by|through).{0,10}(?:text|phone|cell|sms|whatsapp))|(?:provide|confirm|reply.{0,15}with).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})?(?:number|\\bnum\\b|#|line)|(?:share|send).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})(?:number|\\bnum\\b|#|line)|(?:share|send).{0,25}preferred.{0,15}(?:text.?enabled.{0,15})?(?:number|\\bnum\\b|#|line)|(?:direct|preferred).{0,15}line.{0,15}(?:for|to|via).{0,10}(?:text|call|reach|contact|sms)|have.{0,15}preferred.{0,10}number'
any of:
any of
ml.nlu_classifier(body.current_thread.text).intentswhere all hold:- .name in ('bec', 'advance_fee')
- .confidence is not 'low'
all of:
- length(body.current_thread.text) < 550
any of:
any of
ml.nlu_classifier(body.current_thread.text).intentswhere:- .name is 'bec'
any of
ml.nlu_classifier(sender.display_name).intentswhere:- .name is 'bec'
any of
ml.nlu_classifier(body.current_thread.text).entitieswhere:- .text contains 'kindly'
any of:
all of:
any of:
- length(headers.references) > 0
- headers.in_reply_to is missing
none of:
- subject.subject starts with 'RE:'
- subject.subject starts with 'RES:'
- subject.subject starts with 'R:'
- subject.subject starts with 'ODG:'
- subject.subject starts with '答复:'
- subject.subject starts with 'AW:'
- subject.subject starts with 'TR:'
- subject.subject starts with 'FWD:'
- subject.subject matches '(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?|automat.*)\\s?:.*'
- length(headers.references) is 0
any of:
not:
- profile.by_sender().solicited
- profile.by_sender().any_messages_malicious_or_spam
not:
- profile.by_sender().any_messages_benign
Inspects: body.current_thread.text, headers.in_reply_to, headers.references, sender.display_name, subject.subject, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, regex.icontains, regex.imatch, strings.icontains, strings.istarts_with.
Indicators matched (16)
| Field | Match | Value |
|---|---|---|
ml.nlu_classifier(body.current_thread.text).entities[].name | equals | disclaimer |
regex.icontains | regex | (?:mobile|contact|current|reliable).{0,10}(?:phone|number|#|\bno)|whatsapp|\bcell|personalcell|(?:share|what).{0,25}number.{0,15}(?:connect|reach|text|message|contact|call)|(?:\bdrop|which|send.{0,5}your|best).{0,25}(?:number|\bnum\b|#).{0,15}(?:(?:connect|reach|contact|call).{0,5}you|text|message|works?\b|stay connected)|forward.{0,25}(?:\bnum\b|#)|get (?:your.{0,25}(?:number|\bnum\b|#)|in touch.{0,15}(?:via|by|through).{0,10}(?:text|phone|cell|sms|whatsapp))|(?:provide|confirm|reply.{0,15}with).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})?(?:number|\bnum\b|#|line)|(?:share|send).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})(?:number|\bnum\b|#|line)|(?:share|send).{0,25}preferred.{0,15}(?:text.?enabled.{0,15})?(?:number|\bnum\b|#|line)|(?:direct|preferred).{0,15}line.{0,15}(?:for|to|via).{0,10}(?:text|call|reach|contact|sms)|have.{0,15}preferred.{0,10}number |
ml.nlu_classifier(body.current_thread.text).intents[].name | member | bec |
ml.nlu_classifier(body.current_thread.text).intents[].name | member | advance_fee |
ml.nlu_classifier(body.current_thread.text).intents[].name | equals | bec |
ml.nlu_classifier(sender.display_name).intents[].name | equals | bec |
strings.icontains | substring | kindly |
strings.istarts_with | prefix | RE: |
strings.istarts_with | prefix | RES: |
strings.istarts_with | prefix | R: |
strings.istarts_with | prefix | ODG: |
strings.istarts_with | prefix | 答复: |
4 more
strings.istarts_with | prefix | AW: |
strings.istarts_with | prefix | TR: |
strings.istarts_with | prefix | FWD: |
regex.imatch | regex | (\[[^\]]+\]\s?){0,3}(re|fwd?|automat.*)\s?:.* |