Detection rules › Sublime MQL

Sublime MQL rules: suspicious

Body: Embedded email headers indicative of thread hijacking/abuse

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

Detects email headers embedded in the message body content, indicating forwarded phishing attempts, MIME boundary manipulation, delivery notification spoofing, or copy-paste phishing. This pattern is commonly seen when attackers forward legitimate emails and the headers get included in the body, or when spoofing system notifications.

Threat classification

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

CategoryValues
Attack typesCredential Phishing, BEC/Fraud, Spam
Tactics and techniquesEvasion, Social engineering, Spoofing

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.html
  • body.plain
  • headers
  • headers.auth_summary
  • sender.email
  • subject
  • type

Rule body

type.inbound
// Headers are in the body.plain.raw but not the body.html.raw
and 2 of (
  strings.icontains(body.plain.raw, "Delivered-To:")
  and not strings.icontains(body.html.raw, "Delivered-To:"),
  strings.icontains(body.plain.raw, "X-Google-Smtp-Source:")
  and not strings.icontains(body.html.raw, "X-Google-Smtp-Source:"),
  strings.icontains(body.plain.raw, "ARC-Seal: i=")
  and not strings.icontains(body.html.raw, "ARC-Seal: i="),
)
and 1 of (
  regex.icontains(body.plain.raw, "Received: by .{10,80} with SMTP id")
  and not regex.icontains(body.html.raw, "Received: by .{10,80} with SMTP id"),
  regex.icontains(body.plain.raw, "X-Received: by .{10,80} with SMTP id")
  and not regex.icontains(body.html.raw, "X-Received: by .{10,80} with SMTP id"),
)
// Negate legitimate forwards where users intentionally include headers
and not (
  (length(headers.references) > 0 or headers.in_reply_to is not null)
  and (subject.is_forward or subject.is_reply)
  and length(body.previous_threads) >= 1
)
// Sender is not from org or high trust domains
and sender.email.domain.root_domain not in $org_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
)

Detection logic

Scope: inbound message.

Detects email headers embedded in the message body content, indicating forwarded phishing attempts, MIME boundary manipulation, delivery notification spoofing, or copy-paste phishing. This pattern is commonly seen when attackers forward legitimate emails and the headers get included in the body, or when spoofing system notifications.

  1. inbound message
  2. at least 2 of:
    • all of:
      • body.plain.raw contains 'Delivered-To:'
      • not:
        • body.html.raw contains 'Delivered-To:'
    • all of:
      • body.plain.raw contains 'X-Google-Smtp-Source:'
      • not:
        • body.html.raw contains 'X-Google-Smtp-Source:'
    • all of:
      • body.plain.raw contains 'ARC-Seal: i='
      • not:
        • body.html.raw contains 'ARC-Seal: i='
  3. at least 1 of:
    • all of:
      • body.plain.raw matches 'Received: by .{10,80} with SMTP id'
      • not:
        • body.html.raw matches 'Received: by .{10,80} with SMTP id'
    • all of:
      • body.plain.raw matches 'X-Received: by .{10,80} with SMTP id'
      • not:
        • body.html.raw matches 'X-Received: by .{10,80} with SMTP id'
  4. not:
    • all of:
      • any of:
        • length(headers.references) > 0
        • headers.in_reply_to is set
      • any of:
        • subject.is_forward
        • subject.is_reply
      • length(body.previous_threads) ≥ 1
  5. sender.email.domain.root_domain not in $org_domains
  6. 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.html.raw, body.plain.raw, body.previous_threads, headers.auth_summary.dmarc.pass, headers.in_reply_to, headers.references, sender.email.domain.root_domain, subject.is_forward, subject.is_reply, type.inbound. Sensors: regex.icontains, strings.icontains. Reference lists: $high_trust_sender_root_domains, $org_domains.

Indicators matched (5)

FieldMatchValue
strings.icontainssubstringDelivered-To:
strings.icontainssubstringX-Google-Smtp-Source:
strings.icontainssubstringARC-Seal: i=
regex.icontainsregexReceived: by .{10,80} with SMTP id
regex.icontainsregexX-Received: by .{10,80} with SMTP id

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      not
        body.html.raw contains "ARC-Seal: i="
      body.plain.raw contains "ARC-Seal: i="
    and
      not
        body.html.raw contains "Delivered-To:"
      body.plain.raw contains "Delivered-To:"
    and
      not
        body.html.raw contains "X-Google-Smtp-Source:"
      body.plain.raw contains "X-Google-Smtp-Source:"
  or
    and
      not
        body.html.raw regex_match "Received: by .{10,80} with SMTP id"
      body.plain.raw regex_match "Received: by .{10,80} with SMTP id"
    and
      not
        body.html.raw regex_match "X-Received: by .{10,80} with SMTP id"
      body.plain.raw regex_match "X-Received: by .{10,80} with SMTP id"
  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.is_forward eq "true"
        subject.is_reply eq "true"
      body.previous_threads length_compare "1"
  type.inbound eq "true"
   macro "sender.email.domain.root_domain not in org_domains"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
headers.in_reply_tois_not_null(no value, null check)excludes:headers.in_reply_to
headers.referenceslength_compare0excludes:headers.references field:"headers.references" value:"0"
subject.is_forwardeqtrueexcludes:subject.is_forward field:"subject.is_forward" value:"true"
subject.is_replyeqtrueexcludes:subject.is_reply field:"subject.is_reply" value:"true"
body.previous_threadslength_compare1excludes:body.previous_threads field:"body.previous_threads" value:"1"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.plain.rawcontains
  • ARC-Seal: i=
  • Delivered-To:
  • X-Google-Smtp-Source:
field:"body.plain.raw" kind:contains
body.plain.rawregex_match
  • Received: by .{10,80} with SMTP id
  • X-Received: by .{10,80} with SMTP id
field:"body.plain.raw" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Fake shipping notification with suspicious language

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

Body contains keywords for shipping, contains suspicious language, and addresses the recipient by their email, which is an indicator of phishing and/or spam.

Threat classification

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

CategoryValues
Attack typesCredential Phishing, Spam
Tactics and techniquesEvasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • recipients
  • recipients.to
  • type

Rule body

type.inbound
// contains at least 1 link
and length(body.links) > 0
and 3 of (
  strings.ilike(body.current_thread.text, "*(1)*"),
  strings.ilike(body.current_thread.text, "*waiting for delivery*"),
  strings.ilike(body.current_thread.text, "*delivery missed*"),
  strings.ilike(body.current_thread.text, "*tracking number*")
)

// urgent/time-sensitive language
and any(ml.nlu_classifier(body.current_thread.text).entities,
        .name == "urgency"
)

// email is not personalized with recipients name
and any(recipients.to,
        any(ml.nlu_classifier(body.current_thread.text).entities,
            .text == ..email.local_part
        )
)

Detection logic

Scope: inbound message.

Body contains keywords for shipping, contains suspicious language, and addresses the recipient by their email, which is an indicator of phishing and/or spam.

  1. inbound message
  2. length(body.links) > 0
  3. at least 3 of 4: body.current_thread.text matches any of 4 patterns
    • *(1)*
    • *waiting for delivery*
    • *delivery missed*
    • *tracking number*
  4. any of ml.nlu_classifier(body.current_thread.text).entities where:
    • .name is 'urgency'
  5. any of recipients.to where:
    • any of ml.nlu_classifier(body.current_thread.text).entities where:
      • .text is .email.local_part

Inspects: body.current_thread.text, body.links, recipients.to, recipients.to[].email.local_part, type.inbound. Sensors: ml.nlu_classifier, strings.ilike.

Indicators matched (5)

FieldMatchValue
strings.ilikesubstring*(1)*
strings.ilikesubstring*waiting for delivery*
strings.ilikesubstring*delivery missed*
strings.ilikesubstring*tracking number*
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsurgency

Stages and Predicates

Stage 1: mql_rule

and
  any(recipients.to)
    any(ml.nlu_classifier(body.current_thread.text).entities)
      ml.nlu_classifier(body.current_thread.text).entities.text cross_field_compare "recipients.to.email.local_part"
  or
    body.current_thread.text match "(1)"
    body.current_thread.text match "delivery missed"
    body.current_thread.text match "tracking number"
    body.current_thread.text match "waiting for delivery"
  any(ml.nlu_classifier(body.current_thread.text).entities)
    ml.nlu_classifier(body.current_thread.text).entities.name eq "urgency"
  body.links length_compare "0"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textwildcard
  • *(1)*
  • *delivery missed*
  • *tracking number*
  • *waiting for delivery*
field:"body.current_thread.text" kind:wildcard
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Request for Quote or Purchase (RFQ|RFP) with HTML smuggling attachment

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

RFQ/RFP scams involve fraudulent emails posing as legitimate requests for quotations or purchases, often sent by scammers impersonating reputable organizations. These scams aim to deceive recipients into providing sensitive information or conducting unauthorized transactions, often leading to financial loss, or data leakage.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • attachments
  • body
  • body.current_thread
  • subject
  • type

Rule body

type.inbound

// RFP/RFQ language
and 1 of (
  regex.icontains(body.current_thread.text, '(discuss.{0,15}purchas(e|ing))'),
  regex.icontains(body.current_thread.text,
                  '(sign(ed?)|view).{0,10}(purchase order)|Request for a Quot(e|ation)'
  ),
  regex.icontains(body.current_thread.text, '(please|kindly).{0,30}quote'),
  regex.icontains(subject.subject,
                  '(request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b)'
  ),
  any(attachments,
      regex.icontains(.file_name, "(purchase.?order|Quot(e|ation))")
  ),
  any(ml.nlu_classifier(body.current_thread.text).entities, .name == "request")
  and any(ml.nlu_classifier(body.current_thread.text).entities,
          .name == "urgency"
  ),
  any(ml.nlu_classifier(body.current_thread.text).tags,
      .name == "purchase_order" and .confidence == "high"
  )
)

