Detection rules › Sublime MQL

BEC/Fraud: Job scam fake thread or plaintext pivot to freemail

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

Detects potential job scams using plaintext or fake threads attempting to pivot to a freemail address from an unsolicited sender.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesFree email provider, Out of band pivot

Event coverage

Rule body MQL

type.inbound
and any(ml.nlu_classifier(body.current_thread.text).entities,
        .name in ("greeting", "salutation")
)

// most likely to occur in plain text
and (
  body.html.raw is null
  or 

  // HTML is not null but fake thread
  (
    subject.is_reply or subject.is_forward
  )
  and (
    (length(headers.references) == 0 and headers.in_reply_to is null)
    or headers.in_reply_to is null
  )
)
and (
  3 of (
    any([subject.subject, body.current_thread.text],
        regex.icontains(., '(full|part).time')
    ),
    strings.ilike(body.current_thread.text, '*job*'),
    regex.icontains(body.current_thread.text, '\bHR\b'),
    strings.ilike(body.current_thread.text, '*manager*'),
    strings.ilike(body.current_thread.text, '*commission*'),
    strings.ilike(body.current_thread.text, '*hourly*'),
    strings.ilike(body.current_thread.text, '*per hour*'),
    strings.ilike(body.current_thread.text, '*prior experience*'),
    strings.ilike(body.current_thread.text, '*company rep*'),
    strings.ilike(body.current_thread.text, "100% legal")
  )
  or (
    length(ml.nlu_classifier(body.current_thread.text).topics) == 1
    and any(ml.nlu_classifier(body.current_thread.text).topics,
            .name == "Professional and Career Development"
            and .confidence == "high"
    )
    and (
      length(recipients.to) == 0
      or all(recipients.to,
             strings.ilike(.display_name, "Undisclosed?recipients")
      )
    )
  )
)

// all attachments are images or there's no attachments
and (
  (
    length(attachments) > 0
    and all(attachments, .file_type in $file_types_images)
  )
  or length(attachments) == 0
)

// there's an email in the body and it's a freemail
and any(regex.extract(body.current_thread.text,
                      "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
        ),
        strings.parse_email(.full_match).domain.domain in $free_email_providers
        or strings.parse_email(.full_match).domain.root_domain in $free_email_providers
)

// and that email doesn't match the sender domain
and (
  all(body.links, .href_url.domain.root_domain != sender.email.domain.domain)
  or sender.email.domain.root_domain in $free_email_providers
)

Detection logic

Scope: inbound message.

Detects potential job scams using plaintext or fake threads attempting to pivot to a freemail address from an unsolicited sender.

  1. inbound message
  2. any of ml.nlu_classifier(body.current_thread.text).entities where:
    • .name in ('greeting', 'salutation')
  3. any of:
    • body.html.raw is missing
    • all of:
      • any of:
        • subject.is_reply
        • subject.is_forward
      • any of:
        • all of:
          • length(headers.references) is 0
          • headers.in_reply_to is missing
        • headers.in_reply_to is missing
  4. any of:
    • at least 3 of:
      • any of [subject.subject, body.current_thread.text] where:
        • . matches '(full|part).time'
      • body.current_thread.text matches '*job*'
      • body.current_thread.text matches '\\bHR\\b'
      • body.current_thread.text matches '*manager*'
      • body.current_thread.text matches '*commission*'
      • body.current_thread.text matches '*hourly*'
      • body.current_thread.text matches '*per hour*'
      • body.current_thread.text matches '*prior experience*'
      • body.current_thread.text matches '*company rep*'
      • body.current_thread.text matches '100% legal'
    • all of:
      • length(ml.nlu_classifier(body.current_thread.text).topics) is 1
      • any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
        • .name is 'Professional and Career Development'
        • .confidence is 'high'
      • any of:
        • length(recipients.to) is 0
        • all of recipients.to where:
          • .display_name matches 'Undisclosed?recipients'
  5. any of:
    • all of:
      • length(attachments) > 0
      • all of attachments where:
        • .file_type in $file_types_images
    • length(attachments) is 0
  6. any of regex.extract(body.current_thread.text) where any holds:
    • strings.parse_email(.full_match).domain.domain in $free_email_providers
    • strings.parse_email(.full_match).domain.root_domain in $free_email_providers
  7. any of:
    • all of body.links where:
      • .href_url.domain.root_domain is not sender.email.domain.domain
    • sender.email.domain.root_domain in $free_email_providers

Inspects: attachments[].file_type, body.current_thread.text, body.html.raw, body.links, body.links[].href_url.domain.root_domain, headers.in_reply_to, headers.references, recipients.to, recipients.to[].display_name, sender.email.domain.domain, sender.email.domain.root_domain, subject.is_forward, subject.is_reply, subject.subject, type.inbound. Sensors: ml.nlu_classifier, regex.extract, regex.icontains, strings.ilike, strings.parse_email. Reference lists: $file_types_images, $free_email_providers.

Indicators matched (16)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).entities[].namemembergreeting
ml.nlu_classifier(body.current_thread.text).entities[].namemembersalutation
regex.icontainsregex(full|part).time
strings.ilikesubstring*job*
regex.icontainsregex\bHR\b
strings.ilikesubstring*manager*
strings.ilikesubstring*commission*
strings.ilikesubstring*hourly*
strings.ilikesubstring*per hour*
strings.ilikesubstring*prior experience*
strings.ilikesubstring*company rep*
strings.ilikesubstring100% legal
4 more
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsProfessional and Career Development
ml.nlu_classifier(body.current_thread.text).topics[].confidenceequalshigh
strings.ilikesubstringUndisclosed?recipients
regex.extractregex[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}