Detection rules › Sublime MQL

Business Email Compromise: Request for mobile number via reply thread hijacking

Severity
medium
Type
rule
Source
github.com/sublime-security/sublime-rules

This rule detects BEC attacks that use reply threads to solicit mobile numbers, evading detection rules that exclude RE: subjects.

Threat classification

Sublime's own taxonomy (not MITRE ATT&CK).

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesSocial engineering

Event coverage

Rule body MQL

type.inbound
and 0 < length(body.previous_threads) < 3
and length(attachments) == 0
// Check previous_threads for mobile solicitation patterns
and any(body.previous_threads,
        (
          length(.text) < 500
          // ignore disclaimers in body length calculation
          or (
            any(map(filter(ml.nlu_classifier(.text).entities,
                           .name == "disclaimer"
                    ),
                    .text
                ),
                (length(..text) - length(.)) < 500
            )
          )
        )
        and regex.icontains(.text,
                            '(?:mobile|suitable|contact|current|cell|call|another).{0,10}(phone|number|#|\bno)|whatsapp|\bcell|personalcell|(?:reliable|recent).{0,30}(?:phone|number).{0,15}contact|(?:share|send|confirm).{0,20}number.{0,25}(?:text|sms|whatsapp|contact|reach\syou[\s,.\?](?:by|whether|when|for))'
        )
)

// NLU analysis on previous_threads content
and (
  any(body.previous_threads,
      any(ml.nlu_classifier(.text).intents,
          .name in ("bec", "advance_fee") and .confidence in ("medium", "high")
      )
  )
  or (
    // confidence can be low on very short bodies
    any(body.previous_threads, length(.text) < 550)
    and (
      any(body.previous_threads,
          any(ml.nlu_classifier(.text).intents, .name == "bec")
      )
      or any(ml.nlu_classifier(sender.display_name).intents, .name == "bec")
      or any(body.previous_threads,
             any(ml.nlu_classifier(.text).entities,
                 strings.icontains(.text, "kindly")
             )
      )
    )
  )
)
// Sender analysis
and (
  not profile.by_sender().solicited
  or profile.by_sender().any_messages_malicious_or_spam
)
and not profile.by_sender().any_messages_benign
// not high trust sender domains
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
)
// Ensure this is likely a hijacked thread (sender doesn't match thread participants)
and (length(headers.references) > 0 or headers.in_reply_to is not null)

Detection logic

Scope: inbound message.

This rule detects BEC attacks that use reply threads to solicit mobile numbers, evading detection rules that exclude RE: subjects.

  1. inbound message
  2. all of:
    • length(body.previous_threads) > 0
    • length(body.previous_threads) < 3
  3. length(attachments) is 0
  4. any of body.previous_threads where all hold:
    • any of:
      • length(.text) < 500
      • any of map(...) where:
        • length(.text) - length(.) < 500
    • .text matches '(?:mobile|suitable|contact|current|cell|call|another).{0,10}(phone|number|#|\\bno)|whatsapp|\\bcell|personalcell|(?:reliable|recent).{0,30}(?:phone|number).{0,15}contact|(?:share|send|confirm).{0,20}number.{0,25}(?:text|sms|whatsapp|contact|reach\\syou[\\s,.\\?](?:by|whether|when|for))'
  5. any of:
    • any of body.previous_threads where:
      • any of ml.nlu_classifier(.text).intents where all hold:
        • .name in ('bec', 'advance_fee')
        • .confidence in ('medium', 'high')
    • all of:
      • any of body.previous_threads where:
        • length(.text) < 550
      • any of:
        • any of body.previous_threads where:
          • any of ml.nlu_classifier(.text).intents where:
            • .name is 'bec'
        • any of ml.nlu_classifier(sender.display_name).intents where:
          • .name is 'bec'
        • any of body.previous_threads where:
          • any of ml.nlu_classifier(.text).entities where:
            • .text contains 'kindly'
  6. any of:
    • not:
      • profile.by_sender().solicited
    • profile.by_sender().any_messages_malicious_or_spam
  7. not:
    • profile.by_sender().any_messages_benign
  8. 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
  9. any of:
    • length(headers.references) > 0
    • headers.in_reply_to is set

Inspects: body.previous_threads, body.previous_threads[].text, headers.auth_summary.dmarc.pass, headers.in_reply_to, headers.references, sender.display_name, sender.email.domain.root_domain, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, regex.icontains, strings.icontains. Reference lists: $high_trust_sender_root_domains.

Indicators matched (9)

FieldMatchValue
ml.nlu_classifier(body.previous_threads[].text).entities[].nameequalsdisclaimer
regex.icontainsregex(?:mobile|suitable|contact|current|cell|call|another).{0,10}(phone|number|#|\bno)|whatsapp|\bcell|personalcell|(?:reliable|recent).{0,30}(?:phone|number).{0,15}contact|(?:share|send|confirm).{0,20}number.{0,25}(?:text|sms|whatsapp|contact|reach\syou[\s,.\?](?:by|whether|when|for))
ml.nlu_classifier(body.previous_threads[].text).intents[].namememberbec
ml.nlu_classifier(body.previous_threads[].text).intents[].namememberadvance_fee
ml.nlu_classifier(body.previous_threads[].text).intents[].confidencemembermedium
ml.nlu_classifier(body.previous_threads[].text).intents[].confidencememberhigh
ml.nlu_classifier(body.previous_threads[].text).intents[].nameequalsbec
ml.nlu_classifier(sender.display_name).intents[].nameequalsbec
strings.icontainssubstringkindly