Detection rules › Sublime MQL

Sublime MQL rules: body

RuleSeverity
Advance Fee Fraud (AFF) from freemail provider or suspicious TLDmedium
Attachment: Encrypted zip file with payment-related luremedium
BEC/Fraud: Fake investment outreach from suspicious TLDmedium
BEC/Fraud: Job scam fake thread or plaintext pivot to freemailmedium
BEC: Financial fraud from newly registered sender domainmedium
Body: CSS clamp() font obfuscation with IP-based linksmedium
Body: CSS Hidden text via clip-pathmedium
Body: CSS zero-value calc() obfuscationmedium
Body: Fake secure email portal with HTML obfuscationhigh
Body: HTML whitespace stuffing with short initial messagemedium
Body: Invisible Unicode obfuscation student loan callback phishingmedium
Body: Suspicious date formatmedium
Body: Suspicious table template fingerprintmedium
Body: Yellow highlighted text markerslow
Brand impersonation: Microsoft logo or suspicious language with open redirecthigh
Business Email Compromise (BEC) attempt from unsolicited sendermedium
Business Email Compromise (BEC) attempt from untrusted sendermedium
Business Email Compromise (BEC) with request for mobile numbermedium
Business Email Compromise: Request for mobile number via reply thread hijackingmedium
Callback phishing solicitation in message bodymedium
COVID-19 themed fraud with sender and reply-to mismatch or compensation awardmedium
Credential theft with 'safe content' deception and social engineering topicsmedium
CVE-2023-5631 - Roundcube Webmail XSS via crafted SVGcritical
Extortion / sextortion (untrusted sender)low
Honorific greeting BEC attempt with sender and reply-to mismatchlow
HTML smuggling with atob in message bodyhigh
HTML: Template placeholders or recipient email in element class attributeshigh
Investor solicitation with organization targetingmedium
Job scam (unsolicited sender)low
Link: Self-sent PDF lure with subject correlationmedium
Message content: Request for author engagementlow
Self-impersonation: Sender matches recipient with bolded name and suspicious linkmedium
URL with Unicode U+2044 (⁄) or U+2215 (∕) characterslow

Advance Fee Fraud (AFF) from freemail provider or suspicious TLD

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

Advance Fee Fraud (AFF) is a type of BEC/Fraud involving upfront fees for promised future returns, such as lottery scams, inheritance payouts, and investment opportunities. This rule identifies messages from Freemail domains or suspicious TLDS, including those with suspicious reply-to addresses. It utilizes Natural Language Understanding to detect AFF language in their contents.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and (
  sender.email.domain.domain in $free_email_providers
  or (
    length(headers.reply_to) > 0
    and all(headers.reply_to,
            (
              .email.domain.root_domain in $free_email_providers
              or .email.domain.tld in $suspicious_tlds
              or (
                network.whois(.email.domain).days_old < 365
                and length(coalesce(body.html.raw, "")) == 0
              )
            )
            and .email.email != sender.email.email
    )
  )
  or sender.email.domain.tld in $suspicious_tlds
  or any(["jp", "jo"], strings.iends_with(sender.email.domain.tld, .))
  or (
    length(recipients.to) == 0
    and any(headers.reply_to,
            .email.domain.root_domain != sender.email.domain.root_domain
    )
  )
)
and (
  any(ml.nlu_classifier(body.current_thread.text).intents,
      .name == "advance_fee" and .confidence in ("medium", "high")
  )
  or (
    length(body.current_thread.text) < 200
    and regex.icontains(body.current_thread.text,
                        '(?:donation|inheritence|\$\d,\d{3}\,\d{3}|lottery)'
    )
    and not regex.icontains(body.current_thread.text,
                            '(?:closed.{0,50})?\$\d,\d{3}\,\d{3}.{0,100}(?:homes|realty|sale)?'
    )
    and not any(body.links,
                regex.icontains(.href_url.url,
                                '(?:donation|inheritence|\$\d,\d{3}\,\d{3}|lottery)'
                )
    )
    and (
      (
        (length(headers.references) > 0 or headers.in_reply_to is null)
        and not (
          (
            strings.istarts_with(subject.subject, "RE:")
            // out of office auto-reply
            or strings.istarts_with(subject.subject, "Automatic reply:")
            or strings.istarts_with(subject.subject, "R:")
            or strings.istarts_with(subject.subject, "ODG:")
            or strings.istarts_with(subject.subject, "答复:")
            or strings.istarts_with(subject.subject, "AW:")
            or strings.istarts_with(subject.subject, "TR:")
            or strings.istarts_with(subject.subject, "FWD:")
            or regex.icontains(subject.subject,
                               '^(\[[^\]]+\]\s?){0,3}(re|fwd?)\s?:'
            )
          )
        )
      )
      or any(headers.reply_to, .email.email != sender.email.email)
    )
  )
)
and (
  not profile.by_sender().solicited
  or profile.by_sender().any_messages_malicious_or_spam
)
and not profile.by_sender().any_messages_benign

Detection logic

Scope: inbound message.

Advance Fee Fraud (AFF) is a type of BEC/Fraud involving upfront fees for promised future returns, such as lottery scams, inheritance payouts, and investment opportunities. This rule identifies messages from Freemail domains or suspicious TLDS, including those with suspicious reply-to addresses. It utilizes Natural Language Understanding to detect AFF language in their contents.

  1. inbound message
  2. any of:
    • sender.email.domain.domain in $free_email_providers
    • all of:
      • length(headers.reply_to) > 0
      • all of headers.reply_to where all hold:
        • any of:
          • .email.domain.root_domain in $free_email_providers
          • .email.domain.tld in $suspicious_tlds
          • all of:
            • network.whois(.email.domain).days_old < 365
            • length(coalesce(body.html.raw, '')) is 0
        • .email.email is not sender.email.email
    • sender.email.domain.tld in $suspicious_tlds
    • any of ['jp', 'jo'] where:
      • strings.iends_with(sender.email.domain.tld)
    • all of:
      • length(recipients.to) is 0
      • any of headers.reply_to where:
        • .email.domain.root_domain is not sender.email.domain.root_domain
  3. any of:
    • any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
      • .name is 'advance_fee'
      • .confidence in ('medium', 'high')
    • all of:
      • length(body.current_thread.text) < 200
      • body.current_thread.text matches '(?:donation|inheritence|\\$\\d,\\d{3}\\,\\d{3}|lottery)'
      • not:
        • body.current_thread.text matches '(?:closed.{0,50})?\\$\\d,\\d{3}\\,\\d{3}.{0,100}(?:homes|realty|sale)?'
      • not:
        • any of body.links where:
          • .href_url.url matches '(?:donation|inheritence|\\$\\d,\\d{3}\\,\\d{3}|lottery)'
      • any of:
        • all of:
          • any of:
            • length(headers.references) > 0
            • headers.in_reply_to is missing
          • none of:
            • subject.subject starts with 'RE:'
            • subject.subject starts with 'Automatic reply:'
            • subject.subject starts with 'R:'
            • subject.subject starts with 'ODG:'
            • subject.subject starts with '答复:'
            • subject.subject starts with 'AW:'
            • subject.subject starts with 'TR:'
            • subject.subject starts with 'FWD:'
            • subject.subject matches '^(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?)\\s?:'
        • any of headers.reply_to where:
          • .email.email is not sender.email.email
  4. any of:
    • not:
      • profile.by_sender().solicited
    • profile.by_sender().any_messages_malicious_or_spam
  5. not:
    • profile.by_sender().any_messages_benign

Inspects: body.current_thread.text, body.html.raw, body.links, body.links[].href_url.url, headers.in_reply_to, headers.references, headers.reply_to, headers.reply_to[].email.domain, headers.reply_to[].email.domain.root_domain, headers.reply_to[].email.domain.tld, headers.reply_to[].email.email, recipients.to, sender.email.domain.domain, sender.email.domain.root_domain, sender.email.domain.tld, sender.email.email, subject.subject, type.inbound. Sensors: ml.nlu_classifier, network.whois, profile.by_sender, regex.icontains, strings.iends_with, strings.istarts_with. Reference lists: $free_email_providers, $suspicious_tlds.

Indicators matched (4)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).intents[].nameequalsadvance_fee
ml.nlu_classifier(body.current_thread.text).intents[].confidencemembermedium
ml.nlu_classifier(body.current_thread.text).intents[].confidencememberhigh
regex.icontainsregex(?:donation|inheritence|\$\d,\d{3}\,\d{3}|lottery)

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        and
          not
            or
              subject.subject regex_match "^(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?)\\s?:"
              subject.subject starts_with "AW:"
              subject.subject starts_with "Automatic reply:"
              subject.subject starts_with "FWD:"
              subject.subject starts_with "ODG:"
              subject.subject starts_with "R:"
              subject.subject starts_with "RE:"
              subject.subject starts_with "TR:"
              subject.subject starts_with "答复:"
          or
            headers.in_reply_to is_null
            headers.references length_compare "0"
        any(headers.reply_to)
          headers.reply_to.email.email cross_field_compare "sender.email.email"
      not
        any(body.links)
          body.links.href_url.url regex_match "(?:donation|inheritence|\\$\\d,\\d{3}\\,\\d{3}|lottery)"
      not
        body.current_thread.text regex_match "(?:closed.{0,50})?\\$\\d,\\d{3}\\,\\d{3}.{0,100}(?:homes|realty|sale)?"
      body.current_thread.text length_compare "200"
      body.current_thread.text regex_match "(?:donation|inheritence|\\$\\d,\\d{3}\\,\\d{3}|lottery)"
    any(ml.nlu_classifier(body.current_thread.text).intents)
      and
        ml.nlu_classifier(body.current_thread.text).intents.confidence in ["high", "medium"]
        ml.nlu_classifier(body.current_thread.text).intents.name eq "advance_fee"
  or
    and
      any(headers.reply_to)
        headers.reply_to.email.domain.root_domain cross_field_compare "sender.email.domain.root_domain"
      recipients.to length_compare "0"
    and
      headers.reply_to length_compare "0"
       macro "all(headers.reply_to)"
    any(['jp', 'jo'])
      strings.iends_with func_call "strings.iends_with(sender.email.domain.tld)"
     macro "sender.email.domain.domain in free_email_providers"
     macro "sender.email.domain.tld in suspicious_tlds"
  or
    not
      profile.by_sender func_call "profile.by_sender().solicited"
    profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Attachment: Encrypted zip file with payment-related lure

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

Detects messages containing zip file attachments with payment-themed content that reference encrypted files, passwords, and payment details. The rule looks for specific patterns indicating the attachment is encrypted and contains payment-related information, commonly used to evade security scanning by requiring manual extraction.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud, Malware/Ransomware
Tactics and techniquesEncryption, Evasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • attachments[0]
  • body
  • body.current_thread
  • subject
  • type

Rule body

type.inbound
and length(attachments) > 0
// 3 instances of zip/encrypted/payment information
and 3 of (
  regex.icontains(body.current_thread.text, 'zip file.{1,50}encrypted'),
  regex.icontains(body.current_thread.text, 'attachment.{1,30}encrypted'),
  regex.icontains(body.current_thread.text,
                  'password.{1,5}is.{1,5}[A-Z0-9]{8,}'
  ),
  regex.icontains(body.current_thread.text,
                  'details.{1,20}payment.{1,30}attach'
  ),
  strings.icontains(subject.subject, "you have received"),
  strings.icontains(subject.subject, "new debit"),
  strings.icontains(subject.subject, "payment confirmation"),
  strings.icontains(subject.subject, "invoice attached")
)
and (
  // one attachment included and the file is a zip
  attachments[0].file_extension == "zip"
  and (
    regex.icontains(attachments[0].file_name,
                    'payment|invoice|receipt|document|bank'
    )
    // long uppercase passwords
    or regex.contains(body.current_thread.text, '[A-Z]{10,}')
  )
)

Detection logic

Scope: inbound message.

Detects messages containing zip file attachments with payment-themed content that reference encrypted files, passwords, and payment details. The rule looks for specific patterns indicating the attachment is encrypted and contains payment-related information, commonly used to evade security scanning by requiring manual extraction.

  1. inbound message
  2. length(attachments) > 0
  3. at least 3 of:
    • body.current_thread.text matches 'zip file.{1,50}encrypted'
    • body.current_thread.text matches 'attachment.{1,30}encrypted'
    • body.current_thread.text matches 'password.{1,5}is.{1,5}[A-Z0-9]{8,}'
    • body.current_thread.text matches 'details.{1,20}payment.{1,30}attach'
    • subject.subject contains 'you have received'
    • subject.subject contains 'new debit'
    • subject.subject contains 'payment confirmation'
    • subject.subject contains 'invoice attached'
  4. all of:
    • attachments[0].file_extension is 'zip'
    • any of:
      • attachments[0].file_name matches 'payment|invoice|receipt|document|bank'
      • body.current_thread.text matches '[A-Z]{10,}'

Inspects: attachments[0].file_extension, attachments[0].file_name, body.current_thread.text, subject.subject, type.inbound. Sensors: regex.contains, regex.icontains, strings.icontains.

Indicators matched (11)

FieldMatchValue
regex.icontainsregexzip file.{1,50}encrypted
regex.icontainsregexattachment.{1,30}encrypted
regex.icontainsregexpassword.{1,5}is.{1,5}[A-Z0-9]{8,}
regex.icontainsregexdetails.{1,20}payment.{1,30}attach
strings.icontainssubstringyou have received
strings.icontainssubstringnew debit
strings.icontainssubstringpayment confirmation
strings.icontainssubstringinvoice attached
attachments[0].file_extensionequalszip
regex.icontainsregexpayment|invoice|receipt|document|bank
regex.containsregex[A-Z]{10,}

Stages and Predicates

Stage 1: mql_rule

and
  or
    attachments[0].file_name regex_match "payment|invoice|receipt|document|bank"
    body.current_thread.text regex_match "[A-Z]{10,}"
  or
    body.current_thread.text regex_match "attachment.{1,30}encrypted"
    body.current_thread.text regex_match "details.{1,20}payment.{1,30}attach"
    body.current_thread.text regex_match "password.{1,5}is.{1,5}[A-Z0-9]{8,}"
    body.current_thread.text regex_match "zip file.{1,50}encrypted"
    subject.subject contains "invoice attached"
    subject.subject contains "new debit"
    subject.subject contains "payment confirmation"
    subject.subject contains "you have received"
  attachments length_compare "0"
  attachments[0].file_extension eq "zip"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
attachments[0].file_extensioneq
  • zip
field:"attachments[0].file_extension" kind:eq value:"zip"
attachments[0].file_nameregex_match
    • payment
    • invoice
    • receipt
    • document
    • bank
field:"attachments[0].file_name" kind:regex_match
body.current_thread.textregex_match
  • [A-Z]{10,}
  • attachment.{1,30}encrypted
  • details.{1,20}payment.{1,30}attach
  • password.{1,5}is.{1,5}[A-Z0-9]{8,}
  • zip file.{1,50}encrypted
field:"body.current_thread.text" kind:regex_match
subject.subjectcontains
  • invoice attached
  • new debit
  • payment confirmation
  • you have received
field:"subject.subject" kind:contains
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

BEC/Fraud: Fake investment outreach from suspicious TLD

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

Detects unsolicited investment/funding outreach emails from suspicious TLDs. Targets mass-mailed spam campaigns offering business funding, capital allocation, and family office outreach.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • sender.email
  • subject
  • type

Rule body

type.inbound
// subject contains investment language
and (
  strings.istarts_with(subject.base, 'financing for', 'investment into')
  or strings.icontains(subject.base, "family office")
)

// financial body content
and (
  any(ml.nlu_classifier(body.current_thread.text).topics,
      .name == "Financial Communications" and .confidence != "low"
  )
  or (
    any(ml.nlu_classifier(body.current_thread.text).topics,
        .name == "B2B Cold Outreach" and .confidence != "low"
    )
    and regex.icontains(body.current_thread.text,
                        'funding',
                        'capital',
                        '\d{3}k',
                        'rates from \d+%',
                        'funded in \d+',
                        'family office'
    )
  )
)

// suspicious sender
and (
  sender.email.domain.tld in $suspicious_tlds
  or sender.email.domain.tld == "info"
)

Detection logic

Scope: inbound message.

Detects unsolicited investment/funding outreach emails from suspicious TLDs. Targets mass-mailed spam campaigns offering business funding, capital allocation, and family office outreach.

  1. inbound message
  2. any of:
    • subject.base starts with any of 2 patterns
      • financing for
      • investment into
    • subject.base contains 'family office'
  3. any of:
    • any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
      • .name is 'Financial Communications'
      • .confidence is not 'low'
    • all of:
      • any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
        • .name is 'B2B Cold Outreach'
        • .confidence is not 'low'
      • body.current_thread.text matches any of 6 patterns
        • funding
        • capital
        • \d{3}k
        • rates from \d+%
        • funded in \d+
        • family office
  4. any of:
    • sender.email.domain.tld in $suspicious_tlds
    • sender.email.domain.tld is 'info'

Inspects: body.current_thread.text, sender.email.domain.tld, subject.base, type.inbound. Sensors: ml.nlu_classifier, regex.icontains, strings.icontains, strings.istarts_with. Reference lists: $suspicious_tlds.

Indicators matched (12)

FieldMatchValue
strings.istarts_withprefixfinancing for
strings.istarts_withprefixinvestment into
strings.icontainssubstringfamily office
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsFinancial Communications
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsB2B Cold Outreach
regex.icontainsregexfunding
regex.icontainsregexcapital
regex.icontainsregex\d{3}k
regex.icontainsregexrates from \d+%
regex.icontainsregexfunded in \d+
regex.icontainsregexfamily office
sender.email.domain.tldequalsinfo

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      any(ml.nlu_classifier(body.current_thread.text).topics)
        and
          ml.nlu_classifier(body.current_thread.text).topics.confidence ne "low"
          ml.nlu_classifier(body.current_thread.text).topics.name eq "B2B Cold Outreach"
      or
        body.current_thread.text regex_match "\\d{3}k"
        body.current_thread.text regex_match "capital"
        body.current_thread.text regex_match "family office"
        body.current_thread.text regex_match "funded in \\d+"
        body.current_thread.text regex_match "funding"
        body.current_thread.text regex_match "rates from \\d+%"
    any(ml.nlu_classifier(body.current_thread.text).topics)
      and
        ml.nlu_classifier(body.current_thread.text).topics.confidence ne "low"
        ml.nlu_classifier(body.current_thread.text).topics.name eq "Financial Communications"
  or
    sender.email.domain.tld eq "info"
     macro "sender.email.domain.tld in suspicious_tlds"
  or
    subject.base contains "family office"
    subject.base starts_with "financing for"
    subject.base starts_with "investment into"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

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

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • attachments
  • body
  • body.current_thread
  • body.html
  • body.links
  • headers
  • recipients
  • recipients.to
  • sender.email
  • subject
  • type

Rule body

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,}

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        and
          headers.in_reply_to is_null
          headers.references length_compare "0"
        headers.in_reply_to is_null
      or
        subject.is_forward eq "true"
        subject.is_reply eq "true"
    body.html.raw is_null
  or
    and
      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
        recipients.to length_compare "0"
         macro "all(recipients.to)"
      ml.nlu_classifier(body.current_thread.text).topics length_compare "1"
    any([subject.subject, body.current_thread.text])
      [subject.subject, body.current_thread.text] regex_match "(full|part).time"
    body.current_thread.text eq "100% legal"
    body.current_thread.text match "commission"
    body.current_thread.text match "company rep"
    body.current_thread.text match "hourly"
    body.current_thread.text match "job"
    body.current_thread.text match "manager"
    body.current_thread.text match "per hour"
    body.current_thread.text match "prior experience"
    body.current_thread.text regex_match "\\bHR\\b"
  or
    and
      attachments length_compare "0"
       macro "all(attachments)"
    attachments length_compare "0"
  any(regex.extract(body.current_thread.text))
    or
      strings.parse_email func_call "strings.parse_email(regex.extract(body.current_thread.text)[].full_match).domain.domain in free_email_providers"
      strings.parse_email func_call "strings.parse_email(regex.extract(body.current_thread.text)[].full_match).domain.root_domain in free_email_providers"
  any(ml.nlu_classifier(body.current_thread.text).entities)
    ml.nlu_classifier(body.current_thread.text).entities.name in ["greeting", "salutation"]
  or
     macro "all(body.links)"
     macro "sender.email.domain.root_domain in free_email_providers"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

BEC: Financial fraud from newly registered sender domain

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

Detects inbound messages from domains registered less than 30 days ago that exhibit business email compromise intent with high-confidence financial or payment topics. The message must also contain explicit banking details such as account and routing numbers, invoice references, or payment urgency language, and must either fail DMARC on a trusted domain or originate from an untrusted domain.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesSocial engineering, Spoofing

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule body

type.inbound
and network.whois(sender.email.domain).days_old < 30
and any(ml.nlu_classifier(body.current_thread.text).intents, .name == "bec")
and (
  any(ml.nlu_classifier(body.current_thread.text).topics,
      .name in ("Financial Communications", "Payment Information")
      and .confidence == "high"
  )
)
and (
  (
    regex.icontains(body.current_thread.text,
                    'account\s*(?:number|num|no\.?)\s*:?\s*\d{5,}'
    )
    and regex.icontains(body.current_thread.text,
                        '(?:wire\s*)?routing\s*(?:number|num|no\.?)\s*:?\s*\d{5,}'
    )
  )
  or regex.icontains(body.current_thread.text,
                     'invoice\s*(?:#|number|num|no\.?)\s*:?\s*[A-Z0-9-]{3,}',
                     'per\s+\w+.{0,5}s\s+request'
  )
  or strings.icontains(body.current_thread.text,
                       'due upon receipt',
                       'confirm receipt of invoice',
                       'see attached invoice'
  )
)
// negate highly trusted sender domains unless they fail DMARC authentication
and not (
  sender.email.domain.root_domain in $high_trust_sender_root_domains
  and coalesce(headers.auth_summary.dmarc.pass, false)
)