// HTML smuggling
and any(attachments,
        (
          .file_extension in~ ("html", "htm", "shtml", "dhtml")
          or .file_extension in~ $file_extensions_common_archives
          or .file_type == "html"
        )
        and any(file.explode(.),
                (
                  length(filter(.scan.javascript.identifiers,
                                strings.like(., "document", "write", "atob")
                         )
                  ) == 3
                  // usage: document['write'](atob)
                  or any(.scan.strings.strings,
                         strings.ilike(., "*document*write*atob*")
                  )
                  // usage: some_var = atob();
                  or any(.scan.strings.strings, strings.ilike(., "*=*atob*;"))
                  // usage: obfuscating "atob"
                  or any(.scan.javascript.identifiers,
                         strings.ilike(., '*ato\u0062*')
                  )
                  // usage: document.head.insertAdjacentHTML("beforeend", atob(...
                  or any(.scan.strings.strings,
                         strings.ilike(.,
                                       "*document*write*atob*",
                                       "*document*insertAdjacentHTML*atob*"
                         )
                  )
                )
        )
)

Detection logic

Scope: inbound message.

RFQ/RFP scams involve fraudulent emails posing as legitimate requests for quotations or purchases, often sent by scammers impersonating reputable organizations. These scams aim to deceive recipients into providing sensitive information or conducting unauthorized transactions, often leading to financial loss, or data leakage.

  1. inbound message
  2. at least 1 of:
    • body.current_thread.text matches '(discuss.{0,15}purchas(e|ing))'
    • body.current_thread.text matches '(sign(ed?)|view).{0,10}(purchase order)|Request for a Quot(e|ation)'
    • body.current_thread.text matches '(please|kindly).{0,30}quote'
    • subject.subject matches '(request for (purchase|quot(e|ation))|\\bRFQ\\b|\\bRFP\\b)'
    • any of attachments where:
      • .file_name matches '(purchase.?order|Quot(e|ation))'
    • all of:
      • any of ml.nlu_classifier(body.current_thread.text).entities where:
        • .name is 'request'
      • any of ml.nlu_classifier(body.current_thread.text).entities where:
        • .name is 'urgency'
    • any of ml.nlu_classifier(body.current_thread.text).tags where all hold:
      • .name is 'purchase_order'
      • .confidence is 'high'
  3. any of attachments where all hold:
    • any of:
      • .file_extension in ('html', 'htm', 'shtml', 'dhtml')
      • .file_extension in $file_extensions_common_archives
      • .file_type is 'html'
    • any of file.explode(.) where any holds:
      • length(filter(.scan.javascript.identifiers, strings.like(., 'document', 'write', 'atob'))) is 3
      • any of .scan.strings.strings where:
        • . matches '*document*write*atob*'
      • any of .scan.strings.strings where:
        • . matches '*=*atob*;'
      • any of .scan.javascript.identifiers where:
        • . matches '*ato\\u0062*'
      • any of .scan.strings.strings where:
        • . matches any of 2 patterns
          • *document*write*atob*
          • *document*insertAdjacentHTML*atob*

Inspects: attachments[].file_extension, attachments[].file_name, attachments[].file_type, body.current_thread.text, subject.subject, type.inbound. Sensors: file.explode, ml.nlu_classifier, regex.icontains, strings.ilike, strings.like. Reference lists: $file_extensions_common_archives.

Indicators matched (21)

FieldMatchValue
regex.icontainsregex(discuss.{0,15}purchas(e|ing))
regex.icontainsregex(sign(ed?)|view).{0,10}(purchase order)|Request for a Quot(e|ation)
regex.icontainsregex(please|kindly).{0,30}quote
regex.icontainsregex(request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b)
regex.icontainsregex(purchase.?order|Quot(e|ation))
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsrequest
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsurgency
ml.nlu_classifier(body.current_thread.text).tags[].nameequalspurchase_order
ml.nlu_classifier(body.current_thread.text).tags[].confidenceequalshigh
attachments[].file_extensionmemberhtml
attachments[].file_extensionmemberhtm
attachments[].file_extensionmembershtml
9 more
attachments[].file_extensionmemberdhtml
attachments[].file_typeequalshtml
strings.likesubstringdocument
strings.likesubstringwrite
strings.likesubstringatob
strings.ilikesubstring*document*write*atob*
strings.ilikesubstring*=*atob*;
strings.ilikesubstring*ato\u0062*
strings.ilikesubstring*document*insertAdjacentHTML*atob*

Stages and Predicates

Stage 1: mql_rule

and
  any(attachments)
    and
      any(file.explode(attachments))
        or
          any(file.explode(attachments).scan.strings.strings)
            or
              file.explode(attachments[])[].scan.strings.strings[] wildcard "*document*insertAdjacentHTML*atob*"
              file.explode(attachments[])[].scan.strings.strings[] wildcard "*document*write*atob*"
          any(file.explode(attachments).scan.javascript.identifiers)
            file.explode(attachments).scan.javascript.identifiers match "ato\\u0062"
          any(file.explode(attachments).scan.strings.strings)
            file.explode(attachments).scan.strings.strings wildcard "*=*atob*;"
          any(file.explode(attachments).scan.strings.strings)
            file.explode(attachments).scan.strings.strings wildcard "*document*write*atob*"
          filter(file.explode(attachments[])[].scan.javascript.identifiers, strings.like(file.explode(attachments[])[], 'document', 'write', 'atob')) length_compare "3"
      or
        attachments.file_extension in ["dhtml", "htm", "html", "shtml"]
        attachments.file_type eq "html"
         macro "attachments[].file_extension in file_extensions_common_archives"
  or
    and
      any(ml.nlu_classifier(body.current_thread.text).entities)
        ml.nlu_classifier(body.current_thread.text).entities.name eq "request"
      any(ml.nlu_classifier(body.current_thread.text).entities)
        ml.nlu_classifier(body.current_thread.text).entities.name eq "urgency"
    any(ml.nlu_classifier(body.current_thread.text).tags)
      and
        ml.nlu_classifier(body.current_thread.text).tags.confidence eq "high"
        ml.nlu_classifier(body.current_thread.text).tags.name eq "purchase_order"
    any(attachments)
      attachments.file_name regex_match "(purchase.?order|Quot(e|ation))"
    body.current_thread.text regex_match "(discuss.{0,15}purchas(e|ing))"
    body.current_thread.text regex_match "(please|kindly).{0,30}quote"
    body.current_thread.text regex_match "(sign(ed?)|view).{0,10}(purchase order)|Request for a Quot(e|ation)"
    subject.subject regex_match "(request for (purchase|quot(e|ation))|\\bRFQ\\b|\\bRFP\\b)"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • (discuss.{0,15}purchas(e|ing))
  • (please|kindly).{0,30}quote
    • (sign(ed?)|view).{0,10}(purchase order)
    • Request for a Quot(e|ation)
field:"body.current_thread.text" kind:regex_match
subject.subjectregex_match
  • (request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b)
field:"subject.subject" kind:regex_match value:"(request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b)"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Request for Quote or Purchase (RFQ|RFP) with suspicious sender or recipient pattern

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

RFQ/RFP scams involve fraudulent emails posing as legitimate requests for quotations or purchases, often sent by scammers impersonating reputable organizations. These scams aim to deceive recipients into providing sensitive information or conducting unauthorized transactions, often leading to financial loss, or data leakage.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesEvasion, Free email provider

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • attachments
  • body
  • body.current_thread
  • body.links
  • headers
  • headers.auth_summary
  • headers.reply_to
  • recipients
  • recipients.cc[0]
  • recipients.to
  • recipients.to[0]
  • sender.email
  • subject
  • type

Rule body

type.inbound
and (
  (
    (
      length(recipients.to) == 0
      or all(recipients.to,
             .display_name in (
               "Undisclosed recipients",
               "undisclosed-recipients"
             )
      )
    )
    and length(recipients.cc) == 0
  )
  or (
    sender.email.domain.root_domain in $free_email_providers
    and any(headers.reply_to, .email.email != sender.email.email)
    and any(headers.reply_to, .email.email not in $recipient_emails)
  )
  or (
    length(headers.reply_to) > 0
    and all(headers.reply_to,
            .email.domain.root_domain != sender.email.domain.root_domain
            and not .email.domain.root_domain in $org_domains
    )
  )
  or (
    length(recipients.to) == 1
    and all(recipients.to, .email.email == sender.email.email)
    and (length(recipients.cc) > 0 or length(recipients.bcc) > 0)
  )
  or (
    length(recipients.to) == 0
    and length(recipients.cc) == 1
    and sender.email.email == recipients.cc[0].email.email
  )
  or (
    length(recipients.to) == 1
    and length(recipients.cc) == 0
    and sender.email.email == recipients.to[0].email.email
  )
)
and (
  // Group the keyword patterns that specifically indicate RFQ/RFP
  (
    (
      // RFQ/RFP specific language patterns
      regex.icontains(body.current_thread.text,
                      '(discuss.{0,15}purchas(e|ing))'
      )
      or regex.icontains(body.current_thread.text,
                         '(sign(ed?)|view).{0,10}(purchase order)|Request for (a Quot(e|ation)|Proposal)'
      )
      or regex.icontains(body.current_thread.text,
                         '(please|kindly|preferred).{0,30}(?:proposal|quot(e|ation))'
      )
      or regex.icontains(subject.subject,
                         '(request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b|bid invit(e|ation))'
      )
      or any(attachments,
             regex.icontains(.file_name, "(purchase.?order|Quot(e|ation))")
      )
      or any(ml.nlu_classifier(body.current_thread.text).tags,
             .name == "purchase_order" and .confidence == "high"
      )
      or any(ml.nlu_classifier(body.current_thread.text).entities,
             .name == "financial" and regex.imatch(.text, "rfp|rfq")
      )
      or any(ml.nlu_classifier(body.current_thread.text).entities,
             .name == "request" and strings.icontains(.text, 'submit bid')
      )
    )
    // Required: at least one RFQ/RFP keyword pattern

    // Optional: at least one additional indicator (can be another keyword pattern or a non-keyword indicator)
    and (
      2 of (
        // RFQ/RFP keyword patterns (same as above)
        regex.icontains(body.current_thread.text,
                        '(discuss.{0,15}purchas(e|ing))'
        ),
        regex.icontains(body.current_thread.text,
                        '(sign(ed?)|view).{0,10}(purchase order)|Request for a Quot(e|ation)'
        ),
        regex.icontains(body.current_thread.text,
                        '(please|kindly|preferred).{0,30}(?:proposal|quot(e|ation))'
        ),
        regex.icontains(body.current_thread.text,
                        '(?:invitation|intent) to bid'
        ),
        regex.icontains(subject.subject,
                        '(request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b|bid invit(e|ation))'
        ),
        any(attachments,
            regex.icontains(.file_name, "(purchase.?order|Quot(e|ation))")
        ),
        any(ml.nlu_classifier(body.current_thread.text).tags,
            .name == "purchase_order" and .confidence == "high"
        ),
        any(ml.nlu_classifier(body.current_thread.text).entities,
            .name == "financial" and regex.imatch(.text, "(?:rfp|rfq)")
        ),
        any(attachments,
            any(file.explode(.),
                any(ml.nlu_classifier(.scan.ocr.raw).entities,
                    .name == "request"
                    and regex.imatch(.text, "view (?:rfp|rfq)")
                )
            )
        ),

        // Non-keyword indicators
        (
          any(ml.nlu_classifier(body.current_thread.text).entities,
              .name == "request"
          )
          and any(ml.nlu_classifier(body.current_thread.text).entities,
                  .name == "urgency"
          )
          and not any(ml.nlu_classifier(body.current_thread.text).topics,
                      .name == "Advertising and Promotions"
                      and .confidence == "high"
          )
        ),
        (
          0 < length(filter(body.links,
                            (
                              .href_url.domain.domain in $free_subdomain_hosts
                              or .href_url.domain.domain in $free_file_hosts
                              or network.whois(.href_url.domain).days_old < 30
                            )
                            and (
                              regex.match(.display_text, '[A-Z ]+')
                              or any(ml.nlu_classifier(.display_text).entities,
                                     .name in ("request", "urgency")
                              )
                              or any(ml.nlu_classifier(.display_text).intents,
                                     .name in ("cred_theft")
                              )
                            )
                     )
          ) < 3
        ),
        // mentions an attachment that does not exist
        (
          length(attachments) == 0
          and strings.icontains(body.current_thread.text, "attached")
        ),
        any(body.current_thread.links, regex.icontains(.href_url.url, 'RFP'))
      )
    )
  )
  or (
    length(attachments) == 1
    and length(body.current_thread.text) < 100
    and all(attachments,
            .file_type in $file_types_images
            and any(file.explode(.),
                    2 of (
                      regex.icontains(.scan.ocr.raw,
                                      '(discuss.{0,15}purchas(e|ing))'
                      ),
                      regex.icontains(.scan.ocr.raw,
                                      '(sign(ed?)|view).{0,10}(purchase order)|Request for a Quot(e|ation)'
                      ),
                      regex.icontains(.scan.ocr.raw,
                                      '(please|kindly).{0,30}quote'
                      ),
                      (
                        any(ml.nlu_classifier(.scan.ocr.raw).entities,
                            .name == "request"
                        )
                        and any(ml.nlu_classifier(.scan.ocr.raw).entities,
                                .name == "urgency"
                        )
                      ),
                      any(ml.nlu_classifier(.scan.ocr.raw).tags,
                          .name == "purchase_order" and .confidence == "high"
                      ),
                      any(ml.nlu_classifier(.scan.ocr.raw).entities,
                          .name == "financial"
                          and regex.imatch(.text, "rfp|rfq")
                      ),
                    )
            )
    )
  )
  // fake PDF file icon used as a link lure with bid solicitation language
  or (
    regex.icontains(subject.subject, 'project\s+summary')
    and any(html.xpath(body.html, '//div[.//img[contains(@src,"pdf")]]//a').nodes,
            regex.icontains(.display_text, 'project\s+summary')
    )
    and regex.icontains(body.current_thread.text,
                        '(put a bid|\bbid\s+(for|on)\b|submit.{1,20}(bid|quot(e|ation))|request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b|project\s+summary)'
    )
  )
)
// wetransfer includes user specific reply-to's & link display text which triggers NLU logic further within the rule
and not (
  sender.email.domain.root_domain == "wetransfer.com"
  and coalesce(headers.auth_summary.dmarc.pass, false)
)

// 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().solicited
      or profile.by_sender().days_since.last_contact > 30
    )
    and not profile.by_sender().any_messages_benign
  )
  // sender address listed as a recipient
  or (
    length(recipients.to) == 1
    and sender.email.email in map(recipients.to, .email.email)
  )
)

Detection logic

Scope: inbound message.

RFQ/RFP scams involve fraudulent emails posing as legitimate requests for quotations or purchases, often sent by scammers impersonating reputable organizations. These scams aim to deceive recipients into providing sensitive information or conducting unauthorized transactions, often leading to financial loss, or data leakage.

  1. inbound message
  2. any of:
    • all of:
      • any of:
        • length(recipients.to) is 0
        • all of recipients.to where:
          • .display_name in ('Undisclosed recipients', 'undisclosed-recipients')
      • length(recipients.cc) is 0
    • all of:
      • sender.email.domain.root_domain in $free_email_providers
      • any of headers.reply_to where:
        • .email.email is not sender.email.email
      • any of headers.reply_to where:
        • .email.email not in $recipient_emails
    • all of:
      • length(headers.reply_to) > 0
      • all of headers.reply_to where all hold:
        • .email.domain.root_domain is not sender.email.domain.root_domain
        • not:
          • .email.domain.root_domain in $org_domains
    • all of:
      • length(recipients.to) is 1
      • all of recipients.to where:
        • .email.email is sender.email.email
      • any of:
        • length(recipients.cc) > 0
        • length(recipients.bcc) > 0
    • all of:
      • length(recipients.to) is 0
      • length(recipients.cc) is 1
      • sender.email.email is recipients.cc[0].email.email
    • all of:
      • length(recipients.to) is 1
      • length(recipients.cc) is 0
      • sender.email.email is recipients.to[0].email.email
  3. any of:
    • all of:
      • any of:
        • body.current_thread.text matches '(discuss.{0,15}purchas(e|ing))'
        • body.current_thread.text matches '(sign(ed?)|view).{0,10}(purchase order)|Request for (a Quot(e|ation)|Proposal)'
        • body.current_thread.text matches '(please|kindly|preferred).{0,30}(?:proposal|quot(e|ation))'
        • subject.subject matches '(request for (purchase|quot(e|ation))|\\bRFQ\\b|\\bRFP\\b|bid invit(e|ation))'
        • any of attachments where:
          • .file_name matches '(purchase.?order|Quot(e|ation))'
        • any of ml.nlu_classifier(body.current_thread.text).tags where all hold:
          • .name is 'purchase_order'
          • .confidence is 'high'
        • any of ml.nlu_classifier(body.current_thread.text).entities where all hold:
          • .name is 'financial'
          • .text matches 'rfp|rfq'
        • any of ml.nlu_classifier(body.current_thread.text).entities where all hold:
          • .name is 'request'
          • .text contains 'submit bid'
      • at least 2 of:
        • body.current_thread.text matches '(discuss.{0,15}purchas(e|ing))'
        • body.current_thread.text matches '(sign(ed?)|view).{0,10}(purchase order)|Request for a Quot(e|ation)'
        • body.current_thread.text matches '(please|kindly|preferred).{0,30}(?:proposal|quot(e|ation))'
        • body.current_thread.text matches '(?:invitation|intent) to bid'
        • subject.subject matches '(request for (purchase|quot(e|ation))|\\bRFQ\\b|\\bRFP\\b|bid invit(e|ation))'
        • any of attachments where:
          • .file_name matches '(purchase.?order|Quot(e|ation))'
        • any of ml.nlu_classifier(body.current_thread.text).tags where all hold:
          • .name is 'purchase_order'
          • .confidence is 'high'
        • any of ml.nlu_classifier(body.current_thread.text).entities where all hold:
          • .name is 'financial'
          • .text matches '(?:rfp|rfq)'
        • any of attachments where:
          • any of file.explode(.) where:
            • any of ml.nlu_classifier(.scan.ocr.raw).entities where all hold:
              • .name is 'request'
              • .text matches 'view (?:rfp|rfq)'
        • all of:
          • any of ml.nlu_classifier(body.current_thread.text).entities where:
            • .name is 'request'
          • any of ml.nlu_classifier(body.current_thread.text).entities where:
            • .name is 'urgency'
          • not:
            • any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
              • .name is 'Advertising and Promotions'
              • .confidence is 'high'
        • all of:
          • length(filter(body.links, .href_url.domain.domain in $free_subdomain_hosts or .href_url.domain.domain in $free_file_hosts or network.whois(.href_url.domain).days_old < 30 and regex.match(.display_text, '[A-Z ]+') or any(ml.nlu_classifier(.display_text).entities, .name in ('request', 'urgency')) or any(ml.nlu_classifier(.display_text).intents, .name in ('cred_theft')))) > 0
          • length(filter(body.links, .href_url.domain.domain in $free_subdomain_hosts or .href_url.domain.domain in $free_file_hosts or network.whois(.href_url.domain).days_old < 30 and regex.match(.display_text, '[A-Z ]+') or any(ml.nlu_classifier(.display_text).entities, .name in ('request', 'urgency')) or any(ml.nlu_classifier(.display_text).intents, .name in ('cred_theft')))) < 3
        • all of:
          • length(attachments) is 0
          • body.current_thread.text contains 'attached'
        • any of body.current_thread.links where:
          • .href_url.url matches 'RFP'
    • all of:
      • length(attachments) is 1
      • length(body.current_thread.text) < 100
      • all of attachments where all hold:
        • .file_type in $file_types_images
        • any of file.explode(.) where:
          • at least 2 of:
            • .scan.ocr.raw matches '(discuss.{0,15}purchas(e|ing))'
            • .scan.ocr.raw matches '(sign(ed?)|view).{0,10}(purchase order)|Request for a Quot(e|ation)'
            • .scan.ocr.raw matches '(please|kindly).{0,30}quote'
            • all of:
              • any of ml.nlu_classifier(.scan.ocr.raw).entities where:
                • .name is 'request'
              • any of ml.nlu_classifier(.scan.ocr.raw).entities where:
                • .name is 'urgency'
            • any of ml.nlu_classifier(.scan.ocr.raw).tags where all hold:
              • .name is 'purchase_order'
              • .confidence is 'high'
            • any of ml.nlu_classifier(.scan.ocr.raw).entities where all hold:
              • .name is 'financial'
              • .text matches 'rfp|rfq'
    • all of:
      • subject.subject matches 'project\\s+summary'
      • any of html.xpath(body.html, '//div[.//img[contains(@src,"pdf")]]//a').nodes where:
        • .display_text matches 'project\\s+summary'
      • body.current_thread.text matches '(put a bid|\\bbid\\s+(for|on)\\b|submit.{1,20}(bid|quot(e|ation))|request for (purchase|quot(e|ation))|\\bRFQ\\b|\\bRFP\\b|project\\s+summary)'
  4. not:
    • all of:
      • sender.email.domain.root_domain is 'wetransfer.com'
      • coalesce(headers.auth_summary.dmarc.pass)
  5. 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
  6. any of:
    • all of:
      • any of:
        • not:
          • profile.by_sender().solicited
        • profile.by_sender().days_since.last_contact > 30
      • not:
        • profile.by_sender().any_messages_benign
    • all of:
      • length(recipients.to) is 1
      • sender.email.email in map(recipients.to, .email.email)