Detection logic

Scope: inbound message.

Detects inbound messages from domains registered less than 30 days ago that exhibit business email compromise intent with high-confidence financial or payment topics. The message must also contain explicit banking details such as account and routing numbers, invoice references, or payment urgency language, and must either fail DMARC on a trusted domain or originate from an untrusted domain.

  1. inbound message
  2. network.whois(sender.email.domain).days_old < 30
  3. any of ml.nlu_classifier(body.current_thread.text).intents where:
    • .name is 'bec'
  4. any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
    • .name in ('Financial Communications', 'Payment Information')
    • .confidence is 'high'
  5. any of:
    • all of:
      • body.current_thread.text matches 'account\\s*(?:number|num|no\\.?)\\s*:?\\s*\\d{5,}'
      • body.current_thread.text matches '(?:wire\\s*)?routing\\s*(?:number|num|no\\.?)\\s*:?\\s*\\d{5,}'
    • body.current_thread.text matches any of 2 patterns
      • invoice\s*(?:#|number|num|no\.?)\s*:?\s*[A-Z0-9-]{3,}
      • per\s+\w+.{0,5}s\s+request
    • body.current_thread.text contains any of 3 patterns
      • due upon receipt
      • confirm receipt of invoice
      • see attached invoice
  6. not:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • coalesce(headers.auth_summary.dmarc.pass)

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, sender.email.domain, sender.email.domain.root_domain, type.inbound. Sensors: ml.nlu_classifier, network.whois, regex.icontains, strings.icontains. Reference lists: $high_trust_sender_root_domains.

Indicators matched (11)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).intents[].nameequalsbec
ml.nlu_classifier(body.current_thread.text).topics[].namememberFinancial Communications
ml.nlu_classifier(body.current_thread.text).topics[].namememberPayment Information
ml.nlu_classifier(body.current_thread.text).topics[].confidenceequalshigh
regex.icontainsregexaccount\s*(?:number|num|no\.?)\s*:?\s*\d{5,}
regex.icontainsregex(?:wire\s*)?routing\s*(?:number|num|no\.?)\s*:?\s*\d{5,}
regex.icontainsregexinvoice\s*(?:#|number|num|no\.?)\s*:?\s*[A-Z0-9-]{3,}
regex.icontainsregexper\s+\w+.{0,5}s\s+request
strings.icontainssubstringdue upon receipt
strings.icontainssubstringconfirm receipt of invoice
strings.icontainssubstringsee attached invoice

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      body.current_thread.text regex_match "(?:wire\\s*)?routing\\s*(?:number|num|no\\.?)\\s*:?\\s*\\d{5,}"
      body.current_thread.text regex_match "account\\s*(?:number|num|no\\.?)\\s*:?\\s*\\d{5,}"
    body.current_thread.text contains "confirm receipt of invoice"
    body.current_thread.text contains "due upon receipt"
    body.current_thread.text contains "see attached invoice"
    body.current_thread.text regex_match "invoice\\s*(?:#|number|num|no\\.?)\\s*:?\\s*[A-Z0-9-]{3,}"
    body.current_thread.text regex_match "per\\s+\\w+.{0,5}s\\s+request"
  not
    and
      coalesce func_call "coalesce(headers.auth_summary.dmarc.pass)"
       macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
  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 in ["Financial Communications", "Payment Information"]
  any(ml.nlu_classifier(body.current_thread.text).intents)
    ml.nlu_classifier(body.current_thread.text).intents.name eq "bec"
  network.whois func_call "network.whois(sender.email.domain).days_old < 30"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textcontains
  • confirm receipt of invoice
  • due upon receipt
  • see attached invoice
field:"body.current_thread.text" kind:contains
body.current_thread.textregex_match
  • (?:wire\s*)?routing\s*(?:number|num|no.?)\s*:?\s*\d{5,}
  • account\s*(?:number|num|no.?)\s*:?\s*\d{5,}
  • invoice\s*(?:#|number|num|no.?)\s*:?\s*[A-Z0-9-]{3,}
  • per\s+\w+.{0,5}s\s+request
field:"body.current_thread.text" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Body: CSS clamp() font obfuscation with IP-based links

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

Detects inbound messages using a CSS evasion technique where font-size or line-height properties are set using the clamp() function with very small or negative values — a method used to hide or manipulate rendered text and evade content-based detection. Messages matching this pattern also contain at least one hyperlink resolving directly to an IP address rather than a domain. Observed lures span a wide range of social engineering themes including urgent account alerts, financial offers, storage login codes, service cancellations, and renewal reminders.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • attachments
  • body
  • body.html
  • body.links
  • type

Rule body

type.inbound
and (
  (
    regex.icontains(body.html.raw,
                    '(?:font-size|line-height):\s*clamp\s*\(\s*(?:-\d+|0)(?:px|em|rem|pt)?,'
    )
    and any(body.links, .href_url.ip.ip is not null)
  )
  or any(attachments,
         (.content_type == "message/rfc822" or .file_extension =~ "eml")
         and regex.icontains(file.parse_eml(.).body.html.raw,
                             '(?:font-size|line-height):\s*clamp\s*\(\s*(?:-\d+|0)(?:px|em|rem|pt)?,'
         )
         and any(file.parse_eml(.).body.links, .href_url.ip.ip is not null)
  )
)

Detection logic

Scope: inbound message.

Detects inbound messages using a CSS evasion technique where font-size or line-height properties are set using the clamp() function with very small or negative values — a method used to hide or manipulate rendered text and evade content-based detection. Messages matching this pattern also contain at least one hyperlink resolving directly to an IP address rather than a domain. Observed lures span a wide range of social engineering themes including urgent account alerts, financial offers, storage login codes, service cancellations, and renewal reminders.

  1. inbound message
  2. any of:
    • all of:
      • body.html.raw matches '(?:font-size|line-height):\\s*clamp\\s*\\(\\s*(?:-\\d+|0)(?:px|em|rem|pt)?,'
      • any of body.links where:
        • .href_url.ip.ip is set
    • any of attachments where all hold:
      • any of:
        • .content_type is 'message/rfc822'
        • .file_extension is 'eml'
      • file.parse_eml(.).body.html.raw matches '(?:font-size|line-height):\\s*clamp\\s*\\(\\s*(?:-\\d+|0)(?:px|em|rem|pt)?,'
      • any of file.parse_eml(.).body.links where:
        • .href_url.ip.ip is set

Inspects: attachments[].content_type, attachments[].file_extension, body.html.raw, body.links, body.links[].href_url.ip.ip, type.inbound. Sensors: file.parse_eml, regex.icontains.

Indicators matched (3)

FieldMatchValue
regex.icontainsregex(?:font-size|line-height):\s*clamp\s*\(\s*(?:-\d+|0)(?:px|em|rem|pt)?,
attachments[].content_typeequalsmessage/rfc822
attachments[].file_extensionequalseml

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(attachments)
      and
        or
          attachments.content_type eq "message/rfc822"
          attachments.file_extension eq "eml"
        any(file.parse_eml(attachments).body.links)
          file.parse_eml(attachments).body.links.href_url.ip.ip is_not_null
        file.parse_eml(attachments[]).body.html.raw regex_match "(?:font-size|line-height):\\s*clamp\\s*\\(\\s*(?:-\\d+|0)(?:px|em|rem|pt)?,"
    and
      any(body.links)
        body.links.href_url.ip.ip is_not_null
      body.html.raw regex_match "(?:font-size|line-height):\\s*clamp\\s*\\(\\s*(?:-\\d+|0)(?:px|em|rem|pt)?,"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.html.rawregex_match
  • (?:font-size|line-height):\s*clamp\s*(\s*(?:-\d+|0)(?:px|em|rem|pt)?,
field:"body.html.raw" kind:regex_match value:"(?:font-size|line-height):\s*clamp\s*(\s*(?:-\d+|0)(?:px|em|rem|pt)?,"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Body: CSS Hidden text via clip-path

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

Detects inbound messages containing a div styled with 'clip-path: inset(100%)', a CSS technique used to visually hide substantial blocks of text (over 150 characters) from the recipient while keeping it present in the underlying HTML. This method is commonly used to evade content-based detection engines by hiding filler or unrelated text within the message body.

Threat classification

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

CategoryValues
Attack typesSpam, Credential Phishing
Tactics and techniquesEvasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • type

Rule body

type.inbound
and any(html.xpath(body.html, '//div[contains(@style,"clip-path")]').nodes,
        regex.icontains(.raw, 'clip-path:\s*inset\(\s*100%')
        and length(.inner_text) > 150
)

Detection logic

Scope: inbound message.

Detects inbound messages containing a div styled with 'clip-path: inset(100%)', a CSS technique used to visually hide substantial blocks of text (over 150 characters) from the recipient while keeping it present in the underlying HTML. This method is commonly used to evade content-based detection engines by hiding filler or unrelated text within the message body.

  1. inbound message
  2. any of html.xpath(body.html, '//div[contains(@style,"clip-path")]').nodes where all hold:
    • .raw matches 'clip-path:\\s*inset\\(\\s*100%'
    • length(.inner_text) > 150

Inspects: body.html, type.inbound. Sensors: html.xpath, regex.icontains.

Indicators matched (1)

FieldMatchValue
regex.icontainsregexclip-path:\s*inset\(\s*100%

Stages and Predicates

Stage 1: mql_rule

and
  any(html.xpath(body.html, '//div[contains(@style,"clip-path")]').nodes)
    and
      html.xpath(body.html, '//div[contains(@style,"clip-path")]').nodes.inner_text length_compare "150"
      html.xpath(body.html, '//div[contains(@style,"clip-path")]').nodes.raw regex_match "clip-path:\\s*inset\\(\\s*100%"
  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"

Body: CSS zero-value calc() obfuscation

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

Detects inbound messages containing HTML where a CSS calc() expression subtracts an identical value and unit from itself (e.g., calc(100px - 100px)), always resolving to zero. This pattern is commonly used to hide or collapse content from view while keeping it present in the underlying HTML, a technique often leveraged to evade text-based detection or conceal malicious content from recipients.

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.html
  • type

Rule body

type.inbound
and any(regex.iextract(body.html.raw,
                       'calc\(\s*(?P<n1>\d+(?:\.\d+)?)(?P<u1>px|vw|vh|em|rem|%)\s*-\s*(?P<n2>\d+(?:\.\d+)?)(?P<u2>px|vw|vh|em|rem|%)\s*\)'
        ),
        .named_groups["n1"] == .named_groups["n2"]
        and .named_groups["u1"] == .named_groups["u2"]
)

Detection logic

Scope: inbound message.

Detects inbound messages containing HTML where a CSS calc() expression subtracts an identical value and unit from itself (e.g., calc(100px - 100px)), always resolving to zero. This pattern is commonly used to hide or collapse content from view while keeping it present in the underlying HTML, a technique often leveraged to evade text-based detection or conceal malicious content from recipients.

  1. inbound message
  2. any of regex.iextract(body.html.raw) where all hold:
    • .named_groups['n1'] is .named_groups['n2']
    • .named_groups['u1'] is .named_groups['u2']

Inspects: body.html.raw, type.inbound. Sensors: regex.iextract.

Indicators matched (1)

FieldMatchValue
regex.iextractregexcalc\(\s*(?P<n1>\d+(?:\.\d+)?)(?P<u1>px|vw|vh|em|rem|%)\s*-\s*(?P<n2>\d+(?:\.\d+)?)(?P<u2>px|vw|vh|em|rem|%)\s*\)

Stages and Predicates

Stage 1: mql_rule

and
  any(regex.iextract(body.html.raw))
    and
      regex.iextract(body.html.raw).named_groups['n1'] cross_field_compare "regex.iextract(body.html.raw).named_groups['n2']"
      regex.iextract(body.html.raw).named_groups['u1'] cross_field_compare "regex.iextract(body.html.raw).named_groups['u2']"
  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"

Body: Fake secure email portal with HTML obfuscation

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

Detects inbound messages with empty subjects impersonating a secure email portal, identified through multiple indicators including hidden HTML characters used to obfuscate the sender address, recipient domain echoed back as a portal sender, template typos, or frozen tracking links associated with known secure messaging infrastructure abuse.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Social engineering, Spoofing

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and subject.base == ""
and strings.icontains(body.current_thread.text, "Secure Emails")
and strings.icontains(body.current_thread.text, "Title:")
and (
  // hidden-@ HTML obfuscation
  regex.icontains(body.html.raw, '<font[^>]+display\s*:\s*none[^>]*>@</font>')
  // recipient root domain echoed back as a portal sender
  or any(recipients.to,
         strings.icontains(body.current_thread.text,
                           strings.concat("acts@", .email.domain.root_domain)
         )
  )
  // template typo
  or strings.icontains(body.current_thread.text, "commuication portal")
  // frozen crId or linkprotect.cudasvc.com inner target
  or any(body.links,
         .href_url.domain.domain == "links.us1.defend.egress.com"
         and (
           strings.icontains(.href_url.query_params,
                             "crId=6a17340ad2523c97775fed21"
           )
           or strings.icontains(.href_url.query_params,
                                "OriginalLink=linkprotect.cudasvc.com"
           )
         )
  )
)

Detection logic

Scope: inbound message.

Detects inbound messages with empty subjects impersonating a secure email portal, identified through multiple indicators including hidden HTML characters used to obfuscate the sender address, recipient domain echoed back as a portal sender, template typos, or frozen tracking links associated with known secure messaging infrastructure abuse.

  1. inbound message
  2. subject.base is ''
  3. body.current_thread.text contains 'Secure Emails'
  4. body.current_thread.text contains 'Title:'
  5. any of:
    • body.html.raw matches '<font[^>]+display\\s*:\\s*none[^>]*>@</font>'
    • any of recipients.to where:
      • strings.icontains(body.current_thread.text)
    • body.current_thread.text contains 'commuication portal'
    • any of body.links where all hold:
      • .href_url.domain.domain is 'links.us1.defend.egress.com'
      • any of:
        • .href_url.query_params contains 'crId=6a17340ad2523c97775fed21'
        • .href_url.query_params contains 'OriginalLink=linkprotect.cudasvc.com'

Inspects: body.current_thread.text, body.html.raw, body.links, body.links[].href_url.domain.domain, body.links[].href_url.query_params, recipients.to, recipients.to[].email.domain.root_domain, subject.base, type.inbound. Sensors: regex.icontains, strings.concat, strings.icontains.

Indicators matched (8)

FieldMatchValue
subject.baseequals
strings.icontainssubstringSecure Emails
strings.icontainssubstringTitle:
regex.icontainsregex<font[^>]+display\s*:\s*none[^>]*>@</font>
strings.icontainssubstringcommuication portal
body.links[].href_url.domain.domainequalslinks.us1.defend.egress.com
strings.icontainssubstringcrId=6a17340ad2523c97775fed21
strings.icontainssubstringOriginalLink=linkprotect.cudasvc.com

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(body.links)
      and
        or
          body.links.href_url.query_params contains "OriginalLink=linkprotect.cudasvc.com"
          body.links.href_url.query_params contains "crId=6a17340ad2523c97775fed21"
        body.links.href_url.domain.domain eq "links.us1.defend.egress.com"
    any(recipients.to)
      strings.icontains func_call "strings.icontains(body.current_thread.text)"
    body.current_thread.text contains "commuication portal"
    body.html.raw regex_match "<font[^>]+display\\s*:\\s*none[^>]*>@</font>"
  body.current_thread.text contains "Secure Emails"
  body.current_thread.text contains "Title:"
  subject.base eq ""
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Body: HTML whitespace stuffing with short initial message

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

Detects messages that uses HTML-based whitespace padding (repeated br tags, p-nbsp blocks, or div-br wrappers) to push content below the visible fold.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • body.html
  • headers
  • headers.auth_summary
  • recipients
  • recipients.to[0]
  • sender.email
  • type

Rule body

type.inbound
// not a legitimate thread reply or is indicative of self sender
and (
  (length(headers.references) == 0 and headers.in_reply_to is null)
  or (
    length(recipients.to) == 1
    and length(recipients.cc) == 0
    and sender.email.email == recipients.to[0].email.email
  )
)
// whitespace-stuffed credphish targets single recipients
and length(recipients.to) == 1
and length(recipients.cc) == 0
and length(recipients.bcc) == 0
// short lure
and length(body.current_thread.text) < 2000
// HTML whitespace stuffing
and (
  regex.icontains(body.html.raw,
                  '(?:<br\s*/?\s*>\s*){30,}',
                  '(?:<p>\s*(?:&nbsp;|&#160;)\s*</p>\s*){10,}',
                  '(?:<div[^>]*>\s*<br\s*/?\s*>\s*</div>\s*){20,}'
  )
  or (
    regex.icontains(body.html.raw,
                    '(?:<p[^>]*>\s*<o:p>\s*(?:&nbsp;|&#160;)\s*</o:p>\s*</p>\s*){10,}',
                    '(?:<p[^>]*>\s*(?:&nbsp;|&#160;)\s*</p>\s*){30,}'
    )
    and any(ml.nlu_classifier(body.current_thread.text).intents,
            .name in ("cred_theft", "bec")
    )
  )
)
// low word count excludes legitimate long threads
and regex.count(body.html.display_text, '\S+') < 3000

// visible link in current thread pointing to external domain
and any(body.current_thread.links,
        .href_url.domain.root_domain != sender.email.domain.root_domain
        and .href_url.domain.valid
        and .href_url.scheme in ("https", "http")
        and .visible == true
)

// credential phishing has few visible links - newsletters have many
and length(filter(body.current_thread.links,
                  .href_url.domain.valid
                  and .href_url.scheme in ("https", "http")
                  and .visible == true
           )
) < 10

// negate high trust senders that pass auth
and not (
  sender.email.domain.root_domain in $high_trust_sender_root_domains
  and coalesce(headers.auth_summary.dmarc.pass, false)
)

// negate authenticated senders with unsubscribe mechanism (marketing)
and not (
  coalesce(headers.auth_summary.dmarc.pass, false)
  and any(body.current_thread.links,
          strings.icontains(.display_text, "unsubscribe")
          or strings.icontains(.href_url.path, "unsubscribe")
  )
)

Detection logic

Scope: inbound message.

Detects messages that uses HTML-based whitespace padding (repeated br tags, p-nbsp blocks, or div-br wrappers) to push content below the visible fold.

  1. inbound message
  2. any of:
    • all of:
      • length(headers.references) is 0
      • headers.in_reply_to is missing
    • all of:
      • length(recipients.to) is 1
      • length(recipients.cc) is 0
      • sender.email.email is recipients.to[0].email.email
  3. length(recipients.to) is 1
  4. length(recipients.cc) is 0
  5. length(recipients.bcc) is 0
  6. length(body.current_thread.text) < 2000
  7. any of:
    • body.html.raw matches any of 3 patterns
      • (?:<br\s*/?\s*>\s*){30,}
      • (?:<p>\s*(?:&nbsp;|&#160;)\s*</p>\s*){10,}
      • (?:<div[^>]*>\s*<br\s*/?\s*>\s*</div>\s*){20,}
    • all of:
      • body.html.raw matches any of 2 patterns
        • (?:<p[^>]*>\s*<o:p>\s*(?:&nbsp;|&#160;)\s*</o:p>\s*</p>\s*){10,}
        • (?:<p[^>]*>\s*(?:&nbsp;|&#160;)\s*</p>\s*){30,}
      • any of ml.nlu_classifier(body.current_thread.text).intents where:
        • .name in ('cred_theft', 'bec')
  8. regex.count(body.html.display_text, '\\S+') < 3000
  9. any of body.current_thread.links where all hold:
    • .href_url.domain.root_domain is not sender.email.domain.root_domain
    • .href_url.domain.valid
    • .href_url.scheme in ('https', 'http')
    • .visible is True
  10. length(filter(body.current_thread.links, .href_url.domain.valid and .href_url.scheme in ('https', 'http') and .visible == True)) < 10
  11. not:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • coalesce(headers.auth_summary.dmarc.pass)
  12. not:
    • all of:
      • coalesce(headers.auth_summary.dmarc.pass)
      • any of body.current_thread.links where any holds:
        • .display_text contains 'unsubscribe'
        • .href_url.path contains 'unsubscribe'

Inspects: body.current_thread.links, body.current_thread.links[].display_text, body.current_thread.links[].href_url.domain.root_domain, body.current_thread.links[].href_url.domain.valid, body.current_thread.links[].href_url.path, body.current_thread.links[].href_url.scheme, body.current_thread.links[].visible, body.current_thread.text, body.html.display_text, body.html.raw, headers.auth_summary.dmarc.pass, headers.in_reply_to, headers.references, recipients.bcc, recipients.cc, recipients.to, recipients.to[0].email.email, sender.email.domain.root_domain, sender.email.email, type.inbound. Sensors: ml.nlu_classifier, regex.count, regex.icontains, strings.icontains. Reference lists: $high_trust_sender_root_domains.

Indicators matched (10)

FieldMatchValue
regex.icontainsregex(?:<br\s*/?\s*>\s*){30,}
regex.icontainsregex(?:<p>\s*(?:&nbsp;|&#160;)\s*</p>\s*){10,}
regex.icontainsregex(?:<div[^>]*>\s*<br\s*/?\s*>\s*</div>\s*){20,}
regex.icontainsregex(?:<p[^>]*>\s*<o:p>\s*(?:&nbsp;|&#160;)\s*</o:p>\s*</p>\s*){10,}
regex.icontainsregex(?:<p[^>]*>\s*(?:&nbsp;|&#160;)\s*</p>\s*){30,}
ml.nlu_classifier(body.current_thread.text).intents[].namemembercred_theft
ml.nlu_classifier(body.current_thread.text).intents[].namememberbec
regex.countregex\S+
body.current_thread.links[].href_url.schemememberhttps
body.current_thread.links[].href_url.schemememberhttp

Stages and Predicates

Stage 1: mql_rule

and
  not
    and
      any(body.current_thread.links)
        or
          body.current_thread.links.display_text contains "unsubscribe"
          body.current_thread.links.href_url.path contains "unsubscribe"
      coalesce func_call "coalesce(headers.auth_summary.dmarc.pass)"
  or
    and
      or
        body.html.raw regex_match "(?:<p[^>]*>\\s*(?:&nbsp;|&#160;)\\s*</p>\\s*){30,}"
        body.html.raw regex_match "(?:<p[^>]*>\\s*<o:p>\\s*(?:&nbsp;|&#160;)\\s*</o:p>\\s*</p>\\s*){10,}"
      any(ml.nlu_classifier(body.current_thread.text).intents)
        ml.nlu_classifier(body.current_thread.text).intents.name in ["bec", "cred_theft"]
    body.html.raw regex_match "(?:<br\\s*/?\\s*>\\s*){30,}"
    body.html.raw regex_match "(?:<div[^>]*>\\s*<br\\s*/?\\s*>\\s*</div>\\s*){20,}"
    body.html.raw regex_match "(?:<p>\\s*(?:&nbsp;|&#160;)\\s*</p>\\s*){10,}"
  any(body.current_thread.links)
    and
      body.current_thread.links.href_url.domain.root_domain cross_field_compare "sender.email.domain.root_domain"
      body.current_thread.links.href_url.domain.valid eq "true"
      body.current_thread.links.href_url.scheme in ["http", "https"]
      body.current_thread.links.visible eq "true"
  not
    and
      coalesce func_call "coalesce(headers.auth_summary.dmarc.pass)"
       macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
  or
    and
      headers.in_reply_to is_null
      headers.references length_compare "0"
    and
      recipients.cc length_compare "0"
      recipients.to length_compare "1"
      sender.email.email cross_field_compare "recipients.to[0].email.email"
  body.current_thread.text length_compare "2000"
  filter(body.current_thread.links, .href_url.domain.valid and .href_url.scheme in ('https', 'http') and .visible == True) length_compare "10"
  recipients.bcc length_compare "0"
  recipients.cc length_compare "0"
  recipients.to length_compare "1"
  regex.count func_call "regex.count(body.html.display_text, \"\\S+\") < 3000"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
body.current_thread.linksarray_any(no value, null check)excludes:body.current_thread.links

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.html.rawregex_match
  • (?:<br\s*/?\s*>\s*){30,}
  • (?:<div[^>]*>\s*<br\s*/?\s*>\s*</div>\s*){20,}
  • (?:<p>\s*(?: | )\s*</p>\s*){10,}
  • (?:<p[^>]*>\s*(?: | )\s*</p>\s*){30,}
  • (?:<p[^>]*>\s*<o:p>\s*(?: | )\s*</o:p>\s*</p>\s*){10,}
field:"body.html.raw" kind:regex_match
headers.in_reply_tois_null
  • (no value, null check)
field:"headers.in_reply_to" kind:is_null
sender.email.emailcross_field_compare
  • recipients.to[0].email.email transforms: op:eq
field:"sender.email.email" kind:cross_field_compare value:"recipients.to[0].email.email"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Body: Invisible Unicode obfuscation student loan callback phishing

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

Detects messages containing clusters of Unicode zero-width and invisible characters (such as LRM, RLM, zero-width space, BOM, and directional isolates) interspersed within digit sequences and body content matching 'student loan' patterns. This technique is used to obscure text from security filters while remaining visually coherent to recipients.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud, Credential Phishing, Spam
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.html
  • type

Rule body

type.inbound
and regex.contains(body.html.raw,
                   '(\x{2065}|\x{200E}|\x{200F}|\x{2066}|\x{2067}|\x{2068}|\x{2069}|\x{200B}|\x{200C}|\x{200D}|\x{FEFF}){3}'
)
and regex.contains(body.html.raw,
                   '\d[\x{200E}\x{200F}\x{200B}\x{200C}\x{200D}\x{2065}\x{FEFF}]{1,6}\d'
)
and regex.icontains(body.html.raw,
                    's.{0,6}t.{0,6}u.{0,6}d.{0,6}e.{0,6}n.{0,6}t.{0,10}l.{0,6}o.{0,6}a.{0,6}n'
)

Detection logic

Scope: inbound message.

Detects messages containing clusters of Unicode zero-width and invisible characters (such as LRM, RLM, zero-width space, BOM, and directional isolates) interspersed within digit sequences and body content matching 'student loan' patterns. This technique is used to obscure text from security filters while remaining visually coherent to recipients.

  1. inbound message
  2. body.html.raw matches '(\\x{2065}|\\x{200E}|\\x{200F}|\\x{2066}|\\x{2067}|\\x{2068}|\\x{2069}|\\x{200B}|\\x{200C}|\\x{200D}|\\x{FEFF}){3}'
  3. body.html.raw matches '\\d[\\x{200E}\\x{200F}\\x{200B}\\x{200C}\\x{200D}\\x{2065}\\x{FEFF}]{1,6}\\d'
  4. body.html.raw matches 's.{0,6}t.{0,6}u.{0,6}d.{0,6}e.{0,6}n.{0,6}t.{0,10}l.{0,6}o.{0,6}a.{0,6}n'

Inspects: body.html.raw, type.inbound. Sensors: regex.contains, regex.icontains.

Indicators matched (3)

FieldMatchValue
regex.containsregex(\x{2065}|\x{200E}|\x{200F}|\x{2066}|\x{2067}|\x{2068}|\x{2069}|\x{200B}|\x{200C}|\x{200D}|\x{FEFF}){3}
regex.containsregex\d[\x{200E}\x{200F}\x{200B}\x{200C}\x{200D}\x{2065}\x{FEFF}]{1,6}\d
regex.icontainsregexs.{0,6}t.{0,6}u.{0,6}d.{0,6}e.{0,6}n.{0,6}t.{0,10}l.{0,6}o.{0,6}a.{0,6}n

Stages and Predicates

Stage 1: mql_rule

and
  body.html.raw regex_match "(\\x{2065}|\\x{200E}|\\x{200F}|\\x{2066}|\\x{2067}|\\x{2068}|\\x{2069}|\\x{200B}|\\x{200C}|\\x{200D}|\\x{FEFF}){3}"
  body.html.raw regex_match "\\d[\\x{200E}\\x{200F}\\x{200B}\\x{200C}\\x{200D}\\x{2065}\\x{FEFF}]{1,6}\\d"
  body.html.raw regex_match "s.{0,6}t.{0,6}u.{0,6}d.{0,6}e.{0,6}n.{0,6}t.{0,10}l.{0,6}o.{0,6}a.{0,6}n"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.html.rawregex_match
  • (\x{2065}|\x{200E}|\x{200F}|\x{2066}|\x{2067}|\x{2068}|\x{2069}|\x{200B}|\x{200C}|\x{200D}|\x{FEFF}){3}
  • \d[\x{200E}\x{200F}\x{200B}\x{200C}\x{200D}\x{2065}\x{FEFF}]{1,6}\d
  • s.{0,6}t.{0,6}u.{0,6}d.{0,6}e.{0,6}n.{0,6}t.{0,10}l.{0,6}o.{0,6}a.{0,6}n
field:"body.html.raw" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Body: Suspicious date format

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

Detects messages containing strage date formats observed in phishing emails.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Spoofing, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • type

Rule body

type.inbound
and regex.icontains(body.current_thread.text,
                    'Date:\s(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\s(?:January|February|March|April|May|June|July|August|September|October|November|December)\s202(?:5|6|7|8|9)'
)

Detection logic

Scope: inbound message.

Detects messages containing strage date formats observed in phishing emails.

  1. inbound message
  2. body.current_thread.text matches 'Date:\\s(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\\s(?:January|February|March|April|May|June|July|August|September|October|November|December)\\s202(?:5|6|7|8|9)'

Inspects: body.current_thread.text, type.inbound. Sensors: regex.icontains.

Indicators matched (1)

FieldMatchValue
regex.icontainsregexDate:\s(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\s(?:January|February|March|April|May|June|July|August|September|October|November|December)\s202(?:5|6|7|8|9)

Stages and Predicates

Stage 1: mql_rule

and
  body.current_thread.text regex_match "Date:\\s(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\\s(?:January|February|March|April|May|June|July|August|September|October|November|December)\\s202(?:5|6|7|8|9)"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • Date:\s(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\s(?:January|February|March|April|May|June|July|August|September|October|November|December)\s202(?:5|6|7|8|9)
field:"body.current_thread.text" kind:regex_match value:"Date:\s(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\s(?:January|February|March|April|May|June|July|August|September|October|November|December)\s202(?:5|6|7|8|9)"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Body: Suspicious table template fingerprint

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

Detects messages matching a specific HTML template fingerprint characterized by a table containing both 'Important' and 'Company' text nodes. This pattern is associated with a known malicious message template used to deceive recipients.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesSocial engineering, Evasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • type

Rule body

type.inbound
// table template fingerprint containing "important" and "company"
and any(html.xpath(body.html, '//td[contains(@style, "background-color")]').nodes,
        .display_text == "Important"
)
and any(html.xpath(body.html, '//td[contains(@style, "background-color")]').nodes,
        .display_text == "Company"
)

Detection logic

Scope: inbound message.

Detects messages matching a specific HTML template fingerprint characterized by a table containing both 'Important' and 'Company' text nodes. This pattern is associated with a known malicious message template used to deceive recipients.

  1. inbound message
  2. any of html.xpath(body.html, '//td[contains(@style, "background-color")]').nodes where:
    • .display_text is 'Important'
  3. any of html.xpath(body.html, '//td[contains(@style, "background-color")]').nodes where:
    • .display_text is 'Company'

Inspects: body.html, type.inbound. Sensors: html.xpath.

Indicators matched (2)

FieldMatchValue
html.xpath(body.html, '//td[contains(@style, "background-color")]').nodes[].display_textequalsImportant
html.xpath(body.html, '//td[contains(@style, "background-color")]').nodes[].display_textequalsCompany

Stages and Predicates

Stage 1: mql_rule

and
  any(html.xpath(body.html, '//td[contains(@style, "background-color")]').nodes)
    html.xpath(body.html, '//td[contains(@style, "background-color")]').nodes.display_text eq "Company"
  any(html.xpath(body.html, '//td[contains(@style, "background-color")]').nodes)
    html.xpath(body.html, '//td[contains(@style, "background-color")]').nodes.display_text eq "Important"
  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"

Body: Yellow highlighted text markers

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

Detects messages containing multiple HTML span elements with yellow background highlighting (rgb(255, 241, 0)) and data-markjs attributes, potentially indicating evasion techniques through visual markup manipulation.

Threat classification

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

CategoryValues
Attack typesCredential Phishing, BEC/Fraud
Tactics and techniquesEvasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.html
  • type

Rule body

type.inbound
and regex.icount(body.html.raw, 'data-markjs="true"') >= 2
and strings.icontains(body.html.raw, 'background-color: rgb(255, 241, 0)')

Detection logic

Scope: inbound message.

Detects messages containing multiple HTML span elements with yellow background highlighting (rgb(255, 241, 0)) and data-markjs attributes, potentially indicating evasion techniques through visual markup manipulation.

  1. inbound message
  2. regex.icount(body.html.raw, 'data-markjs="true"') ≥ 2
  3. body.html.raw contains 'background-color: rgb(255, 241, 0)'

Inspects: body.html.raw, type.inbound. Sensors: regex.icount, strings.icontains.

Indicators matched (2)

FieldMatchValue
regex.icountregexdata-markjs="true"
strings.icontainssubstringbackground-color: rgb(255, 241, 0)

Stages and Predicates

Stage 1: mql_rule

and
  body.html.raw contains "background-color: rgb(255, 241, 0)"
  regex.icount func_call "regex.icount(body.html.raw, \"data-markjs=\"true\"\") >= 2"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Brand impersonation: Microsoft logo or suspicious language with open redirect

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

Message contains a Microsoft logo or suspicious terms and use of an open redirect. This has been exploited in the wild to impersonate Microsoft.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesImpersonation: Brand, Open redirect, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • attachments
  • body
  • body.links
  • headers
  • sender.email
  • type

Rule body

type.inbound
and (length(attachments) < 5 or length(body.links) < 10)
// Microsoft logo
and (
  any(attachments,
      .file_type in $file_types_images
      and any(ml.logo_detect(.).brands, strings.starts_with(.name, "Microsoft"))
  )
  or any(attachments,
         .file_type in $file_types_images
         and (
           any(file.explode(.),
               2 of (
                 strings.ilike(.scan.ocr.raw, "*password*"),
                 strings.ilike(.scan.ocr.raw, "*unread messages*"),
                 strings.ilike(.scan.ocr.raw, "*Shared Documents*"),
                 strings.ilike(.scan.ocr.raw, "*expiration*"),
                 strings.ilike(.scan.ocr.raw, "*office*"),
                 strings.ilike(.scan.ocr.raw, "*expire*"),
                 strings.ilike(.scan.ocr.raw, "*expiring*"),
                 strings.ilike(.scan.ocr.raw, "*kindly*"),
                 strings.ilike(.scan.ocr.raw, "*renew*"),
                 strings.ilike(.scan.ocr.raw, "*review"),
                 strings.ilike(.scan.ocr.raw, "*emails failed*"),
                 strings.ilike(.scan.ocr.raw, "*kicked out*"),
                 strings.ilike(.scan.ocr.raw, "*prevented*"),
                 strings.ilike(.scan.ocr.raw, "*storage quota*"),
                 strings.ilike(.scan.ocr.raw, "*required now"),
                 strings.ilike(.scan.ocr.raw, "*cache*"),
                 strings.ilike(.scan.ocr.raw, "*qr code*"),
                 strings.ilike(.scan.ocr.raw, "*barcode*"),
                 strings.ilike(.scan.ocr.raw, "*security update*"),
                 strings.ilike(.scan.ocr.raw, "*quarantine*")
               )
           )
         )
  )
)

// open redirect
and any(body.links,
        any(.href_url.rewrite.encoders, strings.icontains(., "open_redirect"))
        and not .href_url.domain.root_domain in $org_domains
)
and not (length(headers.references) > 0 or headers.in_reply_to is not null)
and sender.email.domain.root_domain not in $org_domains
and sender.email.domain.root_domain not in (
  "bing.com",
  "microsoft.com",
  "microsoftonline.com",
  "microsoftstoreemail.com",
  "microsoftsupport.com",
  "microsoft365.com",
  "office.com",
  "onedrive.com",
  "sharepointonline.com",
  "yammer.com"
)

Detection logic

Scope: inbound message.

Message contains a Microsoft logo or suspicious terms and use of an open redirect. This has been exploited in the wild to impersonate Microsoft.

  1. inbound message
  2. any of:
    • length(attachments) < 5
    • length(body.links) < 10
  3. any of:
    • any of attachments where all hold:
      • .file_type in $file_types_images
      • any of ml.logo_detect(.).brands where:
        • .name starts with 'Microsoft'
    • any of attachments where all hold:
      • .file_type in $file_types_images
      • any of file.explode(.) where:
        • at least 2 of 20: .scan.ocr.raw matches any of 20 patterns
          • *password*
          • *unread messages*
          • *Shared Documents*
          • *expiration*
          • *office*
          • *expire*
          • *expiring*
          • *kindly*
          • *renew*
          • *review
          • *emails failed*
          • *kicked out*
          • *prevented*
          • *storage quota*
          • *required now
          • *cache*
          • *qr code*
          • *barcode*
          • *security update*
          • *quarantine*
  4. any of body.links where all hold:
    • any of .href_url.rewrite.encoders where:
      • . contains 'open_redirect'
    • not:
      • .href_url.domain.root_domain in $org_domains
  5. none of:
    • length(headers.references) > 0
    • headers.in_reply_to is set
  6. sender.email.domain.root_domain not in $org_domains
  7. sender.email.domain.root_domain not in ('bing.com', 'microsoft.com', 'microsoftonline.com', 'microsoftstoreemail.com', 'microsoftsupport.com', 'microsoft365.com', 'office.com', 'onedrive.com', 'sharepointonline.com', 'yammer.com')

Inspects: attachments[].file_type, body.links, body.links[].href_url.domain.root_domain, body.links[].href_url.rewrite.encoders, headers.in_reply_to, headers.references, sender.email.domain.root_domain, type.inbound. Sensors: file.explode, ml.logo_detect, strings.icontains, strings.ilike, strings.starts_with. Reference lists: $file_types_images, $org_domains.

Indicators matched (22)

FieldMatchValue
strings.starts_withprefixMicrosoft
strings.ilikesubstring*password*
strings.ilikesubstring*unread messages*
strings.ilikesubstring*Shared Documents*
strings.ilikesubstring*expiration*
strings.ilikesubstring*office*
strings.ilikesubstring*expire*
strings.ilikesubstring*expiring*
strings.ilikesubstring*kindly*
strings.ilikesubstring*renew*
strings.ilikesubstring*review
strings.ilikesubstring*emails failed*
10 more
strings.ilikesubstring*kicked out*
strings.ilikesubstring*prevented*
strings.ilikesubstring*storage quota*
strings.ilikesubstring*required now
strings.ilikesubstring*cache*
strings.ilikesubstring*qr code*
strings.ilikesubstring*barcode*
strings.ilikesubstring*security update*
strings.ilikesubstring*quarantine*
strings.icontainssubstringopen_redirect

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(attachments)
      and
        any(file.explode(attachments))
          or
            file.explode(attachments[])[].scan.ocr.raw ends_with "required now"
            file.explode(attachments[])[].scan.ocr.raw ends_with "review"
            file.explode(attachments[])[].scan.ocr.raw match "Shared Documents"
            file.explode(attachments[])[].scan.ocr.raw match "barcode"
            file.explode(attachments[])[].scan.ocr.raw match "cache"
            file.explode(attachments[])[].scan.ocr.raw match "emails failed"
            file.explode(attachments[])[].scan.ocr.raw match "expiration"
            file.explode(attachments[])[].scan.ocr.raw match "expire"
            file.explode(attachments[])[].scan.ocr.raw match "expiring"
            file.explode(attachments[])[].scan.ocr.raw match "kicked out"
            file.explode(attachments[])[].scan.ocr.raw match "kindly"
            file.explode(attachments[])[].scan.ocr.raw match "office"
            file.explode(attachments[])[].scan.ocr.raw match "password"
            file.explode(attachments[])[].scan.ocr.raw match "prevented"
            file.explode(attachments[])[].scan.ocr.raw match "qr code"
            file.explode(attachments[])[].scan.ocr.raw match "quarantine"
            file.explode(attachments[])[].scan.ocr.raw match "renew"
            file.explode(attachments[])[].scan.ocr.raw match "security update"
            file.explode(attachments[])[].scan.ocr.raw match "storage quota"
            file.explode(attachments[])[].scan.ocr.raw match "unread messages"
         macro "attachments[].file_type in file_types_images"
    any(attachments)
      and
        any(ml.logo_detect(attachments).brands)
          ml.logo_detect(attachments).brands.name starts_with "Microsoft"
         macro "attachments[].file_type in file_types_images"
  any(body.links)
    and
      any(body.links.href_url.rewrite.encoders)
        body.links.href_url.rewrite.encoders contains "open_redirect"
      not
         macro "body.links[].href_url.domain.root_domain in org_domains"
  not
    or
      headers.in_reply_to is_not_null
      headers.references length_compare "0"
  or
    attachments length_compare "5"
    body.links length_compare "10"
  not
    sender.email.domain.root_domain in ["bing.com", "microsoft.com", "microsoft365.com", "microsoftonline.com", "microsoftstoreemail.com", "microsoftsupport.com", "office.com", "onedrive.com", "sharepointonline.com", "yammer.com"]
  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"
sender.email.domain.root_domaininbing.com, microsoft.com, microsoft365.com, microsoftonline.com, microsoftstoreemail.com, microsoftsupport.com, office.com, onedrive.com, sharepointonline.com, yammer.comexcludes:sender.email.domain.root_domain

Indicators

These rows show field, operator, and value matches.

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

Business Email Compromise (BEC) attempt from unsolicited sender

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

Detects potential Business Email Compromise (BEC) attacks by analyzing text within the email body from unsolicited senders.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesSocial engineering, Spoofing

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • headers.reply_to
  • headers.return_path
  • sender.email
  • type

Rule body

type.inbound
and any(ml.nlu_classifier(body.current_thread.text).intents,
        .name in ("bec") and .confidence == "high"
)
and 
// mismatched From and Reply-to
(
  (
    length(headers.reply_to) > 0
    and all(headers.reply_to,
            .email.domain.root_domain != sender.email.domain.root_domain
    )
  )
  or not headers.auth_summary.dmarc.pass
  or not headers.auth_summary.spf.pass
)

// negate "via" senders via dmarc authentication or gmail autoforwards
and not (
  strings.ilike(headers.return_path.local_part, "*+caf_=*")
  and strings.contains(sender.display_name, "via")
  and (headers.auth_summary.dmarc.pass)
)
and (
  not profile.by_sender().solicited
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)

Detection logic

Scope: inbound message.

Detects potential Business Email Compromise (BEC) attacks by analyzing text within the email body from unsolicited senders.

  1. inbound message
  2. any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
    • .name in ('bec')
    • .confidence is 'high'
  3. any of:
    • all of:
      • length(headers.reply_to) > 0
      • all of headers.reply_to where:
        • .email.domain.root_domain is not sender.email.domain.root_domain
    • not:
      • headers.auth_summary.dmarc.pass
    • not:
      • headers.auth_summary.spf.pass
  4. not:
    • all of:
      • headers.return_path.local_part matches '*+caf_=*'
      • sender.display_name contains 'via'
      • headers.auth_summary.dmarc.pass
  5. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, headers.reply_to, headers.reply_to[].email.domain.root_domain, headers.return_path.local_part, sender.display_name, sender.email.domain.root_domain, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, strings.contains, strings.ilike.

Indicators matched (2)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).intents[].namememberbec
ml.nlu_classifier(body.current_thread.text).intents[].confidenceequalshigh

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      not
        profile.by_sender func_call "profile.by_sender().any_messages_benign"
      profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
    not
      profile.by_sender func_call "profile.by_sender().solicited"
  not
    and
      headers.auth_summary.dmarc.pass eq "true"
      headers.return_path.local_part match "+caf_="
      sender.display_name contains "via"
  or
    not
      headers.auth_summary.dmarc.pass eq "true"
    not
      headers.auth_summary.spf.pass eq "true"
    and
      headers.reply_to length_compare "0"
       macro "all(headers.reply_to)"
  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 "bec"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

Indicators

These rows show field, operator, and value matches.

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

Business Email Compromise (BEC) attempt from untrusted sender

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

Detects potential Business Email Compromise (BEC) attacks by analyzing text within the email body from first-time senders.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • attachments
  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • subject
  • type

Rule body

type.inbound
and any(ml.nlu_classifier(body.current_thread.text).intents,
        .name in ("bec") and .confidence == "high"
)
// negating legit replies
and not (
  (
    (
      subject.is_reply
      or subject.is_forward
      // out of office auto-reply
      // the NLU model will handle these better natively soon
      or strings.istarts_with(subject.subject, "Automatic reply:")
    )
    and (length(headers.references) > 0 or headers.in_reply_to is not null)
  )
  or (
    // calendar invite responses
    regex.icontains(subject.base,
                    '(?:Accepted|Declined|New Time Proposed|Tentative):'
    )
    and any(attachments, .content_type == "text/calendar")
    and profile.by_sender_email().solicited
  )
)
and (
  not profile.by_sender().solicited
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)

// negate highly trusted sender domains unless they fail DMARC authentication
and (
  (
    sender.email.domain.root_domain in $high_trust_sender_root_domains
    and not headers.auth_summary.dmarc.pass
  )
  or sender.email.domain.root_domain not in $high_trust_sender_root_domains
)

Detection logic

Scope: inbound message.

Detects potential Business Email Compromise (BEC) attacks by analyzing text within the email body from first-time senders.

  1. inbound message
  2. any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
    • .name in ('bec')
    • .confidence is 'high'
  3. none of:
    • all of:
      • any of:
        • subject.is_reply
        • subject.is_forward
        • subject.subject starts with 'Automatic reply:'
      • any of:
        • length(headers.references) > 0
        • headers.in_reply_to is set
    • all of:
      • subject.base matches '(?:Accepted|Declined|New Time Proposed|Tentative):'
      • any of attachments where:
        • .content_type is 'text/calendar'
      • profile.by_sender_email().solicited
  4. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
  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

Inspects: attachments[].content_type, body.current_thread.text, headers.auth_summary.dmarc.pass, headers.in_reply_to, headers.references, sender.email.domain.root_domain, subject.base, subject.is_forward, subject.is_reply, subject.subject, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, profile.by_sender_email, regex.icontains, strings.istarts_with. Reference lists: $high_trust_sender_root_domains.

Indicators matched (2)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).intents[].namememberbec
ml.nlu_classifier(body.current_thread.text).intents[].confidenceequalshigh

Stages and Predicates

Stage 1: mql_rule

and
  not
    or
      and
        any(attachments)
          attachments.content_type eq "text/calendar"
        profile.by_sender_email func_call "profile.by_sender_email().solicited"
        subject.base regex_match "(?:Accepted|Declined|New Time Proposed|Tentative):"
      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"
          subject.subject starts_with "Automatic reply:"
  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
        profile.by_sender func_call "profile.by_sender().any_messages_benign"
      profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
    not
      profile.by_sender func_call "profile.by_sender().solicited"
  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 "bec"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
attachmentsarray_any(no value, null check)excludes:attachments
subject.baseregex_match(?:Accepted|Declined|New Time Proposed|Tentative):excludes:subject.base field:"subject.base" value:"(?:Accepted|Declined|New Time Proposed|Tentative):"
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"
subject.subjectstarts_withAutomatic reply:excludes:subject.subject field:"subject.subject" value:"Automatic reply:"

Indicators

These rows show field, operator, and value matches.

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

Business Email Compromise (BEC) with request for mobile number

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

This rule detects unsolicited messages with a small plain text body, that is attempting to solicit a mobile number.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • headers
  • subject
  • type

Rule body

type.inbound
and (
  length(body.current_thread.text) < 500
  or any(map(filter(ml.nlu_classifier(body.current_thread.text).entities,
                    .name == "disclaimer"
             ),
             .text
         ),
         (length(body.current_thread.text) - length(.)) < 500
  )
)
and length(attachments) == 0
and regex.icontains(body.current_thread.text,
                    '(?:mobile|contact|current|reliable|latest).{0,10}(?:phone|number|#|\bno)|whatsapp|\bcell|personalcell|(?:share|what).{0,25}number.{0,15}(?:connect|reach|text|message|contact|call)|(?:\bdrop|which|send.{0,5}your|best).{0,25}(?:number|\bnum\b|#).{0,15}(?:(?:connect|reach|contact|call).{0,5}you|text|message|works?\b|stay connected|hit you up)|forward.{0,25}(?:\bnum\b|#)|get (?:your.{0,25}(?:number|\bnum\b|#)|in touch.{0,15}(?:via|by|through).{0,10}(?:text|phone|cell|sms|whatsapp))|(?:provide|confirm|reply.{0,15}with).{0,25}(?:direct|preferred|personal).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})?(?:number|\bnum\b|#|line)|(?:share|send).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})(?:number|\bnum\b|#|line)|(?:share|send).{0,25}preferred.{0,15}(?:text.?enabled.{0,15})?(?:number|\bnum\b|#|line)|(?:share|send|provide).{0,25}preferred.{0,25}(?:contact|communication).{0,15}(?:method|info|means|way)|(?:direct|preferred).{0,15}line.{0,15}(?:for|to|via).{0,10}(?:text|call|reach|contact|sms)|have.{0,15}preferred.{0,10}number|(?:share|send|provide).{0,25}way.{0,25}(?:reach|contact).{0,25}(?:call|text|sms|whatsapp)'
)
and (
  any(ml.nlu_classifier(body.current_thread.text).intents,
      .name in ("bec", "advance_fee") and .confidence != "low"
  )
  or (
    // confidence can be low on very short bodies
    length(body.current_thread.text) < 550
    and (
      any(ml.nlu_classifier(body.current_thread.text).intents, .name == "bec")
      or any(ml.nlu_classifier(sender.display_name).intents, .name == "bec")
      or any(ml.nlu_classifier(body.current_thread.text).entities,
             strings.icontains(.text, "kindly")
      )
    )
  )
)
and (
  (
    (length(headers.references) > 0 or headers.in_reply_to is null)
    and not (
      (
        strings.istarts_with(subject.subject, "RE:")
        or strings.istarts_with(subject.subject, "RES:")
        or strings.istarts_with(subject.subject, "R:")
        or strings.istarts_with(subject.subject, "ODG:")
        or strings.istarts_with(subject.subject, "答复:")
        or strings.istarts_with(subject.subject, "AW:")
        or strings.istarts_with(subject.subject, "TR:")
        or strings.istarts_with(subject.subject, "FWD:")
        or regex.imatch(subject.subject,
                        '(\[[^\]]+\]\s?){0,3}(re|fwd?|automat.*)\s?:.*'
        )
      )
    )
  )
  or length(headers.references) == 0
)
and (
  not profile.by_sender().solicited
  or profile.by_sender().any_messages_malicious_or_spam
)
and not profile.by_sender().any_messages_benign

Detection logic

Scope: inbound message.

This rule detects unsolicited messages with a small plain text body, that is attempting to solicit a mobile number.

  1. inbound message
  2. any of:
    • length(body.current_thread.text) < 500
    • any of map(...) where:
      • length(body.current_thread.text) - length(.) < 500
  3. length(attachments) is 0
  4. body.current_thread.text matches '(?:mobile|contact|current|reliable|latest).{0,10}(?:phone|number|#|\\bno)|whatsapp|\\bcell|personalcell|(?:share|what).{0,25}number.{0,15}(?:connect|reach|text|message|contact|call)|(?:\\bdrop|which|send.{0,5}your|best).{0,25}(?:number|\\bnum\\b|#).{0,15}(?:(?:connect|reach|contact|call).{0,5}you|text|message|works?\\b|stay connected|hit you up)|forward.{0,25}(?:\\bnum\\b|#)|get (?:your.{0,25}(?:number|\\bnum\\b|#)|in touch.{0,15}(?:via|by|through).{0,10}(?:text|phone|cell|sms|whatsapp))|(?:provide|confirm|reply.{0,15}with).{0,25}(?:direct|preferred|personal).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})?(?:number|\\bnum\\b|#|line)|(?:share|send).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})(?:number|\\bnum\\b|#|line)|(?:share|send).{0,25}preferred.{0,15}(?:text.?enabled.{0,15})?(?:number|\\bnum\\b|#|line)|(?:share|send|provide).{0,25}preferred.{0,25}(?:contact|communication).{0,15}(?:method|info|means|way)|(?:direct|preferred).{0,15}line.{0,15}(?:for|to|via).{0,10}(?:text|call|reach|contact|sms)|have.{0,15}preferred.{0,10}number|(?:share|send|provide).{0,25}way.{0,25}(?:reach|contact).{0,25}(?:call|text|sms|whatsapp)'
  5. any of:
    • any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
      • .name in ('bec', 'advance_fee')
      • .confidence is not 'low'
    • all of:
      • length(body.current_thread.text) < 550
      • any of:
        • any of ml.nlu_classifier(body.current_thread.text).intents where:
          • .name is 'bec'
        • any of ml.nlu_classifier(sender.display_name).intents where:
          • .name is 'bec'
        • any of ml.nlu_classifier(body.current_thread.text).entities where:
          • .text contains 'kindly'
  6. any of:
    • all of:
      • any of:
        • length(headers.references) > 0
        • headers.in_reply_to is missing
      • none of:
        • subject.subject starts with 'RE:'
        • subject.subject starts with 'RES:'
        • subject.subject starts with 'R:'
        • subject.subject starts with 'ODG:'
        • subject.subject starts with '答复:'
        • subject.subject starts with 'AW:'
        • subject.subject starts with 'TR:'
        • subject.subject starts with 'FWD:'
        • subject.subject matches '(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?|automat.*)\\s?:.*'
    • length(headers.references) is 0
  7. any of:
    • not:
      • profile.by_sender().solicited
    • profile.by_sender().any_messages_malicious_or_spam
  8. not:
    • profile.by_sender().any_messages_benign

Inspects: body.current_thread.text, headers.in_reply_to, headers.references, sender.display_name, subject.subject, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, regex.icontains, regex.imatch, strings.icontains, strings.istarts_with.

Indicators matched (7)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsdisclaimer
regex.icontainsregex(?:mobile|contact|current|reliable|latest).{0,10}(?:phone|number|#|\bno)|whatsapp|\bcell|personalcell|(?:share|what).{0,25}number.{0,15}(?:connect|reach|text|message|contact|call)|(?:\bdrop|which|send.{0,5}your|best).{0,25}(?:number|\bnum\b|#).{0,15}(?:(?:connect|reach|contact|call).{0,5}you|text|message|works?\b|stay connected|hit you up)|forward.{0,25}(?:\bnum\b|#)|get (?:your.{0,25}(?:number|\bnum\b|#)|in touch.{0,15}(?:via|by|through).{0,10}(?:text|phone|cell|sms|whatsapp))|(?:provide|confirm|reply.{0,15}with).{0,25}(?:direct|preferred|personal).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})?(?:number|\bnum\b|#|line)|(?:share|send).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})(?:number|\bnum\b|#|line)|(?:share|send).{0,25}preferred.{0,15}(?:text.?enabled.{0,15})?(?:number|\bnum\b|#|line)|(?:share|send|provide).{0,25}preferred.{0,25}(?:contact|communication).{0,15}(?:method|info|means|way)|(?:direct|preferred).{0,15}line.{0,15}(?:for|to|via).{0,10}(?:text|call|reach|contact|sms)|have.{0,15}preferred.{0,10}number|(?:share|send|provide).{0,25}way.{0,25}(?:reach|contact).{0,25}(?:call|text|sms|whatsapp)
ml.nlu_classifier(body.current_thread.text).intents[].namememberbec
ml.nlu_classifier(body.current_thread.text).intents[].namememberadvance_fee
ml.nlu_classifier(body.current_thread.text).intents[].nameequalsbec
ml.nlu_classifier(sender.display_name).intents[].nameequalsbec
strings.icontainssubstringkindly

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        any(ml.nlu_classifier(body.current_thread.text).entities)
          ml.nlu_classifier(body.current_thread.text).entities.text contains "kindly"
        any(ml.nlu_classifier(body.current_thread.text).intents)
          ml.nlu_classifier(body.current_thread.text).intents.name eq "bec"
        any(ml.nlu_classifier(sender.display_name).intents)
          ml.nlu_classifier(sender.display_name).intents.name eq "bec"
      body.current_thread.text length_compare "550"
    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 in ["advance_fee", "bec"]
  or
    and
      not
        or
          subject.subject regex_match "(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?|automat.*)\\s?:.*"
          subject.subject starts_with "AW:"
          subject.subject starts_with "FWD:"
          subject.subject starts_with "ODG:"
          subject.subject starts_with "R:"
          subject.subject starts_with "RE:"
          subject.subject starts_with "RES:"
          subject.subject starts_with "TR:"
          subject.subject starts_with "答复:"
      or
        headers.in_reply_to is_null
        headers.references length_compare "0"
    headers.references length_compare "0"
  or
    not
      profile.by_sender func_call "profile.by_sender().solicited"
    profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
  or
    any(map(...))
       macro "(length(body.current_thread.text) - length(map(...)[])) < 500"
    body.current_thread.text length_compare "500"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  attachments length_compare "0"
  body.current_thread.text regex_match "(?:mobile|contact|current|reliable|latest).{0,10}(?:phone|number|#|\\bno)|whatsapp|\\bcell|personalcell|(?:share|what).{0,25}number.{0,15}(?:connect|reach|text|message|contact|call)|(?:\\bdrop|which|send.{0,5}your|best).{0,25}(?:number|\\bnum\\b|#).{0,15}(?:(?:connect|reach|contact|call).{0,5}you|text|message|works?\\b|stay connected|hit you up)|forward.{0,25}(?:\\bnum\\b|#)|get (?:your.{0,25}(?:number|\\bnum\\b|#)|in touch.{0,15}(?:via|by|through).{0,10}(?:text|phone|cell|sms|whatsapp))|(?:provide|confirm|reply.{0,15}with).{0,25}(?:direct|preferred|personal).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})?(?:number|\\bnum\\b|#|line)|(?:share|send).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})(?:number|\\bnum\\b|#|line)|(?:share|send).{0,25}preferred.{0,15}(?:text.?enabled.{0,15})?(?:number|\\bnum\\b|#|line)|(?:share|send|provide).{0,25}preferred.{0,25}(?:contact|communication).{0,15}(?:method|info|means|way)|(?:direct|preferred).{0,15}line.{0,15}(?:for|to|via).{0,10}(?:text|call|reach|contact|sms)|have.{0,15}preferred.{0,10}number|(?:share|send|provide).{0,25}way.{0,25}(?:reach|contact).{0,25}(?:call|text|sms|whatsapp)"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
    • (?:mobile|contact|current|reliable|latest).{0,10}(?:phone|number|#|\bno)
    • whatsapp
    • \bcell
    • personalcell
    • (?:share|what).{0,25}number.{0,15}(?:connect|reach|text|message|contact|call)
    • (?:\bdrop|which|send.{0,5}your|best).{0,25}(?:number|\bnum\b|#).{0,15}(?:(?:connect|reach|contact|call).{0,5}you|text|message|works?\b|stay connected|hit you up)
    • forward.{0,25}(?:\bnum\b|#)
    • get (?:your.{0,25}(?:number|\bnum\b|#)|in touch.{0,15}(?:via|by|through).{0,10}(?:text|phone|cell|sms|whatsapp))
    • (?:provide|confirm|reply.{0,15}with).{0,25}(?:direct|preferred|personal).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})?(?:number|\bnum\b|#|line)
    • (?:share|send).{0,25}(?:direct|preferred).{0,15}(?:text.?enabled.{0,15})?(?:phone.{0,5})(?:number|\bnum\b|#|line)
    • (?:share|send).{0,25}preferred.{0,15}(?:text.?enabled.{0,15})?(?:number|\bnum\b|#|line)
    • (?:share|send|provide).{0,25}preferred.{0,25}(?:contact|communication).{0,15}(?:method|info|means|way)
    • (?:direct|preferred).{0,15}line.{0,15}(?:for|to|via).{0,10}(?:text|call|reach|contact|sms)
    • have.{0,15}preferred.{0,10}number
    • (?:share|send|provide).{0,25}way.{0,25}(?:reach|contact).{0,25}(?:call|text|sms|whatsapp)
field:"body.current_thread.text" kind:regex_match
headers.in_reply_tois_null
  • (no value, null check)
field:"headers.in_reply_to" kind:is_null
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

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

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

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

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.previous_threads
  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule body

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

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

Detection logic

Scope: inbound message.

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

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

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

Indicators matched (9)

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

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        any(body.previous_threads)
          any(ml.nlu_classifier(body.previous_threads.text).entities)
            ml.nlu_classifier(body.previous_threads.text).entities.text contains "kindly"
        any(body.previous_threads)
          any(ml.nlu_classifier(body.previous_threads.text).intents)
            ml.nlu_classifier(body.previous_threads.text).intents.name eq "bec"
        any(ml.nlu_classifier(sender.display_name).intents)
          ml.nlu_classifier(sender.display_name).intents.name eq "bec"
      any(body.previous_threads)
        body.previous_threads.text length_compare "550"
    any(body.previous_threads)
      any(ml.nlu_classifier(body.previous_threads.text).intents)
        and
          ml.nlu_classifier(body.previous_threads[].text).intents[].confidence in ["high", "medium"]
          ml.nlu_classifier(body.previous_threads[].text).intents[].name in ["advance_fee", "bec"]
  any(body.previous_threads)
    and
      or
        any(map(...))
           macro "(length(body.previous_threads[].text) - length(map(...)[])) < 500"
        body.previous_threads.text length_compare "500"
      body.previous_threads.text regex_match "(?:mobile|suitable|contact|current|cell|call|another).{0,10}(?:phone|number|#|\\bno)|personal\\s?(?:phone|cell|number|#)|whatsapp|\\bcell|personalcell|(?:reliable|recent).{0,30}(?:phone|number).{0,15}contact|(?:share|send|confirm).{0,20}number.{0,25}(?:text|sms|whatsapp|contact|reach\\syou[\\s,.\\?](?:by|whether|when|for))|best\\s+(?:number|#).{0,10}(?:text|sms|contact|call|message|in[-\\s]?touch|to\\suse)|best\\s+way.{0,25}(?:reach|contact).{0,25}(?:text|sms|whatsapp|call|message)"
  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
    not
      profile.by_sender func_call "profile.by_sender().solicited"
    profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
  or
    headers.in_reply_to is_not_null
    headers.references length_compare "0"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  attachments length_compare "0"
  body.previous_threads length_compare "0"
  body.previous_threads length_compare "3"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
headers.in_reply_tois_not_null
  • (no value, null check)
field:"headers.in_reply_to" kind:is_not_null
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Callback phishing solicitation in message body

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

A fraudulent invoice/receipt found in the body of the message. Callback phishing is an attempt by an attacker to solicit the victim (recipient) to call a phone number. The resulting interaction could lead to a multitude of attacks ranging from Financial theft, Remote Access Trojan (RAT) Installation or Ransomware Deployment.

Threat classification

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

CategoryValues
Attack typesCallback Phishing
Tactics and techniquesFree email provider, Impersonation: Brand, Out of band pivot, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and length(attachments) == 0
and (
  not profile.by_sender().solicited
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)
and (
  sender.email.domain.root_domain in $free_email_providers
  or sender.email.domain.tld in $suspicious_tlds
  or network.whois(sender.email.domain).found == false
  or headers.mailer in~ ("Microsoft CDO for Windows 2000")
  or (
    length(recipients.to) == 1
    and all(recipients.to, .email.domain.domain not in $org_domains)
  )
)
and (
  // this section is synced with attachment_callback_phish_with_pdf.yml and attachment_callback_phish_with_img.yml
  regex.icontains(body.current_thread.text,
                  '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'
  )
  or any(ml.logo_detect(file.message_screenshot()).brands,
         .name in ("PayPal", "Norton", "GeekSquad", "Ebay", "McAfee", "AT&T")
  )
  //
  // 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
  //
  or regex.icontains(beta.ocr(file.message_screenshot()).text,
                     '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'
  )
)
and length(body.current_thread.text) < 1750
and (
  (
    // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
    // however, the 3 of logic and requiring a phone number is specific to this rule in order to reduce FPs
    // caused by messages which mention cancelling or otherwise managing a subscription
    // it is also synced and below for message_screenshot OCR output
    3 of (
      strings.icontains(body.current_thread.text, 'purchase'),
      strings.icontains(body.current_thread.text, 'payment'),
      strings.icontains(body.current_thread.text, 'transaction'),
      strings.icontains(body.current_thread.text, 'subscription'),
      strings.icontains(body.current_thread.text, 'antivirus'),
      strings.icontains(body.current_thread.text, 'order'),
      strings.icontains(body.current_thread.text, 'support'),
      strings.icontains(body.current_thread.text, 'help line'),
      strings.icontains(body.current_thread.text, 'receipt'),
      strings.icontains(body.current_thread.text, 'invoice'),
      strings.icontains(body.current_thread.text, 'call'),
      strings.icontains(body.current_thread.text, 'cancel'),
      strings.icontains(body.current_thread.text, 'renew'),
      strings.icontains(body.current_thread.text, 'refund'),
      regex.icontains(body.current_thread.text, "(?:reach|contact) us at"),
      strings.icontains(body.current_thread.text, "+1"),
      strings.icontains(body.current_thread.text, "amount"),
      strings.icontains(body.current_thread.text, "charged"),
      strings.icontains(body.current_thread.text, "crypto"),
      strings.icontains(body.current_thread.text, "wallet address"),
      regex.icontains(body.current_thread.text, '\$\d{3}\.\d{2}\b'),
    )
    // phone number regex
    and regex.icontains(body.current_thread.text,
                        '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                        '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
    )
  )
  or (
    any(file.explode(file.message_screenshot()),
        // this seciton is synced with attachment_callback_phish_with_img.yml and attachment_callback_phish_with_pdf.yml
        // and above for current_thread.text
        3 of (
          strings.icontains(.scan.ocr.raw, 'purchase'),
          strings.icontains(.scan.ocr.raw, 'payment'),
          strings.icontains(.scan.ocr.raw, 'transaction'),
          strings.icontains(.scan.ocr.raw, 'subscription'),
          strings.icontains(.scan.ocr.raw, 'antivirus'),
          strings.icontains(.scan.ocr.raw, 'order'),
          strings.icontains(.scan.ocr.raw, 'support'),
          strings.icontains(.scan.ocr.raw, 'help line'),
          strings.icontains(.scan.ocr.raw, 'receipt'),
          strings.icontains(.scan.ocr.raw, 'invoice'),
          strings.icontains(.scan.ocr.raw, 'call'),
          strings.icontains(.scan.ocr.raw, 'helpdesk'),
          strings.icontains(.scan.ocr.raw, 'cancel'),
          strings.icontains(.scan.ocr.raw, 'renew'),
          strings.icontains(.scan.ocr.raw, 'refund'),
          regex.icontains(.scan.ocr.raw, "(?:reach|contact) us at"),
          strings.icontains(.scan.ocr.raw, '+1'),
          strings.icontains(.scan.ocr.raw, 'amount'),
          strings.icontains(.scan.ocr.raw, 'charged'),
          strings.icontains(.scan.ocr.raw, 'crypto'),
          strings.icontains(.scan.ocr.raw, 'wallet address'),
          regex.icontains(.scan.ocr.raw, '\$\d{3}\.\d{2}\b'),
        )
        // phone number regex
        and regex.icontains(.scan.ocr.raw,
                            '\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}',
                            '\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}'
        )

        // negate messages with previous threads.  While callback phishing with thread hijacking or with current_thread 
        // padded with whitespace and previous threads in the message has been observed, the intetion of using OCR is for image embedded callbacks
        and not regex.icount(.scan.ocr.raw, '(?:from|to|sent|date|cc|subject):') > 3
        // this notation of previous threads often only occurs once
        and not regex.icontains(.scan.ocr.raw, 'wrote:[\r\n]')
    )
  )
)
// not high trust sender domains
and (
  (
    sender.email.domain.root_domain in $high_trust_sender_root_domains
    and not headers.auth_summary.dmarc.pass
  )
  or sender.email.domain.root_domain not in $high_trust_sender_root_domains
)
and not strings.ends_with(headers.message_id, "@shopify.com>")

Detection logic

Scope: inbound message.

A fraudulent invoice/receipt found in the body of the message. Callback phishing is an attempt by an attacker to solicit the victim (recipient) to call a phone number. The resulting interaction could lead to a multitude of attacks ranging from Financial theft, Remote Access Trojan (RAT) Installation or Ransomware Deployment.

  1. inbound message
  2. length(attachments) is 0
  3. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
  4. any of:
    • sender.email.domain.root_domain in $free_email_providers
    • sender.email.domain.tld in $suspicious_tlds
    • network.whois(sender.email.domain).found is False
    • headers.mailer in ('Microsoft CDO for Windows 2000')
    • all of:
      • length(recipients.to) is 1
      • all of recipients.to where:
        • .email.domain.domain not in $org_domains
  5. any of:
    • body.current_thread.text matches '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'
    • any of ml.logo_detect(file.message_screenshot()).brands where:
      • .name in ('PayPal', 'Norton', 'GeekSquad', 'Ebay', 'McAfee', 'AT&T')
    • beta.ocr(file.message_screenshot()).text matches '(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)'
  6. length(body.current_thread.text) < 1750
  7. any of:
    • all of:
      • at least 3 of:
        • body.current_thread.text contains 'purchase'
        • body.current_thread.text contains 'payment'
        • body.current_thread.text contains 'transaction'
        • body.current_thread.text contains 'subscription'
        • body.current_thread.text contains 'antivirus'
        • body.current_thread.text contains 'order'
        • body.current_thread.text contains 'support'
        • body.current_thread.text contains 'help line'
        • body.current_thread.text contains 'receipt'
        • body.current_thread.text contains 'invoice'
        • body.current_thread.text contains 'call'
        • body.current_thread.text contains 'cancel'
        • body.current_thread.text contains 'renew'
        • body.current_thread.text contains 'refund'
        • body.current_thread.text matches '(?:reach|contact) us at'
        • body.current_thread.text contains '+1'
        • body.current_thread.text contains 'amount'
        • body.current_thread.text contains 'charged'
        • body.current_thread.text contains 'crypto'
        • body.current_thread.text contains 'wallet address'
        • body.current_thread.text matches '\\$\\d{3}\\.\\d{2}\\b'
      • body.current_thread.text matches any of 2 patterns
        • \+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
        • \+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
    • any of file.explode(...) where all hold:
      • at least 3 of:
        • .scan.ocr.raw contains 'purchase'
        • .scan.ocr.raw contains 'payment'
        • .scan.ocr.raw contains 'transaction'
        • .scan.ocr.raw contains 'subscription'
        • .scan.ocr.raw contains 'antivirus'
        • .scan.ocr.raw contains 'order'
        • .scan.ocr.raw contains 'support'
        • .scan.ocr.raw contains 'help line'
        • .scan.ocr.raw contains 'receipt'
        • .scan.ocr.raw contains 'invoice'
        • .scan.ocr.raw contains 'call'
        • .scan.ocr.raw contains 'helpdesk'
        • .scan.ocr.raw contains 'cancel'
        • .scan.ocr.raw contains 'renew'
        • .scan.ocr.raw contains 'refund'
        • .scan.ocr.raw matches '(?:reach|contact) us at'
        • .scan.ocr.raw contains '+1'
        • .scan.ocr.raw contains 'amount'
        • .scan.ocr.raw contains 'charged'
        • .scan.ocr.raw contains 'crypto'
        • .scan.ocr.raw contains 'wallet address'
        • .scan.ocr.raw matches '\\$\\d{3}\\.\\d{2}\\b'
      • .scan.ocr.raw matches any of 2 patterns
        • \+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
        • \+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
      • not:
        • regex.icount(.scan.ocr.raw, '(?:from|to|sent|date|cc|subject):') > 3
      • not:
        • .scan.ocr.raw matches 'wrote:[\\r\\n]'
  8. any of:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • not:
        • headers.auth_summary.dmarc.pass
    • sender.email.domain.root_domain not in $high_trust_sender_root_domains
  9. not:
    • headers.message_id ends with '@shopify.com>'

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, headers.mailer, headers.message_id, recipients.to, recipients.to[].email.domain.domain, sender.email.domain, sender.email.domain.root_domain, sender.email.domain.tld, type.inbound. Sensors: beta.ocr, file.explode, file.message_screenshot, ml.logo_detect, network.whois, profile.by_sender, regex.icontains, regex.icount, strings.ends_with, strings.icontains. Reference lists: $free_email_providers, $high_trust_sender_root_domains, $org_domains, $suspicious_tlds.

Indicators matched (32)

FieldMatchValue
headers.mailermemberMicrosoft CDO for Windows 2000
regex.icontainsregex(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)
ml.logo_detect(file.message_screenshot()).brands[].namememberPayPal
ml.logo_detect(file.message_screenshot()).brands[].namememberNorton
ml.logo_detect(file.message_screenshot()).brands[].namememberGeekSquad
ml.logo_detect(file.message_screenshot()).brands[].namememberEbay
ml.logo_detect(file.message_screenshot()).brands[].namememberMcAfee
ml.logo_detect(file.message_screenshot()).brands[].namememberAT&T
strings.icontainssubstringpurchase
strings.icontainssubstringpayment
strings.icontainssubstringtransaction
strings.icontainssubstringsubscription
20 more
strings.icontainssubstringantivirus
strings.icontainssubstringorder
strings.icontainssubstringsupport
strings.icontainssubstringhelp line
strings.icontainssubstringreceipt
strings.icontainssubstringinvoice
strings.icontainssubstringcall
strings.icontainssubstringcancel
strings.icontainssubstringrenew
strings.icontainssubstringrefund
regex.icontainsregex(?:reach|contact) us at
strings.icontainssubstring+1
strings.icontainssubstringamount
strings.icontainssubstringcharged
strings.icontainssubstringcrypto
strings.icontainssubstringwallet address
regex.icontainsregex\$\d{3}\.\d{2}\b
regex.icontainsregex\+?([ilo0-9]{1}.)?\(?[ilo0-9]{3}?\)?.[ilo0-9]{3}.?[ilo0-9]{4}
regex.icontainsregex\+?([ilo0-9]{1,2})?\s?\(?\d{3}\)?[\s\.\-⋅]{0,5}[ilo0-9]{3}[\s\.\-⋅]{0,5}[ilo0-9]{4}
strings.icontainssubstringhelpdesk

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(file.explode(...))
      and
        or
          file.explode(...).scan.ocr.raw contains "+1"
          file.explode(...).scan.ocr.raw contains "amount"
          file.explode(...).scan.ocr.raw contains "antivirus"
          file.explode(...).scan.ocr.raw contains "call"
          file.explode(...).scan.ocr.raw contains "cancel"
          file.explode(...).scan.ocr.raw contains "charged"
          file.explode(...).scan.ocr.raw contains "crypto"
          file.explode(...).scan.ocr.raw contains "help line"
          file.explode(...).scan.ocr.raw contains "helpdesk"
          file.explode(...).scan.ocr.raw contains "invoice"
          file.explode(...).scan.ocr.raw contains "order"
          file.explode(...).scan.ocr.raw contains "payment"
          file.explode(...).scan.ocr.raw contains "purchase"
          file.explode(...).scan.ocr.raw contains "receipt"
          file.explode(...).scan.ocr.raw contains "refund"
          file.explode(...).scan.ocr.raw contains "renew"
          file.explode(...).scan.ocr.raw contains "subscription"
          file.explode(...).scan.ocr.raw contains "support"
          file.explode(...).scan.ocr.raw contains "transaction"
          file.explode(...).scan.ocr.raw contains "wallet address"
          file.explode(...).scan.ocr.raw regex_match "(?:reach|contact) us at"
          file.explode(...).scan.ocr.raw regex_match "\\$\\d{3}\\.\\d{2}\\b"
        or
          file.explode(...).scan.ocr.raw regex_match "\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}"
          file.explode(...).scan.ocr.raw regex_match "\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}"
        not
          file.explode(...).scan.ocr.raw regex_match "wrote:[\\r\\n]"
        not
          regex.icount func_call "regex.icount(file.explode(...)[].scan.ocr.raw, \"(?:from|to|sent|date|cc|subject):\") > 3"
    and
      or
        body.current_thread.text contains "+1"
        body.current_thread.text contains "amount"
        body.current_thread.text contains "antivirus"
        body.current_thread.text contains "call"
        body.current_thread.text contains "cancel"
        body.current_thread.text contains "charged"
        body.current_thread.text contains "crypto"
        body.current_thread.text contains "help line"
        body.current_thread.text contains "invoice"
        body.current_thread.text contains "order"
        body.current_thread.text contains "payment"
        body.current_thread.text contains "purchase"
        body.current_thread.text contains "receipt"
        body.current_thread.text contains "refund"
        body.current_thread.text contains "renew"
        body.current_thread.text contains "subscription"
        body.current_thread.text contains "support"
        body.current_thread.text contains "transaction"
        body.current_thread.text contains "wallet address"
        body.current_thread.text regex_match "(?:reach|contact) us at"
        body.current_thread.text regex_match "\\$\\d{3}\\.\\d{2}\\b"
      or
        body.current_thread.text regex_match "\\+?([ilo0-9]{1,2})?\\s?\\(?\\d{3}\\)?[\\s\\.\\-⋅]{0,5}[ilo0-9]{3}[\\s\\.\\-⋅]{0,5}[ilo0-9]{4}"
        body.current_thread.text regex_match "\\+?([ilo0-9]{1}.)?\\(?[ilo0-9]{3}?\\)?.[ilo0-9]{3}.?[ilo0-9]{4}"
  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
        profile.by_sender func_call "profile.by_sender().any_messages_benign"
      profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
    not
      profile.by_sender func_call "profile.by_sender().solicited"
  or
    any(ml.logo_detect(file.message_screenshot()).brands)
      ml.logo_detect(file.message_screenshot()).brands.name in ["AT&T", "Ebay", "GeekSquad", "McAfee", "Norton", "PayPal"]
    beta.ocr(file.message_screenshot()).text regex_match "(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)"
    body.current_thread.text regex_match "(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)"
  or
    and
      recipients.to length_compare "1"
       macro "all(recipients.to)"
    headers.mailer eq "Microsoft CDO for Windows 2000"
    network.whois func_call "network.whois(sender.email.domain).found == false"
     macro "sender.email.domain.root_domain in free_email_providers"
     macro "sender.email.domain.tld in suspicious_tlds"
  not
    headers.message_id ends_with "@shopify.com>"
  attachments length_compare "0"
  body.current_thread.text length_compare "1750"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
headers.message_idends_with@shopify.com>excludes:headers.message_id field:"headers.message_id" value:"@shopify.com>"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
beta.ocr(file.message_screenshot()).textregex_match
  • (p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)
field:"beta.ocr(file.message_screenshot()).text" kind:regex_match value:"(p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)"
body.current_thread.textcontains
  • +1
  • amount
  • antivirus
  • call
  • cancel
  • charged
  • crypto
  • help line
  • invoice
  • order
  • payment
  • purchase
  • receipt
  • refund
  • renew
  • subscription
  • support
  • transaction
  • wallet address
field:"body.current_thread.text" kind:contains
body.current_thread.textregex_match
  • (?:reach|contact) us at
  • (p.{0,3}a.{0,3}y.{0,3}p.{0,3}a.{0,3}l|ma?c.?fee|n[o0]rt[o0]n|geek.{0,5}squad|ebay|symantec|best buy|lifel[o0]c|secure anywhere|starz|utilities premium|pc security|at&t)
  • $\d{3}.\d{2}\b
  • +?([ilo0-9]{1,2})?\s?(?\d{3})?[\s.\-⋅]{0,5}[ilo0-9]{3}[\s.\-⋅]{0,5}[ilo0-9]{4}
  • +?([ilo0-9]{1}.)?(?[ilo0-9]{3}?)?.[ilo0-9]{3}.?[ilo0-9]{4}
field:"body.current_thread.text" kind:regex_match
headers.mailerin
  • Microsoft CDO for Windows 2000
field:"headers.mailer" kind:in value:"Microsoft CDO for Windows 2000"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

COVID-19 themed fraud with sender and reply-to mismatch or compensation award

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

Detects potential COVID-19 themed BEC/Fraud scams by analyzing text within the email body for mentions of COVID-19 assistance, compensation, or awards from mismatched senders and other suspicious language.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesFree email provider, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound

// mismatched sender (From) and Reply-to + freemail
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 in $free_email_providers
  )
)

// use of honorific
and regex.icontains(body.current_thread.text,
                    '(?:Mr|Mrs|Ms|Miss|Dr|Prof|Sir|Lady|Rev)\.?[ \t]+',
                    'Dear Sir'
)

// mention of covid or an international organization
and regex.icontains(body.current_thread.text,
                    'international (court of justice|monetary fund)',
                    'united nations',
                    'western union',
                    'world bank',
                    'world health organization',
                    'interpol',
                    'treasury',
                    '\bFEMA\b',
                    '\bIMF\b'
)

// and mention of covid in subject or body
and (
  regex.icontains(subject.subject, 'covid(.{0,5}19)?\b')
  or regex.icontains(body.current_thread.text, 'covid(.{0,5}19)?\b')
)

// Check for compensation or award related language
and (
  2 of (
    any(ml.nlu_classifier(body.current_thread.text).entities,
        .name == "urgency"
    ),
    any(ml.nlu_classifier(body.current_thread.text).entities,
        .name == "request"
    ),
    any(ml.nlu_classifier(body.current_thread.text).entities,
        .name == "financial"
    )
  )
  or regex.icontains(subject.subject,
                     'compensation.{0,20}(award|fund)',
                     'covid.{0,20}(compensation|award)',
                     'selected.{0,30}(compensation|award)',
                     'claim your award',
                     'reference no'
  )
  or regex.icontains(body.current_thread.text,
                     'compensation.{0,20}(award|fund)',
                     'covid.{0,20}(compensation|award)',
                     'selected.{0,30}(compensation|award)',
                     'claim your award',
                     'reference no\W\s*[^\s]*cov(?:id)?(?:.{0,5}19)?\b'
  )
)

// negate highly trusted sender domains unless they fail DMARC authentication
and (
  (
    sender.email.domain.root_domain in $high_trust_sender_root_domains
    and not headers.auth_summary.dmarc.pass
  )
  or sender.email.domain.root_domain not in $high_trust_sender_root_domains
)

Detection logic

Scope: inbound message.

Detects potential COVID-19 themed BEC/Fraud scams by analyzing text within the email body for mentions of COVID-19 assistance, compensation, or awards from mismatched senders and other suspicious language.

  1. inbound message
  2. 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 in $free_email_providers
  3. body.current_thread.text matches any of 2 patterns
    • (?:Mr|Mrs|Ms|Miss|Dr|Prof|Sir|Lady|Rev)\.?[ \t]+
    • Dear Sir
  4. body.current_thread.text matches any of 9 patterns
    • international (court of justice|monetary fund)
    • united nations
    • western union
    • world bank
    • world health organization
    • interpol
    • treasury
    • \bFEMA\b
    • \bIMF\b
  5. any of:
    • subject.subject matches 'covid(.{0,5}19)?\\b'
    • body.current_thread.text matches 'covid(.{0,5}19)?\\b'
  6. any of:
    • at least 2 of:
      • any of ml.nlu_classifier(body.current_thread.text).entities where:
        • .name is 'urgency'
      • 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 'financial'
    • subject.subject matches any of 5 patterns
      • compensation.{0,20}(award|fund)
      • covid.{0,20}(compensation|award)
      • selected.{0,30}(compensation|award)
      • claim your award
      • reference no
    • body.current_thread.text matches any of 5 patterns
      • compensation.{0,20}(award|fund)
      • covid.{0,20}(compensation|award)
      • selected.{0,30}(compensation|award)
      • claim your award
      • reference no\W\s*[^\s]*cov(?:id)?(?:.{0,5}19)?\b
  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

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, headers.reply_to, headers.reply_to[].email.domain.root_domain, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: ml.nlu_classifier, regex.icontains. Reference lists: $free_email_providers, $high_trust_sender_root_domains.

Indicators matched (21)

FieldMatchValue
regex.icontainsregex(?:Mr|Mrs|Ms|Miss|Dr|Prof|Sir|Lady|Rev)\.?[ \t]+
regex.icontainsregexDear Sir
regex.icontainsregexinternational (court of justice|monetary fund)
regex.icontainsregexunited nations
regex.icontainsregexwestern union
regex.icontainsregexworld bank
regex.icontainsregexworld health organization
regex.icontainsregexinterpol
regex.icontainsregextreasury
regex.icontainsregex\bFEMA\b
regex.icontainsregex\bIMF\b
regex.icontainsregexcovid(.{0,5}19)?\b
9 more
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsurgency
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsrequest
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsfinancial
regex.icontainsregexcompensation.{0,20}(award|fund)
regex.icontainsregexcovid.{0,20}(compensation|award)
regex.icontainsregexselected.{0,30}(compensation|award)
regex.icontainsregexclaim your award
regex.icontainsregexreference no
regex.icontainsregexreference no\W\s*[^\s]*cov(?:id)?(?:.{0,5}19)?\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
    any(ml.nlu_classifier(body.current_thread.text).entities)
      ml.nlu_classifier(body.current_thread.text).entities.name eq "financial"
    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"
    body.current_thread.text regex_match "claim your award"
    body.current_thread.text regex_match "compensation.{0,20}(award|fund)"
    body.current_thread.text regex_match "covid.{0,20}(compensation|award)"
    body.current_thread.text regex_match "reference no\\W\\s*[^\\s]*cov(?:id)?(?:.{0,5}19)?\\b"
    body.current_thread.text regex_match "selected.{0,30}(compensation|award)"
    subject.subject regex_match "claim your award"
    subject.subject regex_match "compensation.{0,20}(award|fund)"
    subject.subject regex_match "covid.{0,20}(compensation|award)"
    subject.subject regex_match "reference no"
    subject.subject regex_match "selected.{0,30}(compensation|award)"
  or
    body.current_thread.text regex_match "(?:Mr|Mrs|Ms|Miss|Dr|Prof|Sir|Lady|Rev)\\.?[ \\t]+"
    body.current_thread.text regex_match "Dear Sir"
  or
    body.current_thread.text regex_match "\\bFEMA\\b"
    body.current_thread.text regex_match "\\bIMF\\b"
    body.current_thread.text regex_match "international (court of justice|monetary fund)"
    body.current_thread.text regex_match "interpol"
    body.current_thread.text regex_match "treasury"
    body.current_thread.text regex_match "united nations"
    body.current_thread.text regex_match "western union"
    body.current_thread.text regex_match "world bank"
    body.current_thread.text regex_match "world health organization"
  or
    body.current_thread.text regex_match "covid(.{0,5}19)?\\b"
    subject.subject regex_match "covid(.{0,5}19)?\\b"
  headers.reply_to length_compare "0"
  type.inbound eq "true"
   macro "all(headers.reply_to)"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • (?:Mr|Mrs|Ms|Miss|Dr|Prof|Sir|Lady|Rev).?[ \t]+
  • Dear Sir
  • \bFEMA\b
  • \bIMF\b
  • claim your award
  • compensation.{0,20}(award|fund)
  • covid(.{0,5}19)?\b
  • covid.{0,20}(compensation|award)
  • international (court of justice|monetary fund)
  • interpol
  • reference no\W\s*[^\s]*cov(?:id)?(?:.{0,5}19)?\b
  • selected.{0,30}(compensation|award)
  • treasury
  • united nations
  • western union
  • world bank
  • world health organization
field:"body.current_thread.text" kind:regex_match
subject.subjectregex_match
  • claim your award
  • compensation.{0,20}(award|fund)
  • covid(.{0,5}19)?\b
  • covid.{0,20}(compensation|award)
  • reference no
  • selected.{0,30}(compensation|award)
field:"subject.subject" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Credential theft with 'safe content' deception and social engineering topics

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

Detects messages containing credential theft language combined with social engineering topics like secure messages, notifications, or authentication alerts. The rule specifically identifies emails that deceptively claim to be from a 'safe sender' or contain 'safe content' in the first line, which is a common tactic used to bypass security filters and gain user trust.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesSocial engineering, Evasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • type

Rule body

type.inbound
and any(ml.nlu_classifier(body.current_thread.text).intents,
        .name == "cred_theft" and .confidence != "low"
)
and (
  any(ml.nlu_classifier(body.current_thread.text).topics,
      .name in (
        "Reminders and Notifications",
        "Secure Message",
        "Security and Authentication",
        "Voicemail Call and Missed Call Notifications",
        "E-Signature",
        "Financial Communications"
      )
  )
  or (
    length(distinct(filter(ml.nlu_classifier(body.current_thread.text).entities,
                           .name not in ("org", "recipient", "sender")
                    ),
                    .name
           )
    ) > 0
    and all(distinct(filter(ml.nlu_classifier(body.current_thread.text).entities,
                            .name not in ("org", "recipient", "sender")
                     ),
                     .name
            ),
            .name in ("request", "financial", "urgency")
    )
  )
)
and not any(ml.nlu_classifier(body.current_thread.text).topics,
            .name in (
              "Advertising and Promotions",
              "Newsletters and Digests",
              "News and Current Events",
              "Travel and Transportation"
            )
)
// check only the first line of the email
and any(regex.iextract(body.current_thread.text, "^[^\r\n]*"),
        length(.full_match) < 500
        and strings.ilike(strings.replace_confusables(.full_match),
                          "*safe content*",
                          "*safe sender*",
                          "*trusted sender*"
        )
        and not regex.icontains(.full_match,
                                "add.{0,50} to.{0,50}(address book|safe senders? list)"
        )
)

Detection logic

Scope: inbound message.

Detects messages containing credential theft language combined with social engineering topics like secure messages, notifications, or authentication alerts. The rule specifically identifies emails that deceptively claim to be from a 'safe sender' or contain 'safe content' in the first line, which is a common tactic used to bypass security filters and gain user trust.

  1. inbound message
  2. any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
    • .name is 'cred_theft'
    • .confidence is not 'low'
  3. any of:
    • any of ml.nlu_classifier(body.current_thread.text).topics where:
      • .name in ('Reminders and Notifications', 'Secure Message', 'Security and Authentication', 'Voicemail Call and Missed Call Notifications', 'E-Signature', 'Financial Communications')
    • all of:
      • length(distinct(filter(ml.nlu_classifier(body.current_thread.text).entities, .name not in ('org', 'recipient', 'sender')), .name)) > 0
      • all of distinct(...) where:
        • .name in ('request', 'financial', 'urgency')
  4. not:
    • any of ml.nlu_classifier(body.current_thread.text).topics where:
      • .name in ('Advertising and Promotions', 'Newsletters and Digests', 'News and Current Events', 'Travel and Transportation')
  5. any of regex.iextract(body.current_thread.text) where all hold:
    • length(.full_match) < 500
    • strings.replace_confusables(.full_match) matches any of 3 patterns
      • *safe content*
      • *safe sender*
      • *trusted sender*
    • not:
      • .full_match matches 'add.{0,50} to.{0,50}(address book|safe senders? list)'

Inspects: body.current_thread.text, type.inbound. Sensors: ml.nlu_classifier, regex.icontains, regex.iextract, strings.ilike, strings.replace_confusables.

Indicators matched (14)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscred_theft
ml.nlu_classifier(body.current_thread.text).topics[].namememberReminders and Notifications
ml.nlu_classifier(body.current_thread.text).topics[].namememberSecure Message
ml.nlu_classifier(body.current_thread.text).topics[].namememberSecurity and Authentication
ml.nlu_classifier(body.current_thread.text).topics[].namememberVoicemail Call and Missed Call Notifications
ml.nlu_classifier(body.current_thread.text).topics[].namememberE-Signature
ml.nlu_classifier(body.current_thread.text).topics[].namememberFinancial Communications
distinct(...)[].namememberrequest
distinct(...)[].namememberfinancial
distinct(...)[].namememberurgency
regex.iextractregex^[^\r\n]*
strings.ilikesubstring*safe content*
2 more
strings.ilikesubstring*safe sender*
strings.ilikesubstring*trusted sender*

Stages and Predicates

Stage 1: mql_rule

and
  any(regex.iextract(body.current_thread.text))
    and
      not
        regex.iextract(body.current_thread.text).full_match regex_match "add.{0,50} to.{0,50}(address book|safe senders? list)"
      or
        strings.replace_confusables(regex.iextract(body.current_thread.text)[].full_match) match "safe content"
        strings.replace_confusables(regex.iextract(body.current_thread.text)[].full_match) match "safe sender"
        strings.replace_confusables(regex.iextract(body.current_thread.text)[].full_match) match "trusted sender"
      regex.iextract(body.current_thread.text).full_match length_compare "500"
  or
    and
      distinct(filter(ml.nlu_classifier(body.current_thread.text).entities, .name not in ('org', 'recipient', 'sender')), .name) length_compare "0"
       macro "all(distinct(...))"
    any(ml.nlu_classifier(body.current_thread.text).topics)
      ml.nlu_classifier(body.current_thread.text).topics.name in ["E-Signature", "Financial Communications", "Reminders and Notifications", "Secure Message", "Security and Authentication", "Voicemail Call and Missed Call Notifications"]
  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 "cred_theft"
  not
    any(ml.nlu_classifier(body.current_thread.text).topics)
      ml.nlu_classifier(body.current_thread.text).topics.name in ["Advertising and Promotions", "News and Current Events", "Newsletters and Digests", "Travel and Transportation"]
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
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
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

CVE-2023-5631 - Roundcube Webmail XSS via crafted SVG

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

Body HTML contains an exploit for CVE-2023-5631, a vulnerability in Roundcube Webmail that allows stored XSS via an HTML e-mail message with a crafted SVG document.

Threat classification

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

CategoryValues
Attack typesMalware/Ransomware
Tactics and techniquesEvasion, Exploit, HTML smuggling, Scripting

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.html
  • type

Rule body

type.inbound
and length(attachments) == 0
and strings.ilike(body.html.raw,
                  '*use href="data:image/svg+xml;base64,PHN2Zy*#*'
)

Detection logic

Scope: inbound message.

Body HTML contains an exploit for CVE-2023-5631, a vulnerability in Roundcube Webmail that allows stored XSS via an HTML e-mail message with a crafted SVG document.

  1. inbound message
  2. length(attachments) is 0
  3. body.html.raw matches '*use href="data:image/svg+xml;base64,PHN2Zy*#*'

Inspects: body.html.raw, type.inbound. Sensors: strings.ilike.

Indicators matched (1)

FieldMatchValue
strings.ilikesubstring*use href="data:image/svg+xml;base64,PHN2Zy*#*

Stages and Predicates

Stage 1: mql_rule

and
  attachments length_compare "0"
  body.html.raw wildcard "*use href=\"data:image/svg+xml;base64,PHN2Zy*#*"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.html.rawwildcard
  • *use href="data:image/svg+xml;base64,PHN2Zy*#*
field:"body.html.raw" kind:wildcard
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Extortion / sextortion (untrusted sender)

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

Detects extortion and sextortion attempts by analyzing the email body text from an untrusted sender.

Threat classification

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

CategoryValues
Attack typesExtortion
Tactics and techniquesSocial engineering, Spoofing

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • body.html
  • body.links
  • headers
  • headers.auth_summary
  • headers.hops
  • sender.email
  • subject
  • type

Rule body

type.inbound
and length(filter(body.links, .display_text is not null)) < 10
and not (
  ml.nlu_classifier(body.current_thread.text).language == "english"
  and any(ml.nlu_classifier(body.html.display_text).topics,
          .name in (
            "News and Current Events",
            "Newsletters and Digests",
            "Advertising and Promotions"
          )
          and .confidence in ("high", "medium")
  )
)
and (
  (
    any(ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents,
        (.name == "extortion" and .confidence == "high")
    )
    and (
      any(ml.nlu_classifier(strings.replace_confusables(body.current_thread.text
                            )
          ).entities,
          .name == "financial"
          or (
            .name is not null
            and regex.icontains(.text,
                                "cybḛ[rŗřṙȑȓɍʀɼɽг]c[rŗřṙȑȓɍʀɼɽг]imina[lĺļľḷḹḽłƖʟḻ]s"
            )
          )
      )
      or any(ml.nlu_classifier(strings.replace_confusables(body.current_thread.text
                               )
             ).topics,
             .name == "Financial Communications" and .confidence != "low"
      )
    )
  )
  // catches extortion content delivered as inline base64 images
  or (
    (body.current_thread.text is null or length(body.current_thread.text) < 100)
    and any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents,
            .name == "extortion" and .confidence == "high"
    )
    and (
      any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).entities,
          .name == "financial"
      )
      or any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics,
             .name == "Financial Communications" and .confidence != "low"
      )
    )
  )
  // manual indicators failsafe
  or 3 of (
    // malware terms
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    "(?:(?:spy|[mṁ][aȁḁ]l)[wŵ][aȁḁ][rŗ]e|[tŢ][rŗȓ][oốởộ]j[aǻä][nņ]|[rȓ]emote (?:entry|cont[rř]o[lĺ])|infiltrat(?:ed|ion)|backdoor|vi[rṙ]us|intruder|(?:your|the).{0,15}(?:device|system|computer|phone).{0,10}(?:became|was|got|is).{0,5}comprom[ḯiïíįī]sed|prov[ḯiïíįī]d[ḯiïíįī]ng.{0,20}full [aảǡą]ccess)"
    ),
    // actions recorded
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    "(?:p[oộ][rŗ]n|a[dȡ]ult (?:web)?site|webcam|mastu[rŗ]bating|je[rŗ]king off|pleasu[rŗȑ]i[nŋ]g you[rŗṛ]self|getting off|expli[cƈ]it|cl[ḯiïíįī]ps.{0,20}screenshots|NSFW|gr[aǻẳ]phic c[oỡở]ntent)"
    ),
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    "(?:pe[rŗ]ve[rŗ]t|pe[rŗ]ve[rŗ]sion|mastu[rŗ]bat)"
    ),
    // a timeframe to pay
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    '[ilo0-9]{2} (?:hou[rŗṝ][sṣ]|uu[rŗ])',
                    '(?:one|tw[oờȍ]|2|th[rŗ]ee|\d) [dḍ][aảǡą]y[sṣ]?',
                    'set a timer'
    ),
    // a promise from the actor
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    '(?:pe[rŗ]manently|will|I''ll) delete|([rŗ]emove|destroy) (?:[\p{L}\p{M}\p{N}]+\s*){0,4} (?:data|ev[ḯiïįīí]dence|v[ḯiïíįī]deos?)'
    ),
    // a threat from the actor
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    '(?:\bsen[dt]|forward|expose|share)\s*(?:[\p{L}\p{N}]+\s*){0,5}\s*to\s*(?:[\p{L}\p{N}]+\s*){0,5}(?:contacts|media|family|f[rŗ]iends|coworkers|co-workers|associates|kin\b)'
    ),
    // bitcoin language (excluding newsletters)
    (
      regex.icontains(strings.replace_confusables(body.current_thread.text),
                      '[bḆḂ]i[tṯ][cĉƈ][oöة]i[nņɲň]|\bbtc\b|blockchain'
      )
      // negate cryptocurrency newsletters
      and not (
        any(body.links,
            strings.icontains(.display_text, "unsubscribe")
            and (
              strings.icontains(.href_url.path, "unsubscribe")
              // handle mimecast URL rewrites
              or (
                .href_url.domain.root_domain == 'mimecastprotect.com'
                and strings.icontains(.href_url.query_params,
                                      sender.email.domain.root_domain
                )
              )
            )
        )
      )
    ),
    (
      regex.icontains(strings.replace_confusables(body.current_thread.text),
                      '(?:contact the police|(?:bitcoin|\bbtc\b).{0,20}(?:wallet|address))'
      )
      and regex.icontains(strings.replace_confusables(body.current_thread.text),
                          '(?:\b[13][a-km-zA-HJ-NP-Z0-9]{24,34}\b)|\bX[1-9A-HJ-NP-Za-km-z]{33}\b|\b(?:0x[a-fA-F0-9]{40})\b|\b[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}\b|\b[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}\b'
      )
    ),
    regex.icontains(strings.replace_confusables(body.current_thread.text),
                    'bc1q.{0,50}\b'
    ),
    (
      regex.count(body.current_thread.text,
                  '[\x{0300}-\x{036F}\x{1AB0}-\x{1AFF}\x{1DC0}-\x{1DFF}\x{0100}-\x{024F}\x{1E00}-\x{1EFF}]'
      ) > 20
      and length(body.current_thread.links) == 0
    )
  )
)
and (
  not profile.by_sender().solicited
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
  or any(headers.hops, any(.fields, .name == "X-Google-Group-Id"))

  // many extortion emails spoof sender domains and fail sender authentication
  or (
    not headers.auth_summary.dmarc.pass
    or headers.auth_summary.dmarc.pass is null
    or not headers.auth_summary.spf.pass
  )
)
// negate legit bounce backs
and not any(ml.nlu_classifier(body.current_thread.text).topics,
            .name in ("Bounce Back and Delivery Failure Notifications")
)
// negate legit forwards and replies
and not (
  (subject.is_reply or subject.is_forward)
  and length(body.previous_threads) > 0
  and (length(headers.references) > 0 or headers.in_reply_to is not null)
)
// negate benign newsletters that mention cyber extortion
and not (
  any(body.links,
      strings.icontains(.display_text, "unsubscribe")
      and strings.icontains(.href_url.path, "unsubscribe")
  )
  // newsletters are typically longer than the average extortion script
  and length(body.current_thread.text) > 2000
)
and length(body.current_thread.text) < 8000
// negate highly trusted sender domains unless they fail DMARC authentication
and (
  (
    sender.email.domain.root_domain in $high_trust_sender_root_domains
    and not headers.auth_summary.dmarc.pass
  )
  or sender.email.domain.root_domain not in $high_trust_sender_root_domains
)

Detection logic

Scope: inbound message.

Detects extortion and sextortion attempts by analyzing the email body text from an untrusted sender.

  1. inbound message
  2. length(filter(body.links, .display_text is not null)) < 10
  3. not:
    • all of:
      • ml.nlu_classifier(body.current_thread.text).language is 'english'
      • any of ml.nlu_classifier(body.html.display_text).topics where all hold:
        • .name in ('News and Current Events', 'Newsletters and Digests', 'Advertising and Promotions')
        • .confidence in ('high', 'medium')
  4. any of:
    • all of:
      • any of ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents where all hold:
        • .name is 'extortion'
        • .confidence is 'high'
      • any of:
        • any of ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).entities where any holds:
          • .name is 'financial'
          • all of:
            • .name is set
            • .text matches 'cybḛ[rŗřṙȑȓɍʀɼɽг]c[rŗřṙȑȓɍʀɼɽг]imina[lĺļľḷḹḽłƖʟḻ]s'
        • any of ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).topics where all hold:
          • .name is 'Financial Communications'
          • .confidence is not 'low'
    • all of:
      • any of:
        • body.current_thread.text is missing
        • length(body.current_thread.text) < 100
      • any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents where all hold:
        • .name is 'extortion'
        • .confidence is 'high'
      • any of:
        • any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).entities where:
          • .name is 'financial'
        • any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics where all hold:
          • .name is 'Financial Communications'
          • .confidence is not 'low'
    • at least 3 of:
      • strings.replace_confusables(body.current_thread.text) matches '(?:(?:spy|[mṁ][aȁḁ]l)[wŵ][aȁḁ][rŗ]e|[tŢ][rŗȓ][oốởộ]j[aǻä][nņ]|[rȓ]emote (?:entry|cont[rř]o[lĺ])|infiltrat(?:ed|ion)|backdoor|vi[rṙ]us|intruder|(?:your|the).{0,15}(?:device|system|computer|phone).{0,10}(?:became|was|got|is).{0,5}comprom[ḯiïíįī]sed|prov[ḯiïíįī]d[ḯiïíįī]ng.{0,20}full [aảǡą]ccess)'
      • strings.replace_confusables(body.current_thread.text) matches '(?:p[oộ][rŗ]n|a[dȡ]ult (?:web)?site|webcam|mastu[rŗ]bating|je[rŗ]king off|pleasu[rŗȑ]i[nŋ]g you[rŗṛ]self|getting off|expli[cƈ]it|cl[ḯiïíįī]ps.{0,20}screenshots|NSFW|gr[aǻẳ]phic c[oỡở]ntent)'
      • strings.replace_confusables(body.current_thread.text) matches '(?:pe[rŗ]ve[rŗ]t|pe[rŗ]ve[rŗ]sion|mastu[rŗ]bat)'
      • strings.replace_confusables(body.current_thread.text) matches any of 3 patterns
        • [ilo0-9]{2} (?:hou[rŗṝ][sṣ]|uu[rŗ])
        • (?:one|tw[oờȍ]|2|th[rŗ]ee|\d) [dḍ][aảǡą]y[sṣ]?
        • set a timer
      • strings.replace_confusables(body.current_thread.text) matches "(?:pe[rŗ]manently|will|I'll) delete|([rŗ]emove|destroy) (?:[\\p{L}\\p{M}\\p{N}]+\\s*){0,4} (?:data|ev[ḯiïįīí]dence|v[ḯiïíįī]deos?)"
      • strings.replace_confusables(body.current_thread.text) matches '(?:\\bsen[dt]|forward|expose|share)\\s*(?:[\\p{L}\\p{N}]+\\s*){0,5}\\s*to\\s*(?:[\\p{L}\\p{N}]+\\s*){0,5}(?:contacts|media|family|f[rŗ]iends|coworkers|co-workers|associates|kin\\b)'
      • all of:
        • strings.replace_confusables(body.current_thread.text) matches '[bḆḂ]i[tṯ][cĉƈ][oöة]i[nņɲň]|\\bbtc\\b|blockchain'
        • not:
          • any of body.links where all hold:
            • .display_text contains 'unsubscribe'
            • any of:
              • .href_url.path contains 'unsubscribe'
              • all of:
                • .href_url.domain.root_domain is 'mimecastprotect.com'
                • strings.icontains(.href_url.query_params)
      • all of:
        • strings.replace_confusables(body.current_thread.text) matches '(?:contact the police|(?:bitcoin|\\bbtc\\b).{0,20}(?:wallet|address))'
        • strings.replace_confusables(body.current_thread.text) matches '(?:\\b[13][a-km-zA-HJ-NP-Z0-9]{24,34}\\b)|\\bX[1-9A-HJ-NP-Za-km-z]{33}\\b|\\b(?:0x[a-fA-F0-9]{40})\\b|\\b[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}\\b|\\b[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}\\b'
      • strings.replace_confusables(body.current_thread.text) matches 'bc1q.{0,50}\\b'
      • all of:
        • regex.count(body.current_thread.text, '[\\x{0300}-\\x{036F}\\x{1AB0}-\\x{1AFF}\\x{1DC0}-\\x{1DFF}\\x{0100}-\\x{024F}\\x{1E00}-\\x{1EFF}]') > 20
        • length(body.current_thread.links) is 0
  5. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
    • any of headers.hops where:
      • any of .fields where:
        • .name is 'X-Google-Group-Id'
    • any of:
      • not:
        • headers.auth_summary.dmarc.pass
      • headers.auth_summary.dmarc.pass is missing
      • not:
        • headers.auth_summary.spf.pass
  6. not:
    • any of ml.nlu_classifier(body.current_thread.text).topics where:
      • .name in ('Bounce Back and Delivery Failure Notifications')
  7. not:
    • all of:
      • any of:
        • subject.is_reply
        • subject.is_forward
      • length(body.previous_threads) > 0
      • any of:
        • length(headers.references) > 0
        • headers.in_reply_to is set
  8. not:
    • all of:
      • any of body.links where all hold:
        • .display_text contains 'unsubscribe'
        • .href_url.path contains 'unsubscribe'
      • length(body.current_thread.text) > 2000
  9. length(body.current_thread.text) < 8000
  10. 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.links, body.current_thread.text, body.html.display_text, body.links, body.links[].display_text, body.links[].href_url.domain.root_domain, body.links[].href_url.path, body.links[].href_url.query_params, body.previous_threads, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, headers.hops, headers.hops[].fields, headers.hops[].fields[].name, headers.in_reply_to, headers.references, sender.email.domain.root_domain, subject.is_forward, subject.is_reply, type.inbound. Sensors: beta.ocr, file.message_screenshot, ml.nlu_classifier, profile.by_sender, regex.count, regex.icontains, strings.icontains, strings.replace_confusables. Reference lists: $high_trust_sender_root_domains.

Indicators matched (23)

FieldMatchValue
ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents[].nameequalsextortion
ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents[].confidenceequalshigh
ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).entities[].nameequalsfinancial
regex.icontainsregexcybḛ[rŗřṙȑȓɍʀɼɽг]c[rŗřṙȑȓɍʀɼɽг]imina[lĺļľḷḹḽłƖʟḻ]s
ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).topics[].nameequalsFinancial Communications
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents[].nameequalsextortion
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents[].confidenceequalshigh
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).entities[].nameequalsfinancial
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics[].nameequalsFinancial Communications
regex.icontainsregex(?:(?:spy|[mṁ][aȁḁ]l)[wŵ][aȁḁ][rŗ]e|[tŢ][rŗȓ][oốởộ]j[aǻä][nņ]|[rȓ]emote (?:entry|cont[rř]o[lĺ])|infiltrat(?:ed|ion)|backdoor|vi[rṙ]us|intruder|(?:your|the).{0,15}(?:device|system|computer|phone).{0,10}(?:became|was|got|is).{0,5}comprom[ḯiïíįī]sed|prov[ḯiïíįī]d[ḯiïíįī]ng.{0,20}full [aảǡą]ccess)
regex.icontainsregex(?:p[oộ][rŗ]n|a[dȡ]ult (?:web)?site|webcam|mastu[rŗ]bating|je[rŗ]king off|pleasu[rŗȑ]i[nŋ]g you[rŗṛ]self|getting off|expli[cƈ]it|cl[ḯiïíįī]ps.{0,20}screenshots|NSFW|gr[aǻẳ]phic c[oỡở]ntent)
regex.icontainsregex(?:pe[rŗ]ve[rŗ]t|pe[rŗ]ve[rŗ]sion|mastu[rŗ]bat)
11 more
regex.icontainsregex[ilo0-9]{2} (?:hou[rŗṝ][sṣ]|uu[rŗ])
regex.icontainsregex(?:one|tw[oờȍ]|2|th[rŗ]ee|\d) [dḍ][aảǡą]y[sṣ]?
regex.icontainsregexset a timer
regex.icontainsregex(?:pe[rŗ]manently|will|I'll) delete|([rŗ]emove|destroy) (?:[\p{L}\p{M}\p{N}]+\s*){0,4} (?:data|ev[ḯiïįīí]dence|v[ḯiïíįī]deos?)
regex.icontainsregex(?:\bsen[dt]|forward|expose|share)\s*(?:[\p{L}\p{N}]+\s*){0,5}\s*to\s*(?:[\p{L}\p{N}]+\s*){0,5}(?:contacts|media|family|f[rŗ]iends|coworkers|co-workers|associates|kin\b)
regex.icontainsregex[bḆḂ]i[tṯ][cĉƈ][oöة]i[nņɲň]|\bbtc\b|blockchain
regex.icontainsregex(?:contact the police|(?:bitcoin|\bbtc\b).{0,20}(?:wallet|address))
regex.icontainsregex(?:\b[13][a-km-zA-HJ-NP-Z0-9]{24,34}\b)|\bX[1-9A-HJ-NP-Za-km-z]{33}\b|\b(?:0x[a-fA-F0-9]{40})\b|\b[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}\b|\b[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}\b
regex.icontainsregexbc1q.{0,50}\b
regex.countregex[\x{0300}-\x{036F}\x{1AB0}-\x{1AFF}\x{1DC0}-\x{1DFF}\x{0100}-\x{024F}\x{1E00}-\x{1EFF}]
headers.hops[].fields[].nameequalsX-Google-Group-Id

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      not
        any(body.links)
          and
            or
              and
                body.links.href_url.domain.root_domain eq "mimecastprotect.com"
                strings.icontains func_call "strings.icontains(body.links[].href_url.query_params)"
              body.links.href_url.path contains "unsubscribe"
            body.links.display_text contains "unsubscribe"
      strings.replace_confusables(body.current_thread.text) regex_match "[bḆḂ]i[tṯ][cĉƈ][oöة]i[nņɲň]|\\bbtc\\b|blockchain"
    and
      or
        any(ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).entities)
          or
            and
              ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).entities.name is_not_null
              ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).entities.text regex_match "cybḛ[rŗřṙȑȓɍʀɼɽг]c[rŗřṙȑȓɍʀɼɽг]imina[lĺļľḷḹḽłƖʟḻ]s"
            ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).entities.name eq "financial"
        any(ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).topics)
          and
            ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).topics.confidence ne "low"
            ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).topics.name eq "Financial Communications"
      any(ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents)
        and
          ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents.confidence eq "high"
          ml.nlu_classifier(strings.replace_confusables(body.current_thread.text)).intents.name eq "extortion"
    and
      or
        any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics)
          and
            ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics.confidence ne "low"
            ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics.name eq "Financial Communications"
        any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).entities)
          ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).entities.name eq "financial"
      any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents)
        and
          ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents.confidence eq "high"
          ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents.name eq "extortion"
      or
        body.current_thread.text is_null
        body.current_thread.text length_compare "100"
    and
      body.current_thread.links length_compare "0"
      regex.count func_call "regex.count(body.current_thread.text, \"[\\x{0300}-\\x{036F}\\x{1AB0}-\\x{1AFF}\\x{1DC0}-\\x{1DFF}\\x{0100}-\\x{024F}\\x{1E00}-\\x{1EFF}]\") > 20"
    and
      strings.replace_confusables(body.current_thread.text) regex_match "(?:\\b[13][a-km-zA-HJ-NP-Z0-9]{24,34}\\b)|\\bX[1-9A-HJ-NP-Za-km-z]{33}\\b|\\b(?:0x[a-fA-F0-9]{40})\\b|\\b[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}\\b|\\b[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}\\b"
      strings.replace_confusables(body.current_thread.text) regex_match "(?:contact the police|(?:bitcoin|\\bbtc\\b).{0,20}(?:wallet|address))"
    strings.replace_confusables(body.current_thread.text) regex_match "(?:(?:spy|[mṁ][aȁḁ]l)[wŵ][aȁḁ][rŗ]e|[tŢ][rŗȓ][oốởộ]j[aǻä][nņ]|[rȓ]emote (?:entry|cont[rř]o[lĺ])|infiltrat(?:ed|ion)|backdoor|vi[rṙ]us|intruder|(?:your|the).{0,15}(?:device|system|computer|phone).{0,10}(?:became|was|got|is).{0,5}comprom[ḯiïíįī]sed|prov[ḯiïíįī]d[ḯiïíįī]ng.{0,20}full [aảǡą]ccess)"
    strings.replace_confusables(body.current_thread.text) regex_match "(?:\\bsen[dt]|forward|expose|share)\\s*(?:[\\p{L}\\p{N}]+\\s*){0,5}\\s*to\\s*(?:[\\p{L}\\p{N}]+\\s*){0,5}(?:contacts|media|family|f[rŗ]iends|coworkers|co-workers|associates|kin\\b)"
    strings.replace_confusables(body.current_thread.text) regex_match "(?:one|tw[oờȍ]|2|th[rŗ]ee|\\d) [dḍ][aảǡą]y[sṣ]?"
    strings.replace_confusables(body.current_thread.text) regex_match "(?:p[oộ][rŗ]n|a[dȡ]ult (?:web)?site|webcam|mastu[rŗ]bating|je[rŗ]king off|pleasu[rŗȑ]i[nŋ]g you[rŗṛ]self|getting off|expli[cƈ]it|cl[ḯiïíįī]ps.{0,20}screenshots|NSFW|gr[aǻẳ]phic c[oỡở]ntent)"
    strings.replace_confusables(body.current_thread.text) regex_match "(?:pe[rŗ]manently|will|I'll) delete|([rŗ]emove|destroy) (?:[\\p{L}\\p{M}\\p{N}]+\\s*){0,4} (?:data|ev[ḯiïįīí]dence|v[ḯiïíįī]deos?)"
    strings.replace_confusables(body.current_thread.text) regex_match "(?:pe[rŗ]ve[rŗ]t|pe[rŗ]ve[rŗ]sion|mastu[rŗ]bat)"
    strings.replace_confusables(body.current_thread.text) regex_match "[ilo0-9]{2} (?:hou[rŗṝ][sṣ]|uu[rŗ])"
    strings.replace_confusables(body.current_thread.text) regex_match "bc1q.{0,50}\\b"
    strings.replace_confusables(body.current_thread.text) regex_match "set a timer"
  not
    and
      any(body.links)
        and
          body.links.display_text contains "unsubscribe"
          body.links.href_url.path contains "unsubscribe"
      body.current_thread.text length_compare "2000"
  not
    and
      any(ml.nlu_classifier(body.html.display_text).topics)
        and
          ml.nlu_classifier(body.html.display_text).topics.confidence in ["high", "medium"]
          ml.nlu_classifier(body.html.display_text).topics.name in ["Advertising and Promotions", "News and Current Events", "Newsletters and Digests"]
      ml.nlu_classifier func_call "ml.nlu_classifier(body.current_thread.text).language == english"
  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(headers.hops)
      any(headers.hops.fields)
        headers.hops.fields.name eq "X-Google-Group-Id"
    and
      not
        profile.by_sender func_call "profile.by_sender().any_messages_benign"
      profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
    not
      headers.auth_summary.dmarc.pass eq "true"
    not
      headers.auth_summary.spf.pass eq "true"
    not
      profile.by_sender func_call "profile.by_sender().solicited"
    headers.auth_summary.dmarc.pass is_null
  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 "0"
  not
    any(ml.nlu_classifier(body.current_thread.text).topics)
      ml.nlu_classifier(body.current_thread.text).topics.name eq "Bounce Back and Delivery Failure Notifications"
  body.current_thread.text length_compare "8000"
  filter(body.links, .display_text is not null) length_compare "10"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
body.linksarray_any(no value, null check)excludes:body.links
body.current_thread.textlength_compare2000excludes:body.current_thread.text field:"body.current_thread.text" value:"2000"
ml.nlu_classifier(body.html.display_text).topicsarray_any(no value, null check)excludes:ml.nlu_classifier(body.html.display_text).topics
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_compare0excludes:body.previous_threads field:"body.previous_threads" value:"0"
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.textis_null
  • (no value, null check)
field:"body.current_thread.text" kind:is_null
headers.auth_summary.dmarc.passis_null
  • (no value, null check)
field:"headers.auth_summary.dmarc.pass" kind:is_null
strings.replace_confusables(body.current_thread.text)regex_match
  • (?:(?:spy|[mṁ][aȁḁ]l)[wŵ][aȁḁ][rŗ]e|[tŢ][rŗȓ][oốởộ]j[aǻä][nņ]|[rȓ]emote (?:entry|cont[rř]o[lĺ])|infiltrat(?:ed|ion)|backdoor|vi[rṙ]us|intruder|(?:your|the).{0,15}(?:device|system|computer|phone).{0,10}(?:became|was|got|is).{0,5}comprom[ḯiïíįī]sed|prov[ḯiïíįī]d[ḯiïíįī]ng.{0,20}full [aảǡą]ccess)
    • (?:\b[13][a-km-zA-HJ-NP-Z0-9]{24,34}\b)
    • \bX[1-9A-HJ-NP-Za-km-z]{33}\b
    • \b(?:0x[a-fA-F0-9]{40})\b
    • \b[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}\b
    • \b[48][0-9AB][1-9A-HJ-NP-Za-km-z]{93}\b
  • (?:\bsen[dt]|forward|expose|share)\s*(?:[\p{L}\p{N}]+\s*){0,5}\s*to\s*(?:[\p{L}\p{N}]+\s*){0,5}(?:contacts|media|family|f[rŗ]iends|coworkers|co-workers|associates|kin\b)
  • (?:contact the police|(?:bitcoin|\bbtc\b).{0,20}(?:wallet|address))
  • (?:one|tw[oờȍ]|2|th[rŗ]ee|\d) [dḍ][aảǡą]y[sṣ]?
  • (?:p[oộ][rŗ]n|a[dȡ]ult (?:web)?site|webcam|mastu[rŗ]bating|je[rŗ]king off|pleasu[rŗȑ]i[nŋ]g you[rŗṛ]self|getting off|expli[cƈ]it|cl[ḯiïíįī]ps.{0,20}screenshots|NSFW|gr[aǻẳ]phic c[oỡở]ntent)
    • (?:pe[rŗ]manently|will|I'll) delete
    • ([rŗ]emove|destroy) (?:[\p{L}\p{M}\p{N}]+\s*){0,4} (?:data|ev[ḯiïįīí]dence|v[ḯiïíįī]deos?)
  • (?:pe[rŗ]ve[rŗ]t|pe[rŗ]ve[rŗ]sion|mastu[rŗ]bat)
    • [bḆḂ]i[tṯ][cĉƈ][oöة]i[nņɲň]
    • \bbtc\b
    • blockchain
  • [ilo0-9]{2} (?:hou[rŗṝ][sṣ]|uu[rŗ])
  • bc1q.{0,50}\b
  • set a timer
field:"strings.replace_confusables(body.current_thread.text)" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Honorific greeting BEC attempt with sender and reply-to mismatch

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

Detects generic BEC/Fraud scams by analyzing text within the email body from mismatched senders with other suspicious indicators.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesFree email provider, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • headers.reply_to
  • sender.email
  • type

Rule body

type.inbound
// mismatched sender (From) and Reply-to + freemail
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 in $free_email_providers
  )
)