Inspects: attachments[].file_name, attachments[].file_type, body.current_thread.links, body.current_thread.links[].href_url.url, body.current_thread.text, body.html, body.links, body.links[].display_text, body.links[].href_url.domain, body.links[].href_url.domain.domain, headers.auth_summary.dmarc.pass, headers.reply_to, headers.reply_to[].email.domain.root_domain, headers.reply_to[].email.email, recipients.bcc, recipients.cc, recipients.cc[0].email.email, recipients.to, recipients.to[0].email.email, recipients.to[].display_name, recipients.to[].email.email, sender.email.domain.root_domain, sender.email.email, subject.subject, type.inbound. Sensors: file.explode, html.xpath, ml.nlu_classifier, network.whois, profile.by_sender, regex.icontains, regex.imatch, regex.match, strings.icontains. Reference lists: $file_types_images, $free_email_providers, $free_file_hosts, $free_subdomain_hosts, $high_trust_sender_root_domains, $org_domains, $recipient_emails.

Indicators matched (32)

FieldMatchValue
recipients.to[].display_namememberUndisclosed recipients
recipients.to[].display_namememberundisclosed-recipients
regex.icontainsregex(discuss.{0,15}purchas(e|ing))
regex.icontainsregex(sign(ed?)|view).{0,10}(purchase order)|Request for (a Quot(e|ation)|Proposal)
regex.icontainsregex(please|kindly|preferred).{0,30}(?:proposal|quot(e|ation))
regex.icontainsregex(request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b|bid invit(e|ation))
regex.icontainsregex(purchase.?order|Quot(e|ation))
ml.nlu_classifier(body.current_thread.text).tags[].nameequalspurchase_order
ml.nlu_classifier(body.current_thread.text).tags[].confidenceequalshigh
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsfinancial
regex.imatchregexrfp|rfq
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsrequest
20 more
strings.icontainssubstringsubmit bid
regex.icontainsregex(sign(ed?)|view).{0,10}(purchase order)|Request for a Quot(e|ation)
regex.icontainsregex(?:invitation|intent) to bid
regex.imatchregex(?:rfp|rfq)
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).entities[].nameequalsrequest
regex.imatchregexview (?:rfp|rfq)
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsurgency
regex.matchregex[A-Z ]+
ml.nlu_classifier(body.links[].display_text).entities[].namememberrequest
ml.nlu_classifier(body.links[].display_text).entities[].namememberurgency
ml.nlu_classifier(body.links[].display_text).intents[].namemembercred_theft
strings.icontainssubstringattached
regex.icontainsregexRFP
regex.icontainsregex(please|kindly).{0,30}quote
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).entities[].nameequalsurgency
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).tags[].nameequalspurchase_order
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).tags[].confidenceequalshigh
ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).entities[].nameequalsfinancial
regex.icontainsregexproject\s+summary
regex.icontainsregex(put a bid|\bbid\s+(for|on)\b|submit.{1,20}(bid|quot(e|ation))|request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b|project\s+summary)

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        and
          not
            any(ml.nlu_classifier(body.current_thread.text).topics)
              and
                ml.nlu_classifier(body.current_thread.text).topics.confidence eq "high"
                ml.nlu_classifier(body.current_thread.text).topics.name eq "Advertising and Promotions"
          any(ml.nlu_classifier(body.current_thread.text).entities)
            ml.nlu_classifier(body.current_thread.text).entities.name eq "request"
          any(ml.nlu_classifier(body.current_thread.text).entities)
            ml.nlu_classifier(body.current_thread.text).entities.name eq "urgency"
        any(attachments)
          any(file.explode(attachments))
            any(ml.nlu_classifier(file.explode(attachments).scan.ocr.raw).entities)
              and
                ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).entities[].name eq "request"
                ml.nlu_classifier(file.explode(attachments[])[].scan.ocr.raw).entities[].text regex_match "view (?:rfp|rfq)"
        any(ml.nlu_classifier(body.current_thread.text).entities)
          and
            ml.nlu_classifier(body.current_thread.text).entities.name eq "financial"
            ml.nlu_classifier(body.current_thread.text).entities.text regex_match "(?:rfp|rfq)"
        any(ml.nlu_classifier(body.current_thread.text).tags)
          and
            ml.nlu_classifier(body.current_thread.text).tags.confidence eq "high"
            ml.nlu_classifier(body.current_thread.text).tags.name eq "purchase_order"
        and
          attachments length_compare "0"
          body.current_thread.text contains "attached"
        any(attachments)
          attachments.file_name regex_match "(purchase.?order|Quot(e|ation))"
        any(body.current_thread.links)
          body.current_thread.links.href_url.url regex_match "RFP"
        and
          filter(body.links, .href_url.domain.domain in $free_subdomain_hosts or .href_url.domain.domain in $free_file_hosts or network.whois(.href_url.domain).days_old < 30 and regex.match(.display_text, '[A-Z ]+') or any(ml.nlu_classifier(.display_text).entities, .name in ('request', 'urgency')) or any(ml.nlu_classifier(.display_text).intents, .name in ('cred_theft'))) length_compare "0"
          filter(body.links, .href_url.domain.domain in $free_subdomain_hosts or .href_url.domain.domain in $free_file_hosts or network.whois(.href_url.domain).days_old < 30 and regex.match(.display_text, '[A-Z ]+') or any(ml.nlu_classifier(.display_text).entities, .name in ('request', 'urgency')) or any(ml.nlu_classifier(.display_text).intents, .name in ('cred_theft'))) length_compare "3"
        body.current_thread.text regex_match "(?:invitation|intent) to bid"
        body.current_thread.text regex_match "(discuss.{0,15}purchas(e|ing))"
        body.current_thread.text regex_match "(please|kindly|preferred).{0,30}(?:proposal|quot(e|ation))"
        body.current_thread.text regex_match "(sign(ed?)|view).{0,10}(purchase order)|Request for a Quot(e|ation)"
        subject.subject regex_match "(request for (purchase|quot(e|ation))|\\bRFQ\\b|\\bRFP\\b|bid invit(e|ation))"
      or
        any(ml.nlu_classifier(body.current_thread.text).entities)
          and
            ml.nlu_classifier(body.current_thread.text).entities.name eq "financial"
            ml.nlu_classifier(body.current_thread.text).entities.text regex_match "rfp|rfq"
        any(ml.nlu_classifier(body.current_thread.text).entities)
          and
            ml.nlu_classifier(body.current_thread.text).entities.name eq "request"
            ml.nlu_classifier(body.current_thread.text).entities.text contains "submit bid"
        any(ml.nlu_classifier(body.current_thread.text).tags)
          and
            ml.nlu_classifier(body.current_thread.text).tags.confidence eq "high"
            ml.nlu_classifier(body.current_thread.text).tags.name eq "purchase_order"
        any(attachments)
          attachments.file_name regex_match "(purchase.?order|Quot(e|ation))"
        body.current_thread.text regex_match "(discuss.{0,15}purchas(e|ing))"
        body.current_thread.text regex_match "(please|kindly|preferred).{0,30}(?:proposal|quot(e|ation))"
        body.current_thread.text regex_match "(sign(ed?)|view).{0,10}(purchase order)|Request for (a Quot(e|ation)|Proposal)"
        subject.subject regex_match "(request for (purchase|quot(e|ation))|\\bRFQ\\b|\\bRFP\\b|bid invit(e|ation))"
    and
      any(html.xpath(body.html, '//div[.//img[contains(@src,"pdf")]]//a').nodes)
        html.xpath(body.html, '//div[.//img[contains(@src,"pdf")]]//a').nodes.display_text regex_match "project\\s+summary"
      body.current_thread.text regex_match "(put a bid|\\bbid\\s+(for|on)\\b|submit.{1,20}(bid|quot(e|ation))|request for (purchase|quot(e|ation))|\\bRFQ\\b|\\bRFP\\b|project\\s+summary)"
      subject.subject regex_match "project\\s+summary"
    and
      attachments length_compare "1"
      body.current_thread.text length_compare "100"
       macro "all(attachments)"
  or
    and
      or
        not
          profile.by_sender func_call "profile.by_sender().solicited"
        profile.by_sender func_call "profile.by_sender().days_since.last_contact > 30"
      not
        profile.by_sender func_call "profile.by_sender().any_messages_benign"
    and
      recipients.to length_compare "1"
       macro "sender.email.email in map(recipients.to, .email.email)"
  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"
  or
    and
      any(headers.reply_to)
        headers.reply_to.email.email cross_field_compare "sender.email.email"
      any(headers.reply_to)
         macro "headers.reply_to[].email.email not in recipient_emails"
       macro "sender.email.domain.root_domain in free_email_providers"
    and
      or
        recipients.bcc length_compare "0"
        recipients.cc length_compare "0"
      recipients.to length_compare "1"
       macro "all(recipients.to)"
    and
      or
        recipients.to length_compare "0"
         macro "all(recipients.to)"
      recipients.cc length_compare "0"
    and
      headers.reply_to length_compare "0"
       macro "all(headers.reply_to)"
    and
      recipients.cc length_compare "0"
      recipients.to length_compare "1"
      sender.email.email cross_field_compare "recipients.to[0].email.email"
    and
      recipients.cc length_compare "1"
      recipients.to length_compare "0"
      sender.email.email cross_field_compare "recipients.cc[0].email.email"
  not
    and
      coalesce func_call "coalesce(headers.auth_summary.dmarc.pass)"
      sender.email.domain.root_domain eq "wetransfer.com"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