// use of honorific
and regex.icontains(body.current_thread.text,
                    '(?:Mr|Mrs|Ms|Miss|Dr|Prof|Sir|Lady|Rev)\.?[ \t]+'
)

// BEC-themed language
and (
  any(ml.nlu_classifier(body.current_thread.text).intents,
      .name in ("bec", "advance_fee")
  )
  and any(ml.nlu_classifier(body.current_thread.text).entities,
          .name == "request"
  )
)

// 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 (
  (
    profile.by_sender().prevalence in ("new", "outlier")
    and not profile.by_sender().solicited
  )
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)
and not profile.by_sender().any_messages_benign

Detection logic

Scope: inbound message.

Detects generic BEC/Fraud scams by analyzing text within the email body from mismatched senders with other suspicious indicators.

  1. inbound message
  2. 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 in $free_email_providers
  3. body.current_thread.text matches '(?:Mr|Mrs|Ms|Miss|Dr|Prof|Sir|Lady|Rev)\\.?[ \\t]+'
  4. all of:
    • any of ml.nlu_classifier(body.current_thread.text).intents where:
      • .name in ('bec', 'advance_fee')
    • any of ml.nlu_classifier(body.current_thread.text).entities where:
      • .name is 'request'
  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:
      • profile.by_sender().prevalence in ('new', 'outlier')
      • not:
        • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
  7. not:
    • profile.by_sender().any_messages_benign

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

Indicators matched (4)

FieldMatchValue
regex.icontainsregex(?:Mr|Mrs|Ms|Miss|Dr|Prof|Sir|Lady|Rev)\.?[ \t]+
ml.nlu_classifier(body.current_thread.text).intents[].namememberbec
ml.nlu_classifier(body.current_thread.text).intents[].namememberadvance_fee
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsrequest

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
        profile.by_sender func_call "profile.by_sender().any_messages_benign"
      profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
    and
      not
        profile.by_sender func_call "profile.by_sender().solicited"
      profile.by_sender func_call "profile.by_sender().prevalence in (new, outlier)"
  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).intents)
    ml.nlu_classifier(body.current_thread.text).intents.name in ["advance_fee", "bec"]
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  body.current_thread.text regex_match "(?:Mr|Mrs|Ms|Miss|Dr|Prof|Sir|Lady|Rev)\\.?[ \\t]+"
  headers.reply_to length_compare "0"
  type.inbound eq "true"
   macro "all(headers.reply_to)"

Indicators

These rows show field, operator, and value matches.

HTML smuggling with atob in message body

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

Detects if the email body HTML contains the document write or insertAdjacentHTML method and atob function call. This technique has been observed leading to credential phishing.

Threat classification

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

CategoryValues
Attack typesCredential Phishing, Malware/Ransomware
Tactics and techniquesHTML smuggling

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.html
  • body.plain
  • type