sender.email.domain.root_domaineqwetransfer.comexcludes:sender.email.domain.root_domain field:"sender.email.domain.root_domain" value:"wetransfer.com"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textcontains
  • attached
field:"body.current_thread.text" kind:contains value:"attached"
body.current_thread.textregex_match
  • (?:invitation|intent) to bid
  • (discuss.{0,15}purchas(e|ing))
  • (please|kindly|preferred).{0,30}(?:proposal|quot(e|ation))
  • (put a bid|\bbid\s+(for|on)\b|submit.{1,20}(bid|quot(e|ation))|request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b|project\s+summary)
    • (sign(ed?)|view).{0,10}(purchase order)
    • Request for (a Quot(e|ation)|Proposal)
    • (sign(ed?)|view).{0,10}(purchase order)
    • Request for a Quot(e|ation)
field:"body.current_thread.text" kind:regex_match
sender.email.emailcross_field_compare
  • recipients.cc[0].email.email transforms: op:eq
  • recipients.to[0].email.email transforms: op:eq
field:"sender.email.email" kind:cross_field_compare
subject.subjectregex_match
  • (request for (purchase|quot(e|ation))|\bRFQ\b|\bRFP\b|bid invit(e|ation))
  • project\s+summary
field:"subject.subject" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Sharepoint file share with suspicious recipients pattern

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

This rule detects messages originating from sharepoint.com with undisclosed recipients that are attempting to solicit the user to click a link. This has been observed in the event of an account compromise where the compromised account was utilizing legitimate file sharing services to share malicious links.

Threat classification

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

CategoryValues
Attack typesCredential Phishing, Malware/Ransomware
Tactics and techniquesEvasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • body.links
  • recipients
  • recipients.to
  • subject
  • type

Rule body

type.inbound

// Suspicious recipient pattern
and any(recipients.to, .display_name == "Undisclosed recipients")
and strings.ilike(body.current_thread.text,
                  "*shared a file with you*",
                  "*shared with you*",
                  "*invited you to access a file*"
)
and strings.icontains(subject.subject, "shared")
and any(body.links, .href_url.domain.root_domain == "sharepoint.com")

Detection logic

Scope: inbound message.

This rule detects messages originating from sharepoint.com with undisclosed recipients that are attempting to solicit the user to click a link. This has been observed in the event of an account compromise where the compromised account was utilizing legitimate file sharing services to share malicious links.

  1. inbound message
  2. any of recipients.to where:
    • .display_name is 'Undisclosed recipients'
  3. body.current_thread.text matches any of 3 patterns
    • *shared a file with you*
    • *shared with you*
    • *invited you to access a file*
  4. subject.subject contains 'shared'
  5. any of body.links where:
    • .href_url.domain.root_domain is 'sharepoint.com'

Inspects: body.current_thread.text, body.links, body.links[].href_url.domain.root_domain, recipients.to, recipients.to[].display_name, subject.subject, type.inbound. Sensors: strings.icontains, strings.ilike.

Indicators matched (6)

FieldMatchValue
recipients.to[].display_nameequalsUndisclosed recipients
strings.ilikesubstring*shared a file with you*
strings.ilikesubstring*shared with you*
strings.ilikesubstring*invited you to access a file*
strings.icontainssubstringshared
body.links[].href_url.domain.root_domainequalssharepoint.com

Stages and Predicates

Stage 1: mql_rule

and
  or
    body.current_thread.text match "invited you to access a file"
    body.current_thread.text match "shared a file with you"
    body.current_thread.text match "shared with you"
  any(body.links)
    body.links.href_url.domain.root_domain eq "sharepoint.com"
  any(recipients.to)
    recipients.to.display_name eq "Undisclosed recipients"
  subject.subject contains "shared"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textwildcard
  • *invited you to access a file*
  • *shared a file with you*
  • *shared with you*
field:"body.current_thread.text" kind:wildcard
subject.subjectcontains
  • shared
field:"subject.subject" kind:contains value:"shared"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Subject and sender display name contains matching long alphanumeric string

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

Detects messages where both the subject line and sender display name contain identical alphanumeric strings that are between 32 and 64 characters, which may indicate automated generation or coordination between these fields for malicious purposes.

Threat classification

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

CategoryValues
Attack typesMalware/Ransomware, Credential Phishing
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • subject
  • type

Rule body

type.inbound
and any(regex.iextract(subject.subject,
                       '[^a-z0-9](?P<string>[a-z0-9]{32,64})(?:$|[^a-z0-9])'
        ),
        strings.contains(sender.display_name, .named_groups["string"])
)

Detection logic

Scope: inbound message.

Detects messages where both the subject line and sender display name contain identical alphanumeric strings that are between 32 and 64 characters, which may indicate automated generation or coordination between these fields for malicious purposes.

  1. inbound message
  2. any of regex.iextract(subject.subject) where:
    • strings.contains(sender.display_name)

Inspects: sender.display_name, subject.subject, type.inbound. Sensors: regex.iextract, strings.contains.

Indicators matched (1)

FieldMatchValue
regex.iextractregex[^a-z0-9](?P<string>[a-z0-9]{32,64})(?:$|[^a-z0-9])

Stages and Predicates

Stage 1: mql_rule

and
  any(regex.iextract(subject.subject))
    strings.contains func_call "strings.contains(sender.display_name)"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Suspicious attachment: Duplicate decoy PDF files

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

This rule identifies messages that contain duplicate PDF attachments, defined as either having identical filenames or matching MD5 hash values. Furthermore, the PDF files in question must lack any readable text and must not include hyperlinks.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, PDF

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • attachments
  • body
  • type

Rule body

type.inbound
and length(attachments) > 1
and all(attachments, .file_type == "pdf")
and (
  length(distinct(attachments, .file_name)) == 1
  or length(distinct(attachments, .md5)) == 1
)
and all(attachments,
        .file_type == "pdf"
        and all(file.explode(.),
                (
                  length(.scan.url.urls) == 0
                  or length(.scan.pdf.urls) == 0
                  or length(body.links) == 0
                )
                and .scan.ocr.raw is null
        )
)

Detection logic

Scope: inbound message.

This rule identifies messages that contain duplicate PDF attachments, defined as either having identical filenames or matching MD5 hash values. Furthermore, the PDF files in question must lack any readable text and must not include hyperlinks.

  1. inbound message
  2. length(attachments) > 1
  3. all of attachments where:
    • .file_type is 'pdf'
  4. any of:
    • length(distinct(attachments, .file_name)) is 1
    • length(distinct(attachments, .md5)) is 1
  5. all of attachments where all hold:
    • .file_type is 'pdf'
    • all of file.explode(.) where all hold:
      • any of:
        • length(.scan.url.urls) is 0
        • length(.scan.pdf.urls) is 0
        • length(body.links) is 0
      • .scan.ocr.raw is missing

Inspects: attachments[].file_name, attachments[].file_type, attachments[].md5, body.links, type.inbound. Sensors: file.explode.

Indicators matched (1)

FieldMatchValue
attachments[].file_typeequalspdf

Stages and Predicates

Stage 1: mql_rule

and
  or
    distinct(attachments, .file_name) length_compare "1"
    distinct(attachments, .md5) length_compare "1"
  attachments length_compare "1"
  type.inbound eq "true"
   macro "all(attachments)"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Suspicious recipient pattern and language with low reputation link to login

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

Message contains a suspicious recipient pattern, financial or urgent language, and a suspicious link, with a login page and confusable characters or multiple redirects.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • body.links
  • recipients
  • recipients.cc
  • recipients.to
  • sender.email
  • type

Rule body

type.inbound
and 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
          )
          or .href_url.domain.tld in $suspicious_tlds
        )
        and (
          ml.link_analysis(.).credphish.contains_login
          and (
            ml.link_analysis(.).final_dom.display_text != strings.replace_confusables(ml.link_analysis(.
                                                                                      ).final_dom.display_text
            )
            or length(ml.link_analysis(.).redirect_history) > 1
          )
        )
)
and any(ml.nlu_classifier(body.current_thread.text).entities,
        .name in ("financial", "urgency")
)
and (
  (
    length(recipients.to) == 0
    or length(recipients.bcc) > 0
    or (
      all(recipients.to, .email.domain.valid == false)
      and all(recipients.cc, .email.domain.valid == false)
    )
  )
)
and all(body.links,
        .href_url.domain.root_domain != sender.email.domain.root_domain
)

Detection logic

Scope: inbound message.

Message contains a suspicious recipient pattern, financial or urgent language, and a suspicious link, with a login page and confusable characters or multiple redirects.

  1. inbound message
  2. any of body.links where all hold:
    • any of:
      • all of:
        • .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
      • .href_url.domain.tld in $suspicious_tlds
    • all of:
      • ml.link_analysis(.).credphish.contains_login
      • any of:
        • ml.link_analysis(.).final_dom.display_text is not strings.replace_confusables(ml.link_analysis(.).final_dom.display_text)
        • length(ml.link_analysis(.).redirect_history) > 1
  3. any of ml.nlu_classifier(body.current_thread.text).entities where:
    • .name in ('financial', 'urgency')
  4. any of:
    • length(recipients.to) is 0
    • length(recipients.bcc) > 0
    • all of:
      • all of recipients.to where:
        • .email.domain.valid is False
      • all of recipients.cc where:
        • .email.domain.valid is False
  5. all of body.links where:
    • .href_url.domain.root_domain is not sender.email.domain.root_domain

Inspects: body.current_thread.text, body.links, body.links[].href_url.domain.root_domain, body.links[].href_url.domain.tld, body.links[].href_url.domain.valid, recipients.bcc, recipients.cc, recipients.cc[].email.domain.valid, recipients.to, recipients.to[].email.domain.valid, sender.email.domain.root_domain, type.inbound. Sensors: ml.link_analysis, ml.nlu_classifier, strings.replace_confusables. Reference lists: $high_trust_sender_root_domains, $org_domains, $suspicious_tlds, $tranco_1m.

Indicators matched (2)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).entities[].namememberfinancial
ml.nlu_classifier(body.current_thread.text).entities[].namememberurgency

Stages and Predicates

Stage 1: mql_rule

and
  any(body.links)
    and
      or
        and
          body.links.href_url.domain.valid eq "true"
           macro "body.links[].href_url.domain.root_domain not in high_trust_sender_root_domains"
           macro "body.links[].href_url.domain.root_domain not in org_domains"
           macro "body.links[].href_url.domain.root_domain not in tranco_1m"
         macro "body.links[].href_url.domain.tld in suspicious_tlds"
      or
        ml.link_analysis func_call "ml.link_analysis(body.links[]).final_dom.display_text != strings.replace_confusables(ml.link_analysis(body.links[]).final_dom.display_text)"
        ml.link_analysis(body.links[]).redirect_history length_compare "1"
      ml.link_analysis func_call "ml.link_analysis(body.links[]).credphish.contains_login"
  or
    and
       macro "all(recipients.cc)"
       macro "all(recipients.to)"
    recipients.bcc length_compare "0"
    recipients.to length_compare "0"
  any(ml.nlu_classifier(body.current_thread.text).entities)
    ml.nlu_classifier(body.current_thread.text).entities.name in ["financial", "urgency"]
  type.inbound eq "true"
   macro "all(body.links)"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Suspicious request for financial information

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

Email is from a suspicious sender and contains a request for financial information, such as AR reports.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesFree email provider, Impersonation: Employee, Impersonation: VIP, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • body.links
  • headers
  • headers.auth_summary
  • headers.reply_to
  • recipients
  • recipients.to
  • sender.email
  • subject
  • type

Rule body

type.inbound
and length(attachments) <= 1
and length(recipients.to) <= 2
// suspicious sender
and (
  (
    length(headers.reply_to) > 0
    and all(headers.reply_to,
            .email.domain.root_domain != sender.email.domain.root_domain
            and .email.domain.root_domain not in $org_domains
    )
  )
  or sender.email.domain.root_domain in $free_email_providers
  or profile.by_sender().days_known < 3
)
// specific financial language
and (
  regex.icontains(subject.subject,
                  '\b(Aged|Age?ing) (Payables|Receivables|Report)',
                  'reconcill?iation (report|statement).*(issued (settlement|advice)s?)|billing records?'
  )
  or (
    regex.icontains(body.current_thread.text,
                    '\b(Aged|Age?ing) (?:Collection|Payables|Receivables|Report)',
                    '(latest|updated|recent) (\bAR\b|\b\AP\b|\bAR\b \& \bAP\b|accounts?) (?:Aging|Payables|Receivables|Reports)',
                    '(shared?|send|forward|provide).*remittance (advice|receipts?|statements?)'
    )
    or strings.icontains(body.current_thread.text,
                         "copy of a current statement"
    )
    or (
      strings.icontains(body.current_thread.text, "please send all past due")
      and strings.icontains(body.current_thread.text, "current invoices")
    )
  )
  // suspicious link display text
  or (
    any(body.links,
        regex.icontains(.display_text,
                        '(Payment|Remittance|Settlement|Transfer) ?Batch',
        )
    )
  )
  // suspicious sender display name
  or (
    regex.icontains(sender.display_name,
                    'Accounts? (?:Payable (?:Dep(\.|t\.?|artment)|e?Receipt)|(Co[[:punct:]]?ordinator|Admin|Manager|Payee))'
    )
    // sender email listed as a recipient or recipients undisclosed/null
    and (
      (
        sender.email.email in map(recipients.to, .email.email)
        or (length(recipients.to) == 0 or length(recipients.to) is null)
      )
      // non-benign nlu intent
      or any(ml.nlu_classifier(body.current_thread.text).intents,
             .name != "benign"
      )
    )
  )
  or (
    any(ml.nlu_classifier(body.current_thread.text).intents,
        .name == "cred_theft" and .confidence == "high"
    )
    and any(ml.nlu_classifier(body.current_thread.text).entities,
            .name == "financial" and .text =~ "remittance"
    )
  )
)
// negate resume related/job inquiry outreach
and not (
  any(ml.nlu_classifier(body.current_thread.text).topics,
      .name == "Professional and Career Development" and .confidence == "high"
  )
  and any(ml.nlu_classifier(body.current_thread.text).intents,
          .name == "benign" and .confidence != "low"
  )
)
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.

Email is from a suspicious sender and contains a request for financial information, such as AR reports.

  1. inbound message
  2. length(attachments) ≤ 1
  3. length(recipients.to) ≤ 2
  4. any of:
    • all of:
      • length(headers.reply_to) > 0
      • all of headers.reply_to where all hold:
        • .email.domain.root_domain is not sender.email.domain.root_domain
        • .email.domain.root_domain not in $org_domains
    • sender.email.domain.root_domain in $free_email_providers
    • profile.by_sender().days_known < 3
  5. any of:
    • subject.subject matches any of 2 patterns
      • \b(Aged|Age?ing) (Payables|Receivables|Report)
      • reconcill?iation (report|statement).*(issued (settlement|advice)s?)|billing records?
    • any of:
      • body.current_thread.text matches any of 3 patterns
        • \b(Aged|Age?ing) (?:Collection|Payables|Receivables|Report)
        • (latest|updated|recent) (\bAR\b|\b\AP\b|\bAR\b \& \bAP\b|accounts?) (?:Aging|Payables|Receivables|Reports)
        • (shared?|send|forward|provide).*remittance (advice|receipts?|statements?)
      • body.current_thread.text contains 'copy of a current statement'
      • all of:
        • body.current_thread.text contains 'please send all past due'
        • body.current_thread.text contains 'current invoices'
    • any of body.links where:
      • .display_text matches '(Payment|Remittance|Settlement|Transfer) ?Batch'
    • all of:
      • sender.display_name matches 'Accounts? (?:Payable (?:Dep(\\.|t\\.?|artment)|e?Receipt)|(Co[[:punct:]]?ordinator|Admin|Manager|Payee))'
      • any of:
        • any of:
          • sender.email.email in map(recipients.to, .email.email)
          • any of:
            • length(recipients.to) is 0
            • length(recipients.to) is missing
        • any of ml.nlu_classifier(body.current_thread.text).intents where:
          • .name is not 'benign'
    • all of:
      • any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
        • .name is 'cred_theft'
        • .confidence is 'high'
      • any of ml.nlu_classifier(body.current_thread.text).entities where all hold:
        • .name is 'financial'
        • .text is 'remittance'
  6. not:
    • all of:
      • any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
        • .name is 'Professional and Career Development'
        • .confidence is 'high'
      • any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
        • .name is 'benign'
        • .confidence is not 'low'
  7. 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
  8. not:
    • profile.by_sender().any_messages_benign

Inspects: body.current_thread.text, body.links, body.links[].display_text, headers.auth_summary.dmarc.pass, headers.reply_to, headers.reply_to[].email.domain.root_domain, recipients.to, recipients.to[].email.email, sender.display_name, sender.email.domain.root_domain, sender.email.email, subject.subject, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, regex.icontains, strings.icontains. Reference lists: $free_email_providers, $high_trust_sender_root_domains, $org_domains.

Indicators matched (14)