Rule body

type.inbound
and (length(body.plain.raw) < 200 or body.plain.raw is null)
and regex.icontains(body.html.raw,
                    "document.{0,10}(write|insertAdjacentHTML).{0,10}atob"
)

Detection logic

Scope: inbound message.

Detects if the email body HTML contains the document write or insertAdjacentHTML method and atob function call. This technique has been observed leading to credential phishing.

  1. inbound message
  2. any of:
    • length(body.plain.raw) < 200
    • body.plain.raw is missing
  3. body.html.raw matches 'document.{0,10}(write|insertAdjacentHTML).{0,10}atob'

Inspects: body.html.raw, body.plain.raw, type.inbound. Sensors: regex.icontains.

Indicators matched (1)

FieldMatchValue
regex.icontainsregexdocument.{0,10}(write|insertAdjacentHTML).{0,10}atob

Stages and Predicates

Stage 1: mql_rule

and
  or
    body.plain.raw is_null
    body.plain.raw length_compare "200"
  body.html.raw regex_match "document.{0,10}(write|insertAdjacentHTML).{0,10}atob"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

HTML: Template placeholders or recipient email in element class attributes

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

Detects inbound messages where HTML element class attributes contain either unfilled template placeholders (e.g., {email}, {RECIPIENT_EMAIL}, {domain}) or the recipient's actual email address — optionally wrapped in curly braces. This pattern indicates a bulk-sending infrastructure that failed to substitute personalization tokens, or one that embeds recipient identifiers directly into HTML class names for tracking or evasion purposes. Observed samples follow a consistent pattern: subjects contain numeric identifiers flanking the recipient's email address, and senders vary across unrelated domains, suggesting a coordinated operation targeting multiple organizations including technology and fitness brands.