FieldMatchValue
regex.icontainsregex\b(Aged|Age?ing) (Payables|Receivables|Report)
regex.icontainsregexreconcill?iation (report|statement).*(issued (settlement|advice)s?)|billing records?
regex.icontainsregex\b(Aged|Age?ing) (?:Collection|Payables|Receivables|Report)
regex.icontainsregex(latest|updated|recent) (\bAR\b|\b\AP\b|\bAR\b \& \bAP\b|accounts?) (?:Aging|Payables|Receivables|Reports)
regex.icontainsregex(shared?|send|forward|provide).*remittance (advice|receipts?|statements?)
strings.icontainssubstringcopy of a current statement
strings.icontainssubstringplease send all past due
strings.icontainssubstringcurrent invoices
regex.icontainsregex(Payment|Remittance|Settlement|Transfer) ?Batch
regex.icontainsregexAccounts? (?:Payable (?:Dep(\.|t\.?|artment)|e?Receipt)|(Co[[:punct:]]?ordinator|Admin|Manager|Payee))
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscred_theft
ml.nlu_classifier(body.current_thread.text).intents[].confidenceequalshigh
2 more
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsfinancial
ml.nlu_classifier(body.current_thread.text).entities[].textequalsremittance

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      any(ml.nlu_classifier(body.current_thread.text).entities)
        and
          ml.nlu_classifier(body.current_thread.text).entities.name eq "financial"
          ml.nlu_classifier(body.current_thread.text).entities.text eq "remittance"
      any(ml.nlu_classifier(body.current_thread.text).intents)
        and
          ml.nlu_classifier(body.current_thread.text).intents.confidence eq "high"
          ml.nlu_classifier(body.current_thread.text).intents.name eq "cred_theft"
    and
      or
        any(ml.nlu_classifier(body.current_thread.text).intents)
          ml.nlu_classifier(body.current_thread.text).intents.name ne "benign"
        length(recipients.to) is_null
        recipients.to length_compare "0"
         macro "sender.email.email in map(recipients.to, .email.email)"
      sender.display_name regex_match "Accounts? (?:Payable (?:Dep(\\.|t\\.?|artment)|e?Receipt)|(Co[[:punct:]]?ordinator|Admin|Manager|Payee))"
    and
      body.current_thread.text contains "current invoices"
      body.current_thread.text contains "please send all past due"
    any(body.links)
      body.links.display_text regex_match "(Payment|Remittance|Settlement|Transfer) ?Batch"
    body.current_thread.text contains "copy of a current statement"
    body.current_thread.text regex_match "(latest|updated|recent) (\\bAR\\b|\\b\\AP\\b|\\bAR\\b \\& \\bAP\\b|accounts?) (?:Aging|Payables|Receivables|Reports)"
    body.current_thread.text regex_match "(shared?|send|forward|provide).*remittance (advice|receipts?|statements?)"
    body.current_thread.text regex_match "\\b(Aged|Age?ing) (?:Collection|Payables|Receivables|Report)"
    subject.subject regex_match "\\b(Aged|Age?ing) (Payables|Receivables|Report)"
    subject.subject regex_match "reconcill?iation (report|statement).*(issued (settlement|advice)s?)|billing records?"
  not
    and
      any(ml.nlu_classifier(body.current_thread.text).intents)
        and
          ml.nlu_classifier(body.current_thread.text).intents.confidence ne "low"
          ml.nlu_classifier(body.current_thread.text).intents.name eq "benign"
      any(ml.nlu_classifier(body.current_thread.text).topics)
        and
          ml.nlu_classifier(body.current_thread.text).topics.confidence eq "high"
          ml.nlu_classifier(body.current_thread.text).topics.name eq "Professional and Career Development"
  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"
  or
    and
      headers.reply_to length_compare "0"
       macro "all(headers.reply_to)"
    profile.by_sender func_call "profile.by_sender().days_known < 3"
     macro "sender.email.domain.root_domain in free_email_providers"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  attachments length_compare "1"
  recipients.to length_compare "2"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
ml.nlu_classifier(body.current_thread.text).intentsarray_any(no value, null check)excludes:ml.nlu_classifier(body.current_thread.text).intents
ml.nlu_classifier(body.current_thread.text).topicsarray_any(no value, null check)excludes:ml.nlu_classifier(body.current_thread.text).topics

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textcontains
  • copy of a current statement
  • current invoices
  • please send all past due
field:"body.current_thread.text" kind:contains
body.current_thread.textregex_match
  • (latest|updated|recent) (\bAR\b|\b\AP\b|\bAR\b \& \bAP\b|accounts?) (?:Aging|Payables|Receivables|Reports)
  • (shared?|send|forward|provide).*remittance (advice|receipts?|statements?)
  • \b(Aged|Age?ing) (?:Collection|Payables|Receivables|Report)
field:"body.current_thread.text" kind:regex_match
length(recipients.to)is_null
  • (no value, null check)
field:"length(recipients.to)" kind:is_null
sender.display_nameregex_match
  • Accounts? (?:Payable (?:Dep(.|t.?|artment)|e?Receipt)|(Co[[:punct:]]?ordinator|Admin|Manager|Payee))
field:"sender.display_name" kind:regex_match value:"Accounts? (?:Payable (?:Dep(.|t.?|artment)|e?Receipt)|(Co[[:punct:]]?ordinator|Admin|Manager|Payee))"
subject.subjectregex_match
  • \b(Aged|Age?ing) (Payables|Receivables|Report)
    • reconcill?iation (report|statement).*(issued (settlement|advice)s?)
    • billing records?
field:"subject.subject" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Suspicious sender display name with long procedurally generated text blob

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

This rule identifies sender display names containing long strings of nonsensical or procedurally generated characters, which are often used in phishing or spam campaigns for campaign tracking and identification, as well as to bypass detection filters.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule body

type.inbound
and regex.icontains(sender.display_name, '\b[\w\p{L}\p{N}]{35,}\b')
and not regex.icontains(sender.display_name, '_bot_[a-f0-9]{32}\)')
and not regex.match(sender.display_name, '\b[\w\p{L}\p{N}]{35,}\b')
and not (sender.email.email == "" or sender.email.domain.valid == false)
// negate org domains unless they fail DMARC authentication
and (
  (
    sender.email.domain.root_domain in $org_domains
    and not headers.auth_summary.dmarc.pass
  )
  or sender.email.domain.root_domain not in $org_domains
)

// 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_email().any_messages_benign

Detection logic

Scope: inbound message.

This rule identifies sender display names containing long strings of nonsensical or procedurally generated characters, which are often used in phishing or spam campaigns for campaign tracking and identification, as well as to bypass detection filters.

  1. inbound message
  2. sender.display_name matches '\\b[\\w\\p{L}\\p{N}]{35,}\\b'
  3. not:
    • sender.display_name matches '_bot_[a-f0-9]{32}\\)'
  4. not:
    • sender.display_name matches '\\b[\\w\\p{L}\\p{N}]{35,}\\b'
  5. none of:
    • sender.email.email is ''
    • sender.email.domain.valid is False
  6. any of:
    • all of:
      • sender.email.domain.root_domain in $org_domains
      • not:
        • headers.auth_summary.dmarc.pass
    • sender.email.domain.root_domain not in $org_domains
  7. 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
  8. not:
    • profile.by_sender_email().any_messages_benign

Inspects: headers.auth_summary.dmarc.pass, sender.display_name, sender.email.domain.root_domain, sender.email.domain.valid, sender.email.email, type.inbound. Sensors: profile.by_sender_email, regex.icontains, regex.match. Reference lists: $high_trust_sender_root_domains, $org_domains.

Indicators matched (1)

FieldMatchValue
regex.icontainsregex\b[\w\p{L}\p{N}]{35,}\b

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"
  or
    and
      not
        headers.auth_summary.dmarc.pass eq "true"
       macro "sender.email.domain.root_domain in org_domains"
     macro "sender.email.domain.root_domain not in org_domains"
  not
    or
      sender.email.domain.valid eq "false"
      sender.email.email eq ""
  not
    profile.by_sender_email func_call "profile.by_sender_email().any_messages_benign"
  not
    sender.display_name regex_match "\\b[\\w\\p{L}\\p{N}]{35,}\\b"
  not
    sender.display_name regex_match "_bot_[a-f0-9]{32}\\)"
  sender.display_name regex_match "\\b[\\w\\p{L}\\p{N}]{35,}\\b"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

Indicators

These rows show field, operator, and value matches.

Suspicious SharePoint file sharing

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

This rule detect potential credential phishing leveraging SharePoint file sharing to deliver a PDF, OneNote, or Unknown file type file using indicators such as suspicious sender analysis and link characteristics.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesFree email provider, Free file host, OneNote, PDF

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • body.links
  • headers
  • headers.reply_to
  • sender.email
  • subject
  • type

Rule body

type.inbound

// Matches the message id observed. DKIM/SPF domains can be custom and therefore are unpredictable.
and strings.starts_with(headers.message_id, '<Share-')
and strings.ends_with(headers.message_id, '@odspnotify>')

// SharePoint email indicators
and strings.like(body.current_thread.text,
                 "*shared a file with you*",
                 "*shared with you*",
                 "*invited you to access a file*"
)
and strings.icontains(subject.subject, "shared")