Threat classification

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

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

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • recipients
  • recipients.to
  • type

Rule body

type.inbound
and (
  // observed unpopulated template variables in class names
  any(html.xpath(body.html, "//*/@class").nodes,
      strings.contains(.raw, '{')
      and strings.contains(.raw, '}')
      and regex.icontains(.raw,
                          '\{\s*(?:domain|email|(?:RECIPIENT|SENDER)[_\s]?EMAIL)\s*\}'
      )
  )

  // check where class names _are_ the email address, or contain the email address are wrapped in { } or start/end with { or }
  or any(recipients.to,
         .email.email != ""
         and any(html.xpath(body.html, "//*/@class").nodes,
                 .raw =~ ..email.email
                 or .raw =~ strings.concat("{", ..email.email, "}")
                 or .raw =~ strings.concat("{", ..email.email)
                 or .raw =~ strings.concat(..email.email, "}")
         )
  )
)

Detection logic

Scope: inbound message.

Detects inbound messages where HTML element class attributes contain either unfilled template placeholders (e.g., {email}, {RECIPIENT_EMAIL}, {domain}) or the recipient's actual email address — optionally wrapped in curly braces. This pattern indicates a bulk-sending infrastructure that failed to substitute personalization tokens, or one that embeds recipient identifiers directly into HTML class names for tracking or evasion purposes. Observed samples follow a consistent pattern: subjects contain numeric identifiers flanking the recipient's email address, and senders vary across unrelated domains, suggesting a coordinated operation targeting multiple organizations including technology and fitness brands.

  1. inbound message
  2. any of:
    • any of html.xpath(body.html, '//*/@class').nodes where all hold:
      • .raw contains '{'
      • .raw contains '}'
      • .raw matches '\\{\\s*(?:domain|email|(?:RECIPIENT|SENDER)[_\\s]?EMAIL)\\s*\\}'
    • any of recipients.to where all hold:
      • .email.email is not ''
      • any of html.xpath(body.html, '//*/@class').nodes where any holds:
        • .raw is .email.email
        • .raw is strings.concat('{', .email.email, '}')
        • .raw is strings.concat('{', .email.email)
        • .raw is strings.concat(.email.email, '}')

Inspects: body.html, recipients.to, recipients.to[].email.email, type.inbound. Sensors: html.xpath, regex.icontains, strings.concat, strings.contains.

Indicators matched (3)

FieldMatchValue
strings.containssubstring{
strings.containssubstring}
regex.icontainsregex\{\s*(?:domain|email|(?:RECIPIENT|SENDER)[_\s]?EMAIL)\s*\}

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(recipients.to)
      and
        any(html.xpath(body.html, '//*/@class').nodes)
          or
            html.xpath(body.html, '//*/@class').nodes.raw cross_field_compare "recipients.to.email.email"
             macro "html.xpath(body.html, '//*/@class').nodes[].raw =~ strings.concat('{', recipients.to[].email.email)"
             macro "html.xpath(body.html, '//*/@class').nodes[].raw =~ strings.concat('{', recipients.to[].email.email, '}')"
             macro "html.xpath(body.html, '//*/@class').nodes[].raw =~ strings.concat(recipients.to[].email.email, '}')"
        recipients.to.email.email ne ""
    any(html.xpath(body.html, '//*/@class').nodes)
      and
        html.xpath(body.html, '//*/@class').nodes.raw contains "{"
        html.xpath(body.html, '//*/@class').nodes.raw contains "}"
        html.xpath(body.html, '//*/@class').nodes.raw regex_match "\\{\\s*(?:domain|email|(?:RECIPIENT|SENDER)[_\\s]?EMAIL)\\s*\\}"
  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"

Investor solicitation with organization targeting

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

Detects messages targeting organizations with investment solicitations that specifically reference the recipient's organization by extracting the organization name and matching it to the recipient's email domain.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and (
  // subject contains recipient's org name
  any(recipients.to,
      strings.icontains(subject.subject, .email.domain.sld)
      and regex.imatch(.email.domain.sld, '.{2,}')
  )
  or
  // body extracts org name matching recipient domain
  any(regex.extract(body.current_thread.text,
                    '(?P<org>[a-zA-Z]{2,20})\s(?:recently\s)?came to our attention'
      ),
      any(recipients.to,
          strings.icontains(.email.domain.domain, ..named_groups["org"])
      )
  )
)
and any(headers.reply_to,
        .email.domain.root_domain != sender.email.domain.root_domain
)
// greeting uses recipient's email local_part
and any(recipients.to,
        (
          strings.icontains(body.current_thread.text,
                            strings.concat("Dear ", .email.local_part)
          )
          or any(regex.extract(.email.local_part, '^(?P<first>[^._]+)'),
                 strings.icontains(body.current_thread.text,
                                   strings.concat("Dear ",
                                                  .named_groups["first"]
                                   )
                 )
          )
        )
)
// financial/investment cold outreach language
and (
  2 of (
    strings.icontains(body.current_thread.text, "alternative investments"),
    strings.icontains(body.current_thread.text, "raising capital"),
    strings.icontains(body.current_thread.text, "came to our attention"),
    strings.icontains(body.current_thread.text, "private markets"),
    strings.icontains(body.current_thread.text, "fundraising"),
    strings.icontains(body.current_thread.text, "investment opportunities"),
    strings.icontains(body.current_thread.text, "introductory"),
    strings.icontains(body.current_thread.text, "commitment size"),
    strings.icontains(body.current_thread.text, "ultra-high-net-worth"),
    strings.icontains(body.current_thread.text, "deployed capital"),
    strings.icontains(body.current_thread.text, "value creation"),
    strings.icontains(body.current_thread.text, "capital planning")
  )
  or (
    any(ml.nlu_classifier(body.current_thread.text).topics,
        .name == "Financial Communications"
    )
    and any(ml.nlu_classifier(body.current_thread.text).topics,
            .name == "Out of Band Pivot"
    )
    and any(ml.nlu_classifier(body.current_thread.text).topics,
            .name == "B2B Cold Outreach"
    )
  )
)

Detection logic

Scope: inbound message.

Detects messages targeting organizations with investment solicitations that specifically reference the recipient's organization by extracting the organization name and matching it to the recipient's email domain.

  1. inbound message
  2. any of:
    • any of recipients.to where all hold:
      • strings.icontains(subject.subject)
      • .email.domain.sld matches '.{2,}'
    • any of regex.extract(body.current_thread.text) where:
      • any of recipients.to where:
        • strings.icontains(.email.domain.domain)
  3. any of headers.reply_to where:
    • .email.domain.root_domain is not sender.email.domain.root_domain
  4. any of recipients.to where any holds:
    • strings.icontains(body.current_thread.text)
    • any of regex.extract(.email.local_part) where:
      • strings.icontains(body.current_thread.text)
  5. any of:
    • at least 2 of 12: body.current_thread.text contains any of 12 patterns
      • alternative investments
      • raising capital
      • came to our attention
      • private markets
      • fundraising
      • investment opportunities
      • introductory
      • commitment size
      • ultra-high-net-worth
      • deployed capital
      • value creation
      • capital planning
    • all of:
      • any of ml.nlu_classifier(body.current_thread.text).topics where:
        • .name is 'Financial Communications'
      • any of ml.nlu_classifier(body.current_thread.text).topics where:
        • .name is 'Out of Band Pivot'
      • any of ml.nlu_classifier(body.current_thread.text).topics where:
        • .name is 'B2B Cold Outreach'

Inspects: body.current_thread.text, headers.reply_to, headers.reply_to[].email.domain.root_domain, recipients.to, recipients.to[].email.domain.domain, recipients.to[].email.domain.sld, recipients.to[].email.local_part, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: ml.nlu_classifier, regex.extract, regex.imatch, strings.concat, strings.icontains.

Indicators matched (18)

FieldMatchValue
regex.imatchregex.{2,}
regex.extractregex(?P<org>[a-zA-Z]{2,20})\s(?:recently\s)?came to our attention
regex.extractregex^(?P<first>[^._]+)
strings.icontainssubstringalternative investments
strings.icontainssubstringraising capital
strings.icontainssubstringcame to our attention
strings.icontainssubstringprivate markets
strings.icontainssubstringfundraising
strings.icontainssubstringinvestment opportunities
strings.icontainssubstringintroductory
strings.icontainssubstringcommitment size
strings.icontainssubstringultra-high-net-worth
6 more
strings.icontainssubstringdeployed capital
strings.icontainssubstringvalue creation
strings.icontainssubstringcapital planning
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsFinancial Communications
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsOut of Band Pivot
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsB2B Cold Outreach

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      any(ml.nlu_classifier(body.current_thread.text).topics)
        ml.nlu_classifier(body.current_thread.text).topics.name eq "B2B Cold Outreach"
      any(ml.nlu_classifier(body.current_thread.text).topics)
        ml.nlu_classifier(body.current_thread.text).topics.name eq "Financial Communications"
      any(ml.nlu_classifier(body.current_thread.text).topics)
        ml.nlu_classifier(body.current_thread.text).topics.name eq "Out of Band Pivot"
    body.current_thread.text contains "alternative investments"
    body.current_thread.text contains "came to our attention"
    body.current_thread.text contains "capital planning"
    body.current_thread.text contains "commitment size"
    body.current_thread.text contains "deployed capital"
    body.current_thread.text contains "fundraising"
    body.current_thread.text contains "introductory"
    body.current_thread.text contains "investment opportunities"
    body.current_thread.text contains "private markets"
    body.current_thread.text contains "raising capital"
    body.current_thread.text contains "ultra-high-net-worth"
    body.current_thread.text contains "value creation"
  or
    any(recipients.to)
      and
        recipients.to.email.domain.sld regex_match ".{2,}"
        strings.icontains func_call "strings.icontains(subject.subject)"
    any(regex.extract(body.current_thread.text))
      any(recipients.to)
        strings.icontains func_call "strings.icontains(recipients.to[].email.domain.domain)"
  any(recipients.to)
    or
      any(regex.extract(recipients.to.email.local_part))
        strings.icontains func_call "strings.icontains(body.current_thread.text)"
      strings.icontains func_call "strings.icontains(body.current_thread.text)"
  any(headers.reply_to)
    headers.reply_to.email.domain.root_domain cross_field_compare "sender.email.domain.root_domain"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textcontains
  • alternative investments
  • came to our attention
  • capital planning
  • commitment size
  • deployed capital
  • fundraising
  • introductory
  • investment opportunities
  • private markets
  • raising capital
  • ultra-high-net-worth
  • value creation
field:"body.current_thread.text" kind:contains
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Job scam (unsolicited sender)

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

Detects job scam attempts by analyzing the message body text from an unsolicited sender.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and (
  any(ml.nlu_classifier(body.current_thread.text).intents,
      .name in ("job_scam") and .confidence == "high"
  )
)
and (
  any(ml.nlu_classifier(body.current_thread.text).entities,
      .name == "financial"
  )
  or strings.icontains(body.current_thread.text, "salary package")
  or strings.icontains(body.current_thread.text, "kindly")
  or (
    (
      any(ml.nlu_classifier(body.current_thread.text).entities,
          .name in ("greeting", "salutation")
      )
      or sender.email.domain.root_domain in $free_email_providers
    )
    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)
        )
      )
    )
  )
)
// negating income / job verification senders
and not (
  sender.email.domain.root_domain in ('loandepot.com', 'sofi.com')
  and headers.auth_summary.dmarc.pass
)
and (
  not profile.by_sender().solicited
  or profile.by_sender().any_messages_malicious_or_spam
)
and not profile.by_sender().any_messages_benign

Detection logic

Scope: inbound message.

Detects job scam attempts by analyzing the message body text from an unsolicited sender.

  1. inbound message
  2. any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
    • .name in ('job_scam')
    • .confidence is 'high'
  3. any of:
    • any of ml.nlu_classifier(body.current_thread.text).entities where:
      • .name is 'financial'
    • body.current_thread.text contains 'salary package'
    • body.current_thread.text contains 'kindly'
    • all of:
      • any of:
        • any of ml.nlu_classifier(body.current_thread.text).entities where:
          • .name in ('greeting', 'salutation')
        • sender.email.domain.root_domain in $free_email_providers
      • 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
  4. not:
    • all of:
      • sender.email.domain.root_domain in ('loandepot.com', 'sofi.com')
      • headers.auth_summary.dmarc.pass
  5. any of:
    • not:
      • profile.by_sender().solicited
    • profile.by_sender().any_messages_malicious_or_spam
  6. not:
    • profile.by_sender().any_messages_benign

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, 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.nlu_classifier, profile.by_sender, strings.icontains. Reference lists: $free_email_providers.

Indicators matched (7)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).intents[].namememberjob_scam
ml.nlu_classifier(body.current_thread.text).intents[].confidenceequalshigh
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsfinancial
strings.icontainssubstringsalary package
strings.icontainssubstringkindly
ml.nlu_classifier(body.current_thread.text).entities[].namemembergreeting
ml.nlu_classifier(body.current_thread.text).entities[].namemembersalutation

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        any(ml.nlu_classifier(body.current_thread.text).entities)
          ml.nlu_classifier(body.current_thread.text).entities.name in ["greeting", "salutation"]
         macro "sender.email.domain.root_domain in free_email_providers"
      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 eq "financial"
    body.current_thread.text contains "kindly"
    body.current_thread.text contains "salary package"
  not
    and
      headers.auth_summary.dmarc.pass eq "true"
      sender.email.domain.root_domain in ["loandepot.com", "sofi.com"]
  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 "job_scam"
  or
    not
      profile.by_sender func_call "profile.by_sender().solicited"
    profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

Indicators

These rows show field, operator, and value matches.

Link: Self-sent PDF lure with subject correlation

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

Detects messages sent from a user to themselves containing bold PDF links where the link text correlates with the subject line or sender domain, potentially indicating a compromised account or social engineering technique.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud, Credential Phishing
Tactics and techniquesSocial engineering, Evasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • recipients
  • recipients.to[0]
  • sender.email
  • subject
  • type

Rule body

type.inbound
// self sender
and (
  length(recipients.to) == 1
  and recipients.to[0].email.email == sender.email.email
)
// bold a tags ending in PDF
and any(html.xpath(body.html, '//a[./b]').nodes,
        strings.iends_with(.display_text, ".pdf")
        and (
          // subject appears as the .pdf link
          any(regex.extract(subject.base, '(?P<word>\w+)'),
              strings.contains(..display_text, .named_groups["word"])
          )
          // OR sender domain appears as the pdf link
          or strings.icontains(.display_text, sender.email.domain.sld)
        )
        // we should NOT match urls
        and strings.parse_url(.display_text, strict=false).url is null
        and not any(.links, strings.iends_with(.href_url.path, '.pdf'))
)

Detection logic

Scope: inbound message.

Detects messages sent from a user to themselves containing bold PDF links where the link text correlates with the subject line or sender domain, potentially indicating a compromised account or social engineering technique.

  1. inbound message
  2. all of:
    • length(recipients.to) is 1
    • recipients.to[0].email.email is sender.email.email
  3. any of html.xpath(body.html, '//a[./b]').nodes where all hold:
    • .display_text ends with '.pdf'
    • any of:
      • any of regex.extract(subject.base) where:
        • strings.contains(.display_text)
      • strings.icontains(.display_text)
    • strings.parse_url(.display_text, strict=False).url is missing
    • not:
      • any of .links where:
        • .href_url.path ends with '.pdf'

Inspects: body.html, recipients.to, recipients.to[0].email.email, sender.email.domain.sld, sender.email.email, subject.base, type.inbound. Sensors: html.xpath, regex.extract, strings.contains, strings.icontains, strings.iends_with, strings.parse_url.

Indicators matched (2)

FieldMatchValue
strings.iends_withsuffix.pdf
regex.extractregex(?P<word>\w+)

Stages and Predicates

Stage 1: mql_rule

and
  any(html.xpath(body.html, '//a[./b]').nodes)
    and
      not
        any(html.xpath(body.html, '//a[./b]').nodes.links)
          html.xpath(body.html, '//a[./b]').nodes.links.href_url.path ends_with ".pdf"
      or
        any(regex.extract(subject.base))
          strings.contains func_call "strings.contains(html.xpath(body.html, '//a[./b]').nodes[].display_text)"
        strings.icontains func_call "strings.icontains(html.xpath(body.html, '//a[./b]').nodes[].display_text)"
      html.xpath(body.html, '//a[./b]').nodes.display_text ends_with ".pdf"
      strings.parse_url(html.xpath(body.html, '//a[./b]').nodes[].display_text, strict=False).url is_null
  recipients.to length_compare "1"
  recipients.to[0].email.email cross_field_compare "sender.email.email"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Message content: Request for author engagement

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

Detects inbound messages containing language consistent with unsolicited book/author related scam from a free email provider. Often used to ask for payment related to promotion of an upcoming or recently published book.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • type

Rule body

type.inbound
// freemailer
and sender.email.domain.root_domain in $free_email_providers
// goodreads mention
and strings.icontains(body.current_thread.text, "goodreads")
// addresses the recipient as the author of their own work
and regex.icontains(body.current_thread.text,
                    '\byour (?:books?|work|text|title|novel|manuscript|writing|scholarship|research|reporting|story)\b'
)
//
and regex.icontains(body.current_thread.text,
                    '\b(?:featur|spotlight|showcase|introduce your|invite you|selected your|reading (?:cycle|circle|challenge|community|discussion))'
)
// negate highly trusted sender domains unless they fail DMARC authentication
and not (
  sender.email.domain.root_domain in $high_trust_sender_root_domains
  and coalesce(headers.auth_summary.dmarc.pass, false)
)

Detection logic

Scope: inbound message.

Detects inbound messages containing language consistent with unsolicited book/author related scam from a free email provider. Often used to ask for payment related to promotion of an upcoming or recently published book.

  1. inbound message
  2. sender.email.domain.root_domain in $free_email_providers
  3. body.current_thread.text contains 'goodreads'
  4. body.current_thread.text matches '\\byour (?:books?|work|text|title|novel|manuscript|writing|scholarship|research|reporting|story)\\b'
  5. body.current_thread.text matches '\\b(?:featur|spotlight|showcase|introduce your|invite you|selected your|reading (?:cycle|circle|challenge|community|discussion))'
  6. not:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • coalesce(headers.auth_summary.dmarc.pass)

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, sender.email.domain.root_domain, type.inbound. Sensors: regex.icontains, strings.icontains. Reference lists: $free_email_providers, $high_trust_sender_root_domains.

Indicators matched (3)

FieldMatchValue
strings.icontainssubstringgoodreads
regex.icontainsregex\byour (?:books?|work|text|title|novel|manuscript|writing|scholarship|research|reporting|story)\b
regex.icontainsregex\b(?:featur|spotlight|showcase|introduce your|invite you|selected your|reading (?:cycle|circle|challenge|community|discussion))

Stages and Predicates

Stage 1: mql_rule

and
  not
    and
      coalesce func_call "coalesce(headers.auth_summary.dmarc.pass)"
       macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
  body.current_thread.text contains "goodreads"
  body.current_thread.text regex_match "\\b(?:featur|spotlight|showcase|introduce your|invite you|selected your|reading (?:cycle|circle|challenge|community|discussion))"
  body.current_thread.text regex_match "\\byour (?:books?|work|text|title|novel|manuscript|writing|scholarship|research|reporting|story)\\b"
  type.inbound eq "true"
   macro "sender.email.domain.root_domain in free_email_providers"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textcontains
  • goodreads
field:"body.current_thread.text" kind:contains value:"goodreads"
body.current_thread.textregex_match
  • \b(?:featur|spotlight|showcase|introduce your|invite you|selected your|reading (?:cycle|circle|challenge|community|discussion))
  • \byour (?:books?|work|text|title|novel|manuscript|writing|scholarship|research|reporting|story)\b
field:"body.current_thread.text" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Self-impersonation: Sender matches recipient with bolded name and suspicious link

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

Detects messages where the sender's email address matches the recipient's email address, with the sender's display name appearing in bold text and a suspicious 'Read the Message' link present in the body.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • recipients
  • recipients.to[0]
  • sender.email
  • subject
  • type

Rule body

type.inbound
// sender matches recipients
and (
  length(recipients.to) == 1
  and recipients.to[0].email.email == sender.email.email
)
// sender in current thread BOLD
and any(html.xpath(body.html, '//b').nodes,
        .display_text == sender.display_name
)
// we want the dashed html element to contain a link, and that link to include part of the subject (the subject is the org)
and any(html.xpath(body.html,
                   '//table//td[contains(@style, "border-style: dashed") and contains(@style, "border-width: 1pt")]//a[./b]'
        ).nodes,
        any(regex.extract(subject.base, '(?P<word>\w+)'),
            any(..links,
                strings.icontains(.href_url.url, ..named_groups["word"])
            )
        )
)

Detection logic

Scope: inbound message.

Detects messages where the sender's email address matches the recipient's email address, with the sender's display name appearing in bold text and a suspicious 'Read the Message' link present in the body.

  1. inbound message
  2. all of:
    • length(recipients.to) is 1
    • recipients.to[0].email.email is sender.email.email
  3. any of html.xpath(body.html, '//b').nodes where:
    • .display_text is sender.display_name
  4. any of html.xpath(body.html, '//table//td[contains(@style, "border-style: dashed") and contains(@style, "border-width: 1pt")]//a[./b]').nodes where:
    • any of regex.extract(subject.base) where:
      • any of .links where:
        • strings.icontains(.href_url.url)

Inspects: body.html, recipients.to, recipients.to[0].email.email, sender.display_name, sender.email.email, subject.base, type.inbound. Sensors: html.xpath, regex.extract, strings.icontains.

Indicators matched (1)

FieldMatchValue
regex.extractregex(?P<word>\w+)

Stages and Predicates

Stage 1: mql_rule

and
  any(html.xpath(body.html, '//table//td[contains(@style, "border-style: dashed") and contains(@style, "border-width: 1pt")]//a[./b]').nodes)
    any(regex.extract(subject.base))
      any(html.xpath(body.html, '//table//td[contains(@style, "border-style: dashed") and contains(@style, "border-width: 1pt")]//a[./b]').nodes.links)
        strings.icontains func_call "strings.icontains(html.xpath(body.html, '//table//td[contains(@style, \"border-style: dashed\") and contains(@style, \"border-width: 1pt\")]//a[./b]').nodes[].links[].href_url.url)"
  any(html.xpath(body.html, '//b').nodes)
    html.xpath(body.html, '//b').nodes.display_text cross_field_compare "sender.display_name"
  recipients.to length_compare "1"
  recipients.to[0].email.email cross_field_compare "sender.email.email"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

URL with Unicode U+2044 (⁄) or U+2215 (∕) characters

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

Body of the message, or any links, contain the Unicode U+2044 (⁄) or U+2215 (∕) characters inside a URL.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.links
  • body.plain
  • type

Rule body

type.inbound
and (
  regex.icontains(body.plain.raw,
                  'https?:\/\/[^\s⁄∕]+(?:\/[^\s⁄∕]+)*[⁄∕][^\s⁄∕]+'
  )
  or any(body.links, strings.contains(.href_url.url, '⁄', '∕'))
)

Detection logic

Scope: inbound message.

Body of the message, or any links, contain the Unicode U+2044 (⁄) or U+2215 (∕) characters inside a URL.

  1. inbound message
  2. any of:
    • body.plain.raw matches 'https?:\\/\\/[^\\s⁄∕]+(?:\\/[^\\s⁄∕]+)*[⁄∕][^\\s⁄∕]+'
    • any of body.links where:
      • .href_url.url contains any of 2 patterns

Inspects: body.links, body.links[].href_url.url, body.plain.raw, type.inbound. Sensors: regex.icontains, strings.contains.

Indicators matched (3)

FieldMatchValue
regex.icontainsregexhttps?:\/\/[^\s⁄∕]+(?:\/[^\s⁄∕]+)*[⁄∕][^\s⁄∕]+
strings.containssubstring
strings.containssubstring

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(body.links)
      or
        body.links.href_url.url contains "⁄"
        body.links.href_url.url contains "∕"
    body.plain.raw regex_match "https?:\\/\\/[^\\s⁄∕]+(?:\\/[^\\s⁄∕]+)*[⁄∕][^\\s⁄∕]+"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.plain.rawregex_match
  • https?:\/\/[^\s⁄∕]+(?:\/[^\s⁄∕]+)*[⁄∕][^\s⁄∕]+
field:"body.plain.raw" kind:regex_match value:"https?:\/\/[^\s⁄∕]+(?:\/[^\s⁄∕]+)*[⁄∕][^\s⁄∕]+"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"