// sender analysis 
and (
  (
    // if the sender is not the sharepointonline.com, we can use the sender email
    // to see if it is a solicited email
    sender.email.domain.domain != "sharepointonline.com"
    and not profile.by_sender().solicited
  )
  // if it is the sharepointonline sender, use the reply-to header
  or (
    sender.email.domain.domain =~ "sharepointonline.com"
    and length(headers.reply_to) > 0
    and 
    // a newly created domain
    (
      all(headers.reply_to,
          .email.domain.root_domain not in $free_email_providers
          and network.whois(.email.domain).days_old <= 30
          and .email.email != sender.email.email
      )

      // is a free email provider
      or all(headers.reply_to,
             .email.domain.root_domain in $free_email_providers
      )

      //
      // This rule makes use of a beta feature and is subject to change without notice
      // using the beta feature in custom rules is not suggested until it has been formally released
      //
      
      // no outbound emails 
      or not beta.profile.by_reply_to().solicited
    )
    // do not match if the reply_to address has been observed as a reply_to address
    // of a message that has been classified as benign
    and not beta.profile.by_reply_to().any_messages_benign
  )
)
// link logic
and any(body.links,
        .href_url.domain.root_domain == "sharepoint.com"
        // it is a personal share
        and (
          // /g/ is only found with /personal
          strings.icontains(.href_url.path, '/g/personal/')
          or strings.icontains(.href_url.path, '/p/')
        )
        // it is either a OneNote or PDF
        and (
          strings.icontains(.href_url.path, '/:o:/')
          or strings.icontains(.href_url.path, '/:b:/')
          or strings.icontains(.href_url.path, '/:u:/')
        )
)

Detection logic

Scope: inbound message.

This rule detect potential credential phishing leveraging SharePoint file sharing to deliver a PDF, OneNote, or Unknown file type file using indicators such as suspicious sender analysis and link characteristics.

  1. inbound message
  2. headers.message_id starts with '<Share-'
  3. headers.message_id ends with '@odspnotify>'
  4. body.current_thread.text matches any of 3 patterns
    • *shared a file with you*
    • *shared with you*
    • *invited you to access a file*
  5. subject.subject contains 'shared'
  6. any of:
    • all of:
      • sender.email.domain.domain is not 'sharepointonline.com'
      • not:
        • profile.by_sender().solicited
    • all of:
      • sender.email.domain.domain is 'sharepointonline.com'
      • length(headers.reply_to) > 0
      • any of:
        • all of headers.reply_to where all hold:
          • .email.domain.root_domain not in $free_email_providers
          • network.whois(.email.domain).days_old ≤ 30
          • .email.email is not sender.email.email
        • all of headers.reply_to where:
          • .email.domain.root_domain in $free_email_providers
        • not:
          • beta.profile.by_reply_to().solicited
      • not:
        • beta.profile.by_reply_to().any_messages_benign
  7. any of body.links where all hold:
    • .href_url.domain.root_domain is 'sharepoint.com'
    • any of:
      • .href_url.path contains '/g/personal/'
      • .href_url.path contains '/p/'
    • any of:
      • .href_url.path contains '/:o:/'
      • .href_url.path contains '/:b:/'
      • .href_url.path contains '/:u:/'

Inspects: body.current_thread.text, body.links, body.links[].href_url.domain.root_domain, body.links[].href_url.path, headers.message_id, headers.reply_to, headers.reply_to[].email.domain, headers.reply_to[].email.domain.root_domain, headers.reply_to[].email.email, sender.email.domain.domain, sender.email.email, subject.subject, type.inbound. Sensors: beta.profile.by_reply_to, network.whois, profile.by_sender, strings.ends_with, strings.icontains, strings.like, strings.starts_with. Reference lists: $free_email_providers.

Indicators matched (13)

FieldMatchValue
strings.starts_withprefix<Share-
strings.ends_withsuffix@odspnotify>
strings.likesubstring*shared a file with you*
strings.likesubstring*shared with you*
strings.likesubstring*invited you to access a file*
strings.icontainssubstringshared
sender.email.domain.domainequalssharepointonline.com
body.links[].href_url.domain.root_domainequalssharepoint.com
strings.icontainssubstring/g/personal/
strings.icontainssubstring/p/
strings.icontainssubstring/:o:/
strings.icontainssubstring/:b:/
1 more
strings.icontainssubstring/:u:/

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        not
          beta.profile.by_reply_to func_call "beta.profile.by_reply_to().solicited"
         macro "all(headers.reply_to)"
      not
        beta.profile.by_reply_to func_call "beta.profile.by_reply_to().any_messages_benign"
      headers.reply_to length_compare "0"
      sender.email.domain.domain eq "sharepointonline.com"
    and
      not
        profile.by_sender func_call "profile.by_sender().solicited"
      sender.email.domain.domain ne "sharepointonline.com"
  any(body.links)
    and
      or
        body.links.href_url.path contains "/:b:/"
        body.links.href_url.path contains "/:o:/"
        body.links.href_url.path contains "/:u:/"
      or
        body.links.href_url.path contains "/g/personal/"
        body.links.href_url.path contains "/p/"
      body.links.href_url.domain.root_domain eq "sharepoint.com"
  or
    body.current_thread.text match "invited you to access a file"
    body.current_thread.text match "shared a file with you"
    body.current_thread.text match "shared with you"
  headers.message_id ends_with "@odspnotify>"
  headers.message_id starts_with "<Share-"
  subject.subject contains "shared"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Suspicious subject with long procedurally generated text blob

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

This rule identifies subjects containing long strings of nonsensical or procedurally generated characters, which are often used in phishing or spam campaigns for campaign tracking and identification, as well as to bypass detection filters.

Threat classification

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

CategoryValues
Attack typesCredential Phishing, Spam
Tactics and techniquesEvasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • recipients
  • recipients.to
  • sender.email
  • subject
  • type

Rule body

type.inbound
// "#" symbol, and then letters, numbers or more symbols (-#)
and regex.imatch(subject.subject, ".*#[a-z0-9-#]+$")
// not JUST letters
and not regex.imatch(subject.subject, ".*#[a-z-]+$")
// not JUST numbers
and not regex.imatch(subject.subject, ".*#[0-9-]+$")
and not any(beta.ml_topic(body.current_thread.text).topics,
            .name in (
              "Professional and Career Development",
              "Reminders and Notifications"
            )
            and .confidence == "high"
)

// and 1 other suspicious element
and 2 of (
  any(recipients.to, strings.icontains(sender.display_name, .email.domain.sld)),
  (
    regex.imatch(sender.display_name, ".*#[a-z0-9-#]+$")
    and not regex.imatch(subject.subject, ".*#[a-z-]+$")
    and not regex.imatch(subject.subject, ".*#[0-9-]+$")
  ),
  any(ml.nlu_classifier(subject.subject).intents,
      .name == "cred_theft" and .confidence == "high"
  ),
  body.current_thread.text is null
)

// standard negations
and not profile.by_sender_email().any_messages_benign
and not profile.by_sender_email().solicited
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.

This rule identifies subjects containing long strings of nonsensical or procedurally generated characters, which are often used in phishing or spam campaigns for campaign tracking and identification, as well as to bypass detection filters.

  1. inbound message
  2. subject.subject matches '.*#[a-z0-9-#]+$'
  3. not:
    • subject.subject matches '.*#[a-z-]+$'
  4. not:
    • subject.subject matches '.*#[0-9-]+$'
  5. not:
    • any of beta.ml_topic(body.current_thread.text).topics where all hold:
      • .name in ('Professional and Career Development', 'Reminders and Notifications')
      • .confidence is 'high'
  6. at least 2 of:
    • any of recipients.to where:
      • strings.icontains(sender.display_name)
    • all of:
      • sender.display_name matches '.*#[a-z0-9-#]+$'
      • not:
        • subject.subject matches '.*#[a-z-]+$'
      • not:
        • subject.subject matches '.*#[0-9-]+$'
    • any of ml.nlu_classifier(subject.subject).intents where all hold:
      • .name is 'cred_theft'
      • .confidence is 'high'
    • body.current_thread.text is missing
  7. not:
    • profile.by_sender_email().any_messages_benign
  8. not:
    • profile.by_sender_email().solicited
  9. 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, headers.auth_summary.dmarc.pass, recipients.to, recipients.to[].email.domain.sld, sender.display_name, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: beta.ml_topic, ml.nlu_classifier, profile.by_sender_email, regex.imatch, strings.icontains. Reference lists: $high_trust_sender_root_domains.

Indicators matched (3)

FieldMatchValue
regex.imatchregex.*#[a-z0-9-#]+$
ml.nlu_classifier(subject.subject).intents[].nameequalscred_theft
ml.nlu_classifier(subject.subject).intents[].confidenceequalshigh

Stages and Predicates

Stage 1: mql_rule

and
  not
    any(beta.ml_topic(body.current_thread.text).topics)
      and
        beta.ml_topic(body.current_thread.text).topics.confidence eq "high"
        beta.ml_topic(body.current_thread.text).topics.name in ["Professional and Career Development", "Reminders and Notifications"]
  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"
  or
    any(ml.nlu_classifier(subject.subject).intents)
      and
        ml.nlu_classifier(subject.subject).intents.confidence eq "high"
        ml.nlu_classifier(subject.subject).intents.name eq "cred_theft"
    and
      not
        subject.subject regex_match ".*#[0-9-]+$"
      not
        subject.subject regex_match ".*#[a-z-]+$"
      sender.display_name regex_match ".*#[a-z0-9-#]+$"
    any(recipients.to)
      strings.icontains func_call "strings.icontains(sender.display_name)"
    body.current_thread.text is_null
  not
    profile.by_sender_email func_call "profile.by_sender_email().any_messages_benign"
  not
    profile.by_sender_email func_call "profile.by_sender_email().solicited"
  not
    subject.subject regex_match ".*#[0-9-]+$"
  not
    subject.subject regex_match ".*#[a-z-]+$"
  subject.subject regex_match ".*#[a-z0-9-#]+$"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
beta.ml_topic(body.current_thread.text).topicsarray_any(no value, null check)excludes:beta.ml_topic(body.current_thread.text).topics
subject.subjectregex_match.*#[0-9-]+$excludes:subject.subject field:"subject.subject" value:".*#[0-9-]+$"
subject.subjectregex_match.*#[a-z-]+$excludes:subject.subject field:"subject.subject" value:".*#[a-z-]+$"

Indicators

These rows show field, operator, and value matches.