Detection rules › Sublime MQL

Sublime MQL rules: spam

RuleSeverity
Display Name Emoji with Financial Symbolslow
Link: Spam website with evasion indicatorslow
Spam: Attendee list solicitationlow
Spam: BlackBaud infrastructure abusemedium
Spam: Campaign with excessive display-text and keywords foundlow
Spam: Campaign with excessive space/char obfuscation and free file hosted linklow
Spam: Cold outreach from Cloudflare-hosted newly registered domainlow
Spam: Commonly observed formatting of unauthorized free giveawayslow
Spam: Cryptocurrency airdrop/giveawaylow
Spam: Default Microsoft Exchange Online sender domain (onmicrosoft.com)low
Spam: Fake dating profile notificationlow
Spam: Fake photo sharelow
Spam: Firebase password reset from suspicious senderlow
Spam: Ghostwriting services scam with manipulative languagemedium
Spam: Item giveaway spam templatelow
Spam: Large financial amount mention from newly registered sender domainmedium
Spam: Link to blob.core.windows.net from new domain (<30d)medium
Spam: Mastercard promotional content with image-based bodylow
Spam: New job cold outreach from unsolicited senderlow
Spam: New link domain (<=10d) and emojismedium
Spam: Personalized subject and greetings via Salesforce Marketing Cloudlow
Spam: Sendersrv.com with financial communications and unsubscribe languagemedium
Spam: Sexually explicit content with emoji in subject from freemail providerlow
Spam: Sexually explicit Google Drive sharelow
Spam: Sexually explicit Google group invitationlow
Spam: Sexually explicit Looker Studio reportlow
Spam: Single recipient duplicated in ccmedium
Spam: SMTP & Proxy Communications in Email Bodymedium
Spam: Unsolicited malformed PDFlow
Spam: Unsolicited WordPress account creation or password reset requestlow
Spam: URL shortener with short body content and emojislow
Spam: Website errors solicitationlow

Display Name Emoji with Financial Symbols

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

Detects messages where the sender's display name contains emoji characters alongside financial symbols ($ £ € ¥ ₿) in the subject line. The sender's domain is not present in the Alexa top 1 million sites and has DMARC authentication issues.

Threat classification

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

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

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// Check for emoji in sender display name using Unicode ranges
and regex.contains(sender.display_name,
                   '[\x{1F600}-\x{1F64F}]|[\x{1F300}-\x{1F5FF}]|[\x{1F680}-\x{1F6FF}]|[\x{1F1E0}-\x{1F1FF}]|[\x{2600}-\x{26FF}]|[\x{2700}-\x{27BF}]'
)
// Check for financial symbols in subject
and regex.contains(subject.subject, '[\$£€¥₿]')
and (
  headers.auth_summary.dmarc.pass is null
  or headers.auth_summary.dmarc.pass == false
)
and sender.email.domain.root_domain not in $alexa_1m

Detection logic

Scope: inbound message.

Detects messages where the sender's display name contains emoji characters alongside financial symbols ($ £ € ¥ ₿) in the subject line. The sender's domain is not present in the Alexa top 1 million sites and has DMARC authentication issues.

  1. inbound message
  2. sender.display_name matches '[\\x{1F600}-\\x{1F64F}]|[\\x{1F300}-\\x{1F5FF}]|[\\x{1F680}-\\x{1F6FF}]|[\\x{1F1E0}-\\x{1F1FF}]|[\\x{2600}-\\x{26FF}]|[\\x{2700}-\\x{27BF}]'
  3. subject.subject matches '[\\$£€¥₿]'
  4. any of:
    • headers.auth_summary.dmarc.pass is missing
    • headers.auth_summary.dmarc.pass is False
  5. sender.email.domain.root_domain not in $alexa_1m

Inspects: headers.auth_summary.dmarc.pass, sender.display_name, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: regex.contains. Reference lists: $alexa_1m.

Indicators matched (2)

FieldMatchValue
regex.containsregex[\x{1F600}-\x{1F64F}]|[\x{1F300}-\x{1F5FF}]|[\x{1F680}-\x{1F6FF}]|[\x{1F1E0}-\x{1F1FF}]|[\x{2600}-\x{26FF}]|[\x{2700}-\x{27BF}]
regex.containsregex[\$£€¥₿]

Stages and Predicates

Stage 1: mql_rule

and
  or
    headers.auth_summary.dmarc.pass eq "false"
    headers.auth_summary.dmarc.pass is_null
  sender.display_name regex_match "[\\x{1F600}-\\x{1F64F}]|[\\x{1F300}-\\x{1F5FF}]|[\\x{1F680}-\\x{1F6FF}]|[\\x{1F1E0}-\\x{1F1FF}]|[\\x{2600}-\\x{26FF}]|[\\x{2700}-\\x{27BF}]"
  subject.subject regex_match "[\\$£€¥₿]"
  type.inbound eq "true"
   macro "sender.email.domain.root_domain not in alexa_1m"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
headers.auth_summary.dmarc.passeq
  • false transforms: boolean
field:"headers.auth_summary.dmarc.pass" kind:eq value:"false"
headers.auth_summary.dmarc.passis_null
  • (no value, null check)
field:"headers.auth_summary.dmarc.pass" kind:is_null
sender.display_nameregex_match
    • [\x{1F600}-\x{1F64F}]
    • [\x{1F300}-\x{1F5FF}]
    • [\x{1F680}-\x{1F6FF}]
    • [\x{1F1E0}-\x{1F1FF}]
    • [\x{2600}-\x{26FF}]
    • [\x{2700}-\x{27BF}]
field:"sender.display_name" kind:regex_match
subject.subjectregex_match
  • [$£€¥₿]
field:"subject.subject" kind:regex_match value:"[$£€¥₿]"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Link: Spam website with evasion indicators

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

Detects messages containing links to spam websites that show signs of evasion techniques, including blocklisted IP provider messages or rate limiting responses when analyzed.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesEvasion

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// single meaningful root domain in links
and length(filter(distinct(body.links, .href_url.domain.root_domain),
                  // filter out unrelated domains
                  .href_url.domain.root_domain != sender.email.domain.root_domain
                  and any(recipients.to,
                          .email.domain.root_domain != ..href_url.domain.root_domain
                  )
                  and .href_url.domain.root_domain not in ("aka.ms")
           )
) == 1
// specific spam website pattern
and any(body.links,
        // did not redirect to any other domain
        ml.link_analysis(.).effective_url.domain.domain == .href_url.domain.domain
        and (
          // LinkAnalysis was "evaded"
          any(ml.link_analysis(.).effective_url.query_params_decoded["q"],
              strings.icontains(., "IP provider is blacklisted!")
          )
          // or we encountered the rate limiting
          or ml.link_analysis(.).final_dom.inner_text == "Too Many Requests!"
        )
)

Detection logic

Scope: inbound message.

Detects messages containing links to spam websites that show signs of evasion techniques, including blocklisted IP provider messages or rate limiting responses when analyzed.

  1. inbound message
  2. length(filter(distinct(body.links, .href_url.domain.root_domain), .href_url.domain.root_domain != sender.email.domain.root_domain and any(recipients.to, .email.domain.root_domain != ..href_url.domain.root_domain) and .href_url.domain.root_domain not in ('aka.ms'))) is 1
  3. any of body.links where all hold:
    • ml.link_analysis(.).effective_url.domain.domain is .href_url.domain.domain
    • any of:
      • any of ml.link_analysis(.).effective_url.query_params_decoded['q'] where:
        • . contains 'IP provider is blacklisted!'
      • ml.link_analysis(.).final_dom.inner_text is 'Too Many Requests!'

Inspects: body.links, body.links[].href_url.domain.domain, body.links[].href_url.domain.root_domain, recipients.to, recipients.to[].email.domain.root_domain, sender.email.domain.root_domain, type.inbound. Sensors: ml.link_analysis, strings.icontains.

Indicators matched (1)

FieldMatchValue
strings.icontainssubstringIP provider is blacklisted!

Stages and Predicates

Stage 1: mql_rule

and
  any(body.links)
    and
      or
        any(ml.link_analysis(body.links).effective_url.query_params_decoded['q'])
          ml.link_analysis(body.links).effective_url.query_params_decoded['q'] contains "IP provider is blacklisted!"
        ml.link_analysis func_call "ml.link_analysis(body.links[]).final_dom.inner_text == Too Many Requests!"
      ml.link_analysis func_call "ml.link_analysis(body.links[]).effective_url.domain.domain == body.links[].href_url.domain.domain"
  filter(distinct(body.links, .href_url.domain.root_domain), .href_url.domain.root_domain != sender.email.domain.root_domain and any(recipients.to, .email.domain.root_domain != ..href_url.domain.root_domain) and .href_url.domain.root_domain not in ('aka.ms')) length_compare "1"
  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"

Spam: Attendee list solicitation

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

This rule detects messages claiming to have the attendee list from a specific event, they may list various information such as the number of contacts, the demographic and sample contacts. The messages typically offer to send pricing information upon request.

Threat classification

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

CategoryValues
Attack typesSpam

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • body.html
  • body.links
  • body.plain
  • body.previous_threads
  • sender.email
  • subject
  • type

Rule body

type.inbound
and length(body.current_thread.text) < 2000
and length(body.links) < 5
and any(ml.nlu_classifier(coalesce(body.html.display_text, body.plain.raw)).topics,
        .name in ("Contact List Solicitation", "B2B Cold Outreach")
        and .confidence in ("medium", "high")
)
and (
  (
    (
      any([subject.subject, body.current_thread.text],
          (
            regex.icontains(.,
                            '(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Store|Grocer|Lead)(?:[[:punct:]]*s)?(?:\s\w*){0,9}(?:\blist(?:\b|[^ei])|database)'
            )
            and not (
              regex.icount(.,
                           '(email|contact)(?:[[:punct:]]*s)?(?:\s\w*){0,9}list'
              ) == 1
              and regex.icontains(.,
                                  '(?:unsub|remove|safe|delete|leave|update|part of|be added|safe)[[:punct:]]*s?(?:\s\w*){0,9}(mailing|email|my|sender)(?:\s\w*){0,9}list(?:\b|[^ei])',
                                  'email list(?:\b|[^ei])[[:punct:]]*s?(\s\w*){0,5}(?:unsub|remove|safe|delete|leave|up to date|part of|be added)'
              )
            )
          )
          or regex.icontains(.,
                             '\b(?:list|database)(?:[[:punct:]]*s)?\b(\s\w*){0,9}(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Store|Grocer)s?'
          )
      )
    )
    and (
      regex.icontains(body.current_thread.text,
                      "(?:interest(s|ed)|accessing|purchas|obtain|acuir|sample|provide.{0,10}samples|counts|pricing)"
      )
      or any(body.previous_threads,
             regex.icontains(.text,
                             "(?:interest(s|ed)|accessing|purchas|obtain|acuir|sample|provide.{0,10}samples|counts|pricing)"
             )
      )
    )
    and not regex.icontains(body.current_thread.text,
                            "(?:debit card|transaction.{0,20}processed)"
    )
  )
  // if there are indicators of a previous thread, also inspect the previous thread
  or (
    // contains references to the previous thread
    2 of (
      regex.icontains(body.current_thread.text, '(?:get|got|had) a chance'),
      regex.icontains(body.current_thread.text, '(take|move)(\Wthis)?\Wforward'),
      regex.icontains(body.current_thread.text,
                      '(review|drop me a line about) (my|this|it)'
      ),
      regex.icontains(body.current_thread.text, 'missed it( the)? first time'),
      regex.icontains(body.current_thread.text,
                      '(?:below|previous(ly)?|last|prior|earlier) (message|email|sent)'
      ),
      regex.icontains(body.current_thread.text,
                      // "the email I sent you earlier"
                      '(e?mail|message).{0,20}(sent).{0,20}(?:below|previous(ly)?|last|prior|earlier)'
      ),
      regex.icontains(body.current_thread.text,
                      '(sent).{0,50}(e?mail|message) (?:below|previous(ly)?|last|prior|earlier)'
      ),
      regex.icontains(body.current_thread.text, 'follow(?:ing)?(-| )up'),
      regex.icontains(body.current_thread.text, '(?:contact|attendee)s? list'),
      regex.icontains(body.current_thread.text, '(any|get an) update.{0,50}\?'),
      regex.icontains(body.current_thread.text, '(heard?|circling) back'),
      strings.icontains(body.current_thread.text, 'recently sent'),
      strings.icontains(body.current_thread.text, 'still interested'),
      regex.icontains(body.current_thread.text,
                      'did you (get|receive) (it|my (message|e?mail))'
      ),
      regex.icontains(body.current_thread.text, '(swift|quick|short) response'),
      regex.icontains(body.current_thread.text, 'kindly.{0,30}.interested'),
    )
    and any([body.html.display_text, body.plain.raw],
            (
              3 of (
                strings.icontains(., "from:"),
                strings.icontains(., "to:"),
                strings.icontains(., "sent:"),
                strings.icontains(., "date:"),
                strings.icontains(., "cc:"),
                strings.icontains(., "subject:"),
                strings.icontains(., "--Original Message--")
              )
              or strings.icontains(.,
                                   strings.concat(sender.display_name,
                                                  " <",
                                                  sender.email.email,
                                                  "> wrote:"
                                   )
              )
            )
            // match _after_ the previous thread indciators
            and (
              regex.icontains(.,
                              '(?:from|to|sent|date|cc|subject|wrote):(.|\W)*(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Mailing)(?:[[:punct:]]*s)?(?:\s\w*){0,9}(?:list(?:\b|[^ei])|database)'
              )
              or regex.icontains(.,
                                 '(?:from|to|sent|date|cc|subject|wrote):(.|\W)*(?:list(?:\b|[^ei])|database)(?:[[:punct:]]*s)?(\s\w*){0,9}(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Mailing)s?'
              )
              or (
                2 of (
                  strings.icontains(., "provide counts"),
                  regex.icontains(., "(?:verified|fresh) data"),
                  strings.icontains(., "precise targeting"),
                  strings.icontains(., "deliverability"),
                  regex.icontains(., "target (verticals|regions|criteria)")
                )
                and regex.icontains(., '(?:list(?:\b|[^ei])|database)')
              )
            )
    )
  )
)
// negate Zendesk support tickets
and not any(body.links,
            .href_url.domain.root_domain in ('zendesk.com')
            and .display_text == 'Zendesk'
)
and not profile.by_sender().solicited
and not profile.by_sender().any_messages_benign

Detection logic

Scope: inbound message.

This rule detects messages claiming to have the attendee list from a specific event, they may list various information such as the number of contacts, the demographic and sample contacts. The messages typically offer to send pricing information upon request.

  1. inbound message
  2. length(body.current_thread.text) < 2000
  3. length(body.links) < 5
  4. any of ml.nlu_classifier(coalesce(body.html.display_text, body.plain.raw)).topics where all hold:
    • .name in ('Contact List Solicitation', 'B2B Cold Outreach')
    • .confidence in ('medium', 'high')
  5. any of:
    • all of:
      • any of [subject.subject, body.current_thread.text] where any holds:
        • all of:
          • . matches '(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Store|Grocer|Lead)(?:[[:punct:]]*s)?(?:\\s\\w*){0,9}(?:\\blist(?:\\b|[^ei])|database)'
          • not:
            • all of:
              • regex.icount(., '(email|contact)(?:[[:punct:]]*s)?(?:\\s\\w*){0,9}list') is 1
              • . matches any of 2 patterns
                • (?:unsub|remove|safe|delete|leave|update|part of|be added|safe)[[:punct:]]*s?(?:\s\w*){0,9}(mailing|email|my|sender)(?:\s\w*){0,9}list(?:\b|[^ei])
                • email list(?:\b|[^ei])[[:punct:]]*s?(\s\w*){0,5}(?:unsub|remove|safe|delete|leave|up to date|part of|be added)
        • . matches '\\b(?:list|database)(?:[[:punct:]]*s)?\\b(\\s\\w*){0,9}(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Store|Grocer)s?'
      • any of:
        • body.current_thread.text matches '(?:interest(s|ed)|accessing|purchas|obtain|acuir|sample|provide.{0,10}samples|counts|pricing)'
        • any of body.previous_threads where:
          • .text matches '(?:interest(s|ed)|accessing|purchas|obtain|acuir|sample|provide.{0,10}samples|counts|pricing)'
      • not:
        • body.current_thread.text matches '(?:debit card|transaction.{0,20}processed)'
    • all of:
      • at least 2 of:
        • body.current_thread.text matches '(?:get|got|had) a chance'
        • body.current_thread.text matches '(take|move)(\\Wthis)?\\Wforward'
        • body.current_thread.text matches '(review|drop me a line about) (my|this|it)'
        • body.current_thread.text matches 'missed it( the)? first time'
        • body.current_thread.text matches '(?:below|previous(ly)?|last|prior|earlier) (message|email|sent)'
        • body.current_thread.text matches '(e?mail|message).{0,20}(sent).{0,20}(?:below|previous(ly)?|last|prior|earlier)'
        • body.current_thread.text matches '(sent).{0,50}(e?mail|message) (?:below|previous(ly)?|last|prior|earlier)'
        • body.current_thread.text matches 'follow(?:ing)?(-| )up'
        • body.current_thread.text matches '(?:contact|attendee)s? list'
        • body.current_thread.text matches '(any|get an) update.{0,50}\\?'
        • body.current_thread.text matches '(heard?|circling) back'
        • body.current_thread.text contains 'recently sent'
        • body.current_thread.text contains 'still interested'
        • body.current_thread.text matches 'did you (get|receive) (it|my (message|e?mail))'
        • body.current_thread.text matches '(swift|quick|short) response'
        • body.current_thread.text matches 'kindly.{0,30}.interested'
      • any of [body.html.display_text, body.plain.raw] where all hold:
        • any of:
          • at least 3 of 7: . contains any of 7 patterns
            • from:
            • to:
            • sent:
            • date:
            • cc:
            • subject:
            • --Original Message--
          • strings.icontains(.)
        • any of:
          • . matches '(?:from|to|sent|date|cc|subject|wrote):(.|\\W)*(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Mailing)(?:[[:punct:]]*s)?(?:\\s\\w*){0,9}(?:list(?:\\b|[^ei])|database)'
          • . matches '(?:from|to|sent|date|cc|subject|wrote):(.|\\W)*(?:list(?:\\b|[^ei])|database)(?:[[:punct:]]*s)?(\\s\\w*){0,9}(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Mailing)s?'
          • all of:
            • at least 2 of:
              • . contains 'provide counts'
              • . matches '(?:verified|fresh) data'
              • . contains 'precise targeting'
              • . contains 'deliverability'
              • . matches 'target (verticals|regions|criteria)'
            • . matches '(?:list(?:\\b|[^ei])|database)'
  6. not:
    • any of body.links where all hold:
      • .href_url.domain.root_domain in ('zendesk.com')
      • .display_text is 'Zendesk'
  7. not:
    • profile.by_sender().solicited
  8. not:
    • profile.by_sender().any_messages_benign

Inspects: body.current_thread.text, body.html.display_text, body.links, body.links[].display_text, body.links[].href_url.domain.root_domain, body.plain.raw, body.previous_threads, body.previous_threads[].text, sender.display_name, sender.email.email, subject.subject, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, regex.icontains, regex.icount, strings.concat, strings.icontains.

Indicators matched (38)

FieldMatchValue
ml.nlu_classifier(coalesce(body.html.display_text, body.plain.raw)).topics[].namememberContact List Solicitation
ml.nlu_classifier(coalesce(body.html.display_text, body.plain.raw)).topics[].namememberB2B Cold Outreach
ml.nlu_classifier(coalesce(body.html.display_text, body.plain.raw)).topics[].confidencemembermedium
ml.nlu_classifier(coalesce(body.html.display_text, body.plain.raw)).topics[].confidencememberhigh
regex.icontainsregex(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Store|Grocer|Lead)(?:[[:punct:]]*s)?(?:\s\w*){0,9}(?:\blist(?:\b|[^ei])|database)
regex.icontainsregex\b(?:list|database)(?:[[:punct:]]*s)?\b(\s\w*){0,9}(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Store|Grocer)s?
regex.icontainsregex(?:interest(s|ed)|accessing|purchas|obtain|acuir|sample|provide.{0,10}samples|counts|pricing)
regex.icontainsregex(?:get|got|had) a chance
regex.icontainsregex(take|move)(\Wthis)?\Wforward
regex.icontainsregex(review|drop me a line about) (my|this|it)
regex.icontainsregexmissed it( the)? first time
regex.icontainsregex(?:below|previous(ly)?|last|prior|earlier) (message|email|sent)
26 more
regex.icontainsregex(e?mail|message).{0,20}(sent).{0,20}(?:below|previous(ly)?|last|prior|earlier)
regex.icontainsregex(sent).{0,50}(e?mail|message) (?:below|previous(ly)?|last|prior|earlier)
regex.icontainsregexfollow(?:ing)?(-| )up
regex.icontainsregex(?:contact|attendee)s? list
regex.icontainsregex(any|get an) update.{0,50}\?
regex.icontainsregex(heard?|circling) back
strings.icontainssubstringrecently sent
strings.icontainssubstringstill interested
regex.icontainsregexdid you (get|receive) (it|my (message|e?mail))
regex.icontainsregex(swift|quick|short) response
regex.icontainsregexkindly.{0,30}.interested
strings.icontainssubstringfrom:
strings.icontainssubstringto:
strings.icontainssubstringsent:
strings.icontainssubstringdate:
strings.icontainssubstringcc:
strings.icontainssubstringsubject:
strings.icontainssubstring--Original Message--
regex.icontainsregex(?:from|to|sent|date|cc|subject|wrote):(.|\W)*(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Mailing)(?:[[:punct:]]*s)?(?:\s\w*){0,9}(?:list(?:\b|[^ei])|database)
regex.icontainsregex(?:from|to|sent|date|cc|subject|wrote):(.|\W)*(?:list(?:\b|[^ei])|database)(?:[[:punct:]]*s)?(\s\w*){0,9}(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Mailing)s?
strings.icontainssubstringprovide counts
regex.icontainsregex(?:verified|fresh) data
strings.icontainssubstringprecise targeting
strings.icontainssubstringdeliverability
regex.icontainsregextarget (verticals|regions|criteria)
regex.icontainsregex(?:list(?:\b|[^ei])|database)

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      any([subject.subject, body.current_thread.text])
        or
          and
            not
              and
                or
                  [subject.subject, body.current_thread.text] regex_match "(?:unsub|remove|safe|delete|leave|update|part of|be added|safe)[[:punct:]]*s?(?:\\s\\w*){0,9}(mailing|email|my|sender)(?:\\s\\w*){0,9}list(?:\\b|[^ei])"
                  [subject.subject, body.current_thread.text] regex_match "email list(?:\\b|[^ei])[[:punct:]]*s?(\\s\\w*){0,5}(?:unsub|remove|safe|delete|leave|up to date|part of|be added)"
                regex.icount func_call "regex.icount([subject.subject, body.current_thread.text][], \"(email|contact)(?:[[:punct:]]*s)?(?:\\s\\w*){0,9}list\") == 1"
            [subject.subject, body.current_thread.text] regex_match "(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Store|Grocer|Lead)(?:[[:punct:]]*s)?(?:\\s\\w*){0,9}(?:\\blist(?:\\b|[^ei])|database)"
          [subject.subject, body.current_thread.text] regex_match "\\b(?:list|database)(?:[[:punct:]]*s)?\\b(\\s\\w*){0,9}(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Store|Grocer)s?"
      or
        any(body.previous_threads)
          body.previous_threads.text regex_match "(?:interest(s|ed)|accessing|purchas|obtain|acuir|sample|provide.{0,10}samples|counts|pricing)"
        body.current_thread.text regex_match "(?:interest(s|ed)|accessing|purchas|obtain|acuir|sample|provide.{0,10}samples|counts|pricing)"
      not
        body.current_thread.text regex_match "(?:debit card|transaction.{0,20}processed)"
    and
      any([body.html.display_text, body.plain.raw])
        and
          or
            and
              or
                [body.html.display_text, body.plain.raw] contains "deliverability"
                [body.html.display_text, body.plain.raw] contains "precise targeting"
                [body.html.display_text, body.plain.raw] contains "provide counts"
                [body.html.display_text, body.plain.raw] regex_match "(?:verified|fresh) data"
                [body.html.display_text, body.plain.raw] regex_match "target (verticals|regions|criteria)"
              [body.html.display_text, body.plain.raw] regex_match "(?:list(?:\\b|[^ei])|database)"
            [body.html.display_text, body.plain.raw] regex_match "(?:from|to|sent|date|cc|subject|wrote):(.|\\W)*(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Mailing)(?:[[:punct:]]*s)?(?:\\s\\w*){0,9}(?:list(?:\\b|[^ei])|database)"
            [body.html.display_text, body.plain.raw] regex_match "(?:from|to|sent|date|cc|subject|wrote):(.|\\W)*(?:list(?:\\b|[^ei])|database)(?:[[:punct:]]*s)?(\\s\\w*){0,9}(?:Attendee|Buyer|Contact|Decision Maker|Email|Member|Participant|Professional|Registrant|User|Visitor|Mailing)s?"
          or
            [body.html.display_text, body.plain.raw] contains "--Original Message--"
            [body.html.display_text, body.plain.raw] contains "cc:"
            [body.html.display_text, body.plain.raw] contains "date:"
            [body.html.display_text, body.plain.raw] contains "from:"
            [body.html.display_text, body.plain.raw] contains "sent:"
            [body.html.display_text, body.plain.raw] contains "subject:"
            [body.html.display_text, body.plain.raw] contains "to:"
            strings.icontains func_call "strings.icontains([body.html.display_text, body.plain.raw][])"
      or
        body.current_thread.text contains "recently sent"
        body.current_thread.text contains "still interested"
        body.current_thread.text regex_match "(?:below|previous(ly)?|last|prior|earlier) (message|email|sent)"
        body.current_thread.text regex_match "(?:contact|attendee)s? list"
        body.current_thread.text regex_match "(?:get|got|had) a chance"
        body.current_thread.text regex_match "(any|get an) update.{0,50}\\?"
        body.current_thread.text regex_match "(e?mail|message).{0,20}(sent).{0,20}(?:below|previous(ly)?|last|prior|earlier)"
        body.current_thread.text regex_match "(heard?|circling) back"
        body.current_thread.text regex_match "(review|drop me a line about) (my|this|it)"
        body.current_thread.text regex_match "(sent).{0,50}(e?mail|message) (?:below|previous(ly)?|last|prior|earlier)"
        body.current_thread.text regex_match "(swift|quick|short) response"
        body.current_thread.text regex_match "(take|move)(\\Wthis)?\\Wforward"
        body.current_thread.text regex_match "did you (get|receive) (it|my (message|e?mail))"
        body.current_thread.text regex_match "follow(?:ing)?(-| )up"
        body.current_thread.text regex_match "kindly.{0,30}.interested"
        body.current_thread.text regex_match "missed it( the)? first time"
  not
    any(body.links)
      and
        body.links.display_text eq "Zendesk"
        body.links.href_url.domain.root_domain eq "zendesk.com"
  any(ml.nlu_classifier(coalesce(body.html.display_text, body.plain.raw)).topics)
    and
      ml.nlu_classifier(coalesce(body.html.display_text, body.plain.raw)).topics.confidence in ["high", "medium"]
      ml.nlu_classifier(coalesce(body.html.display_text, body.plain.raw)).topics.name in ["B2B Cold Outreach", "Contact List Solicitation"]
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  not
    profile.by_sender func_call "profile.by_sender().solicited"
  body.current_thread.text length_compare "2000"
  body.links length_compare "5"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

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

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textcontains
  • recently sent
  • still interested
field:"body.current_thread.text" kind:contains
body.current_thread.textregex_match
  • (?:below|previous(ly)?|last|prior|earlier) (message|email|sent)
  • (?:contact|attendee)s? list
  • (?:get|got|had) a chance
  • (?:interest(s|ed)|accessing|purchas|obtain|acuir|sample|provide.{0,10}samples|counts|pricing)
  • (any|get an) update.{0,50}?
  • (e?mail|message).{0,20}(sent).{0,20}(?:below|previous(ly)?|last|prior|earlier)
  • (heard?|circling) back
  • (review|drop me a line about) (my|this|it)
  • (sent).{0,50}(e?mail|message) (?:below|previous(ly)?|last|prior|earlier)
  • (swift|quick|short) response
  • (take|move)(\Wthis)?\Wforward
  • did you (get|receive) (it|my (message|e?mail))
  • follow(?:ing)?(-| )up
  • kindly.{0,30}.interested
  • missed it( the)? first time
field:"body.current_thread.text" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: BlackBaud infrastructure abuse

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

Malvertising campaign has been observed abusing a compromised account with BlackBaud. These campaigns have been leveraging brands like Disney+, Netflix, Paramount+, Peacock, UPS, and impersonating the likeness of Elon Musk.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesEvasion, Impersonation: Brand, Image as content, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.links
  • headers
  • headers.domains
  • headers.hops
  • sender.email
  • subject
  • type

Rule body

type.inbound
and regex.imatch(sender.email.email, 'communications[a-z]{4,}@.+')
and any(headers.hops, any(.fields, strings.ilike(.name, "x-campaignid")))
and any(headers.domains, strings.contains(.domain, "blackbaud.com"))
and regex.imatch(subject.subject, 'RE\s?:.*')
and (length(headers.references) == 0 or headers.in_reply_to is null)
and any(body.links, .display_text is null)

Detection logic

Scope: inbound message.

Malvertising campaign has been observed abusing a compromised account with BlackBaud. These campaigns have been leveraging brands like Disney+, Netflix, Paramount+, Peacock, UPS, and impersonating the likeness of Elon Musk.

  1. inbound message
  2. sender.email.email matches 'communications[a-z]{4,}@.+'
  3. any of headers.hops where:
    • any of .fields where:
      • .name matches 'x-campaignid'
  4. any of headers.domains where:
    • .domain contains 'blackbaud.com'
  5. subject.subject matches 'RE\\s?:.*'
  6. any of:
    • length(headers.references) is 0
    • headers.in_reply_to is missing
  7. any of body.links where:
    • .display_text is missing

Inspects: body.links, body.links[].display_text, headers.domains, headers.domains[].domain, headers.hops, headers.hops[].fields, headers.hops[].fields[].name, headers.in_reply_to, headers.references, sender.email.email, subject.subject, type.inbound. Sensors: regex.imatch, strings.contains, strings.ilike.

Indicators matched (4)

FieldMatchValue
regex.imatchregexcommunications[a-z]{4,}@.+
strings.ilikesubstringx-campaignid
strings.containssubstringblackbaud.com
regex.imatchregexRE\s?:.*

Stages and Predicates

Stage 1: mql_rule

and
  any(headers.hops)
    any(headers.hops.fields)
      headers.hops.fields.name eq "x-campaignid"
  any(body.links)
    body.links.display_text is_null
  any(headers.domains)
    headers.domains.domain contains "blackbaud.com"
  or
    headers.in_reply_to is_null
    headers.references length_compare "0"
  sender.email.email regex_match "communications[a-z]{4,}@.+"
  subject.subject regex_match "RE\\s?:.*"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Spam: Campaign with excessive display-text and keywords found

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

Detects affiliate marketing spam where any link contains display-text greater than 3000 chars and specific values found.

Threat classification

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

CategoryValues
Attack typesSpam

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.links
  • type

Rule body

type.inbound
and length(body.links) > 0
and any(body.links, length(.display_text) > 3000)
and any(body.links,
        regex.icontains(.display_text, '(\bPassword:)', 'Hi.{0,5}Welcome\b')
)
and (
  profile.by_sender().prevalence in ("new", "outlier")
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)

Detection logic

Scope: inbound message.

Detects affiliate marketing spam where any link contains display-text greater than 3000 chars and specific values found.

  1. inbound message
  2. length(body.links) > 0
  3. any of body.links where:
    • length(.display_text) > 3000
  4. any of body.links where:
    • .display_text matches any of 2 patterns
      • (\bPassword:)
      • Hi.{0,5}Welcome\b
  5. any of:
    • profile.by_sender().prevalence in ('new', 'outlier')
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign

Inspects: body.links, body.links[].display_text, type.inbound. Sensors: profile.by_sender, regex.icontains.

Indicators matched (2)

FieldMatchValue
regex.icontainsregex(\bPassword:)
regex.icontainsregexHi.{0,5}Welcome\b

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"
    profile.by_sender func_call "profile.by_sender().prevalence in (new, outlier)"
  any(body.links)
    or
      body.links.display_text regex_match "(\\bPassword:)"
      body.links.display_text regex_match "Hi.{0,5}Welcome\\b"
  any(body.links)
    body.links.display_text length_compare "3000"
  body.links length_compare "0"
  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"

Spam: Campaign with excessive space/char obfuscation and free file hosted link

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

This rule detects mass spam campaigns using excessive space padding with links utilizing free file hosting.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesFree file host

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.html
  • body.links
  • sender.email
  • type

Rule body

type.inbound
and length(attachments) == 0
and regex.icontains(coalesce(body.html.inner_text, body.html.display_text),
                    '([a-zA-Z\d\.]\s){30,}'
)
and any(body.links,
        .href_url.domain.domain in $free_file_hosts
        or .href_url.domain.root_domain in $free_file_hosts
)
and (
  profile.by_sender().prevalence in ("new", "outlier")
  or profile.by_sender().any_messages_malicious_or_spam
  or sender.email.domain.valid == false
)
and not profile.by_sender().any_messages_benign

Detection logic

Scope: inbound message.

This rule detects mass spam campaigns using excessive space padding with links utilizing free file hosting.

  1. inbound message
  2. length(attachments) is 0
  3. coalesce(body.html.inner_text, body.html.display_text) matches '([a-zA-Z\\d\\.]\\s){30,}'
  4. any of body.links where any holds:
    • .href_url.domain.domain in $free_file_hosts
    • .href_url.domain.root_domain in $free_file_hosts
  5. any of:
    • profile.by_sender().prevalence in ('new', 'outlier')
    • profile.by_sender().any_messages_malicious_or_spam
    • sender.email.domain.valid is False
  6. not:
    • profile.by_sender().any_messages_benign

Inspects: body.html.display_text, body.html.inner_text, body.links, body.links[].href_url.domain.domain, body.links[].href_url.domain.root_domain, sender.email.domain.valid, type.inbound. Sensors: profile.by_sender, regex.icontains. Reference lists: $free_file_hosts.

Indicators matched (1)

FieldMatchValue
regex.icontainsregex([a-zA-Z\d\.]\s){30,}

Stages and Predicates

Stage 1: mql_rule

and
  any(body.links)
    or
       macro "body.links[].href_url.domain.domain in free_file_hosts"
       macro "body.links[].href_url.domain.root_domain in free_file_hosts"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  or
    profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
    profile.by_sender func_call "profile.by_sender().prevalence in (new, outlier)"
    sender.email.domain.valid eq "false"
  attachments length_compare "0"
  coalesce(body.html.inner_text, body.html.display_text) regex_match "([a-zA-Z\\d\\.]\\s){30,}"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Spam: Cold outreach from Cloudflare-hosted newly registered domain

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

Flags inbound messages from sender domains that were registered fewer than 365 days ago and use exactly two Cloudflare name servers with subdomains ending in '.ns'. The rule further requires that the subject or body text be classified as B2B cold outreach by the NLU model, indicating abuse of Cloudflare's free DNS infrastructure to rapidly stand up new domains for outreach-style spam.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// newly registered sender domain
and network.whois(sender.email.domain).days_old < 365
// there are 2 name servers which have subdomains ending with .ns
and length(network.whois(sender.email.domain).name_servers) == 2
and all(network.whois(sender.email.domain).name_servers,
        strings.iends_with(.subdomain, '.ns')
        and .root_domain == 'cloudflare.com'
)
and any(ml.nlu_classifier(body.current_thread.text).topics,
        .name == 'B2B Cold Outreach'
)

Detection logic

Scope: inbound message.

Flags inbound messages from sender domains that were registered fewer than 365 days ago and use exactly two Cloudflare name servers with subdomains ending in '.ns'. The rule further requires that the subject or body text be classified as B2B cold outreach by the NLU model, indicating abuse of Cloudflare's free DNS infrastructure to rapidly stand up new domains for outreach-style spam.

  1. inbound message
  2. network.whois(sender.email.domain).days_old < 365
  3. length(network.whois(sender.email.domain).name_servers) is 2
  4. all of network.whois(sender.email.domain).name_servers where all hold:
    • .subdomain ends with '.ns'
    • .root_domain is 'cloudflare.com'
  5. any of ml.nlu_classifier(body.current_thread.text).topics where:
    • .name is 'B2B Cold Outreach'

Inspects: body.current_thread.text, sender.email.domain, type.inbound. Sensors: ml.nlu_classifier, network.whois, strings.iends_with.

Indicators matched (3)

FieldMatchValue
strings.iends_withsuffix.ns
network.whois(sender.email.domain).name_servers[].root_domainequalscloudflare.com
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsB2B Cold Outreach

Stages and Predicates

Stage 1: mql_rule

and
  any(ml.nlu_classifier(body.current_thread.text).topics)
    ml.nlu_classifier(body.current_thread.text).topics.name eq "B2B Cold Outreach"
  network.whois func_call "network.whois(sender.email.domain).days_old < 365"
  network.whois(sender.email.domain).name_servers length_compare "2"
  type.inbound eq "true"
   macro "all(network.whois(sender.email.domain).name_servers)"

Indicators

These rows show field, operator, and value matches.

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

Spam: Commonly observed formatting of unauthorized free giveaways

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

Detects commonly observed formatting of unauthorized giveaways, free tools, and products by multiple different brands.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesImpersonation: Brand, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.links
  • subject
  • type

Rule body

type.inbound
and (
  (
    any(html.xpath(body.html, "//div[contains(@style, 'BACKGROUND: URL')]").nodes,
        .raw is not null
    )
  )
  or (
    any(body.links,
        any([
              "blob.core.windows.net",
              "click.email.formula1.com",
              "firmy-praha.eu"
            ],
            ..href_url.domain.domain == .
            or strings.ends_with(..href_url.domain.domain, .)
        )
    )
  )
)
and (
  (
    // subject has # plus random characters only
    regex.icontains(subject.base, "#[a-z0-9]{5,}?")
    // plus one of these
    and (
      // display name has a # + random characters only
      regex.icontains(sender.display_name, "#[a-z0-9]{5,}?")
      // subject starts with a period (yes, both subject cases should be true)
      or strings.starts_with(subject.base, ".")
      // Display name contains at least 2 emojis
      or length(distinct(map(regex.extract(sender.display_name,
                                           '(?P<emoji>[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}])'
                             ),
                             .full_match
                         )
                )
      ) >= 2
    )
  )
  or (
    // Subject contains at least 2 emojias
    length(distinct(map(regex.extract(subject.base,
                                      '(?P<emoji>[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}])'
                        ),
                        .full_match
                    )
           )
    ) >= 2
  )
  or 
  // another variant with different strings that have numbers but the same pattern is in both subject and displayname
  (
    // subject has # plus random characters & numbers
    regex.icontains(subject.base, "#[1-9a-z]+")
    // plus one of these
    and (
      regex.icontains(sender.display_name, "#[1-9a-z]+")
      or strings.icontains(sender.display_name, "rewards")
    )
  )
  or (
    // or prornotions (promotions) once confusables are stripped in subject
    strings.icontains(strings.replace_confusables(subject.base), "prornotions")
    // and rewards in display name
    and strings.icontains(sender.display_name, "rewards")
  )
  or (
    // subject has * plus 4 random characters and numbers *
    regex.icontains(subject.base, '\*[1-9a-z]{4,}\*')
    // same with the display name
    and regex.icontains(sender.display_name, '\*[1-9a-z]{4,}\*')
  )
  or (
    // subject and display name has two *
    strings.count(subject.base, "*") == 2
    and strings.count(sender.display_name, "*") == 2
  )
  or (
    // subject has string of random characters and numbers
    // checking if string has 1 uppercase, 1 lowercase and 1 number
    any(regex.extract(subject.base, '(?:-{1,2}|\s)([a-zA-Z0-9]{11,})'),
        regex.contains(.full_match, '[A-Z]')
        and regex.contains(.full_match, '[a-z]')
        and regex.contains(.full_match, '[0-9]')
        // some matches are legit but they are 35+ characters
        and length(.full_match) <= 30
    )
    // negating support thread email subjects containg multiple : in their IDs
    and not regex.count(subject.base, ':') > 5
  )
)

Detection logic

Scope: inbound message.

Detects commonly observed formatting of unauthorized giveaways, free tools, and products by multiple different brands.

  1. inbound message
  2. any of:
    • any of html.xpath(body.html, "//div[contains(@style, 'BACKGROUND: URL')]").nodes where:
      • .raw is set
    • any of body.links where:
      • any of ['blob.core.windows.net', 'click.email.formula1.com', 'firmy-praha.eu'] where any holds:
        • .href_url.domain.domain is .
        • strings.ends_with(.href_url.domain.domain)
  3. any of:
    • all of:
      • subject.base matches '#[a-z0-9]{5,}?'
      • any of:
        • sender.display_name matches '#[a-z0-9]{5,}?'
        • subject.base starts with '.'
        • length(distinct(map(regex.extract(sender.display_name, '(?P<emoji>[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}])'), .full_match))) ≥ 2
    • length(distinct(map(regex.extract(subject.base, '(?P<emoji>[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}])'), .full_match))) ≥ 2
    • all of:
      • subject.base matches '#[1-9a-z]+'
      • any of:
        • sender.display_name matches '#[1-9a-z]+'
        • sender.display_name contains 'rewards'
    • all of:
      • strings.replace_confusables(subject.base) contains 'prornotions'
      • sender.display_name contains 'rewards'
    • all of:
      • subject.base matches '\\*[1-9a-z]{4,}\\*'
      • sender.display_name matches '\\*[1-9a-z]{4,}\\*'
    • all of:
      • strings.count(subject.base, '*') is 2
      • strings.count(sender.display_name, '*') is 2
    • all of:
      • any of regex.extract(subject.base) where all hold:
        • .full_match matches '[A-Z]'
        • .full_match matches '[a-z]'
        • .full_match matches '[0-9]'
        • length(.full_match) ≤ 30
      • not:
        • regex.count(subject.base, ':') > 5

Inspects: body.html, body.links, body.links[].href_url.domain.domain, sender.display_name, subject.base, type.inbound. Sensors: html.xpath, regex.contains, regex.count, regex.extract, regex.icontains, strings.count, strings.ends_with, strings.icontains, strings.replace_confusables, strings.starts_with.

Indicators matched (11)

FieldMatchValue
regex.icontainsregex#[a-z0-9]{5,}?
strings.starts_withprefix.
regex.extractregex(?P<emoji>[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}])
regex.icontainsregex#[1-9a-z]+
strings.icontainssubstringrewards
strings.icontainssubstringprornotions
regex.icontainsregex\*[1-9a-z]{4,}\*
regex.extractregex(?:-{1,2}|\s)([a-zA-Z0-9]{11,})
regex.containsregex[A-Z]
regex.containsregex[a-z]
regex.containsregex[0-9]

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(body.links)
      any(['blob.core.windows.net', 'click.email.formula1.com', 'firmy-praha.eu'])
        or
          body.links.href_url.domain.domain cross_field_compare "['blob.core.windows.net', 'click.email.formula1.com', 'firmy-praha.eu']"
          strings.ends_with func_call "strings.ends_with(body.links[].href_url.domain.domain)"
    any(html.xpath(body.html, "//div[contains(@style, 'BACKGROUND: URL')]").nodes)
      html.xpath(body.html, "//div[contains(@style, 'BACKGROUND: URL')]").nodes.raw is_not_null
  or
    and
      any(regex.extract(subject.base))
        and
          regex.extract(subject.base).full_match length_compare "30"
          regex.extract(subject.base).full_match regex_match "[0-9]"
          regex.extract(subject.base).full_match regex_match "[A-Z]"
          regex.extract(subject.base).full_match regex_match "[a-z]"
      not
        regex.count func_call "regex.count(subject.base, \":\") > 5"
    and
      or
        distinct(map(regex.extract(sender.display_name, '(?P<emoji>[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}])'), .full_match)) length_compare "2"
        sender.display_name regex_match "#[a-z0-9]{5,}?"
        subject.base starts_with "."
      subject.base regex_match "#[a-z0-9]{5,}?"
    and
      or
        sender.display_name contains "rewards"
        sender.display_name regex_match "#[1-9a-z]+"
      subject.base regex_match "#[1-9a-z]+"
    and
      sender.display_name contains "rewards"
      strings.replace_confusables(subject.base) contains "prornotions"
    and
      sender.display_name regex_match "\\*[1-9a-z]{4,}\\*"
      subject.base regex_match "\\*[1-9a-z]{4,}\\*"
    and
      strings.count func_call "strings.count(sender.display_name, \"*\") == 2"
      strings.count func_call "strings.count(subject.base, \"*\") == 2"
    distinct(map(regex.extract(subject.base, '(?P<emoji>[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}])'), .full_match)) length_compare "2"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Spam: Cryptocurrency airdrop/giveaway

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

Detects messages promoting cryptocurrency airdrops, token claims, or wallet-related rewards.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesSocial engineering, Impersonation: Brand

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • 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).topics,
        .name in ("Financial Communications")
)
and not any(ml.nlu_classifier(body.current_thread.text).topics,
            .name in (
              'Newsletters and Digests',
              'News and Current Events',
              'Legal and Compliance'
            )
)
and not any(ml.nlu_classifier(body.current_thread.text).intents,
            .name == "benign" and .confidence == "high"
)
// action word
and (
  strings.ilike(subject.base, '*airdrop*')
  or strings.like(body.current_thread.text, '*airdrop*')
  or strings.ilike(subject.base, '*giveaway*')
  or strings.like(body.current_thread.text, '*giveaway*')
  or regex.icontains(body.current_thread.text, 'receive \$\d{2,5} worth')
  or regex.icontains(body.current_thread.text,
                     'claim your (allocation|airdrop|bonus|share|\$\d{2,5})'
  )
  or regex.icontains(body.current_thread.text, 'connect .{0,20} wallet')
)
and not regex.icontains(body.current_thread.text, '\$\d{2,4} (off|cash)')
and (
  // crypto keyword
  regex.icontains(body.current_thread.text,
                  '\bmetamask\b',
                  '\bethereum\b',
                  '\bbinance\b',
                  '\bgemini\b',
                  '\bwallet\b',
                  '\bkraken\b',
                  '\bsolana\b',
                  '\btrezor\b',
                  '\bledger\b'
  )
  // token name, e.g. $USDT
  or regex.contains(body.current_thread.text, '\s\$[A-Z]{3,4}\s')
)
and not (
  sender.email.domain.root_domain in (
    "gemini.com",
    "ledger.com",
    "binance.com",
    "trezor.io",
    "kraken.com",
    "solana.com",
    "metamask.com",
    "ethereum.org",
    "bloomberg.com"
  )
  and headers.auth_summary.dmarc.pass
)

Detection logic

Scope: inbound message.

Detects messages promoting cryptocurrency airdrops, token claims, or wallet-related rewards.

  1. inbound message
  2. any of ml.nlu_classifier(body.current_thread.text).topics where:
    • .name in ('Financial Communications')
  3. not:
    • any of ml.nlu_classifier(body.current_thread.text).topics where:
      • .name in ('Newsletters and Digests', 'News and Current Events', 'Legal and Compliance')
  4. not:
    • any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
      • .name is 'benign'
      • .confidence is 'high'
  5. any of:
    • subject.base matches '*airdrop*'
    • body.current_thread.text matches '*airdrop*'
    • subject.base matches '*giveaway*'
    • body.current_thread.text matches '*giveaway*'
    • body.current_thread.text matches 'receive \\$\\d{2,5} worth'
    • body.current_thread.text matches 'claim your (allocation|airdrop|bonus|share|\\$\\d{2,5})'
    • body.current_thread.text matches 'connect .{0,20} wallet'
  6. not:
    • body.current_thread.text matches '\\$\\d{2,4} (off|cash)'
  7. any of:
    • body.current_thread.text matches any of 9 patterns
      • \bmetamask\b
      • \bethereum\b
      • \bbinance\b
      • \bgemini\b
      • \bwallet\b
      • \bkraken\b
      • \bsolana\b
      • \btrezor\b
      • \bledger\b
    • body.current_thread.text matches '\\s\\$[A-Z]{3,4}\\s'
  8. not:
    • all of:
      • sender.email.domain.root_domain in ('gemini.com', 'ledger.com', 'binance.com', 'trezor.io', 'kraken.com', 'solana.com', 'metamask.com', 'ethereum.org', 'bloomberg.com')
      • headers.auth_summary.dmarc.pass

Inspects: body.current_thread.text, headers.auth_summary.dmarc.pass, sender.email.domain.root_domain, subject.base, type.inbound. Sensors: ml.nlu_classifier, regex.contains, regex.icontains, strings.ilike, strings.like.

Indicators matched (18)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).topics[].namememberFinancial Communications
strings.ilikesubstring*airdrop*
strings.likesubstring*airdrop*
strings.ilikesubstring*giveaway*
strings.likesubstring*giveaway*
regex.icontainsregexreceive \$\d{2,5} worth
regex.icontainsregexclaim your (allocation|airdrop|bonus|share|\$\d{2,5})
regex.icontainsregexconnect .{0,20} wallet
regex.icontainsregex\bmetamask\b
regex.icontainsregex\bethereum\b
regex.icontainsregex\bbinance\b
regex.icontainsregex\bgemini\b
6 more
regex.icontainsregex\bwallet\b
regex.icontainsregex\bkraken\b
regex.icontainsregex\bsolana\b
regex.icontainsregex\btrezor\b
regex.icontainsregex\bledger\b
regex.containsregex\s\$[A-Z]{3,4}\s

Stages and Predicates

Stage 1: mql_rule

and
  not
    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 "benign"
  not
    and
      headers.auth_summary.dmarc.pass eq "true"
      sender.email.domain.root_domain in ["binance.com", "bloomberg.com", "ethereum.org", "gemini.com", "kraken.com", "ledger.com", "metamask.com", "solana.com", "trezor.io"]
  not
    any(ml.nlu_classifier(body.current_thread.text).topics)
      ml.nlu_classifier(body.current_thread.text).topics.name in ["Legal and Compliance", "News and Current Events", "Newsletters and Digests"]
  or
    body.current_thread.text match "airdrop"
    body.current_thread.text match "giveaway"
    body.current_thread.text regex_match "claim your (allocation|airdrop|bonus|share|\\$\\d{2,5})"
    body.current_thread.text regex_match "connect .{0,20} wallet"
    body.current_thread.text regex_match "receive \\$\\d{2,5} worth"
    subject.base match "airdrop"
    subject.base match "giveaway"
  not
    body.current_thread.text regex_match "\\$\\d{2,4} (off|cash)"
  or
    body.current_thread.text regex_match "\\bbinance\\b"
    body.current_thread.text regex_match "\\bethereum\\b"
    body.current_thread.text regex_match "\\bgemini\\b"
    body.current_thread.text regex_match "\\bkraken\\b"
    body.current_thread.text regex_match "\\bledger\\b"
    body.current_thread.text regex_match "\\bmetamask\\b"
    body.current_thread.text regex_match "\\bsolana\\b"
    body.current_thread.text regex_match "\\btrezor\\b"
    body.current_thread.text regex_match "\\bwallet\\b"
    body.current_thread.text regex_match "\\s\\$[A-Z]{3,4}\\s"
  any(ml.nlu_classifier(body.current_thread.text).topics)
    ml.nlu_classifier(body.current_thread.text).topics.name eq "Financial Communications"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
ml.nlu_classifier(body.current_thread.text).intentsarray_any(no value, null check)excludes:ml.nlu_classifier(body.current_thread.text).intents
headers.auth_summary.dmarc.passeqtrueexcludes:headers.auth_summary.dmarc.pass field:"headers.auth_summary.dmarc.pass" value:"true"
sender.email.domain.root_domaininbinance.com, bloomberg.com, ethereum.org, gemini.com, kraken.com, ledger.com, metamask.com, solana.com, trezor.ioexcludes:sender.email.domain.root_domain
ml.nlu_classifier(body.current_thread.text).topicsarray_any(no value, null check)excludes:ml.nlu_classifier(body.current_thread.text).topics
body.current_thread.textregex_match$\d{2,4} (off|cash)excludes:body.current_thread.text field:"body.current_thread.text" value:"$\d{2,4} (off|cash)"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • \bbinance\b
  • \bethereum\b
  • \bgemini\b
  • \bkraken\b
  • \bledger\b
  • \bmetamask\b
  • \bsolana\b
  • \btrezor\b
  • \bwallet\b
  • \s$[A-Z]{3,4}\s
  • claim your (allocation|airdrop|bonus|share|$\d{2,5})
  • connect .{0,20} wallet
  • receive $\d{2,5} worth
field:"body.current_thread.text" kind:regex_match
body.current_thread.textwildcard
  • *airdrop*
  • *giveaway*
field:"body.current_thread.text" kind:wildcard
subject.basewildcard
  • *airdrop*
  • *giveaway*
field:"subject.base" kind:wildcard
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: Default Microsoft Exchange Online sender domain (onmicrosoft.com)

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

The default Microsoft Exchange Online sender domain, onmicrosoft.com, is commonly used to send unwanted and malicious email. Enable this rule in your environment if receiving email from the onmicrosoft.com domain is unexpected behaviour.

Threat classification

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

CategoryValues
Attack typesCallback Phishing, Credential Phishing, Spam
Tactics and techniquesFree email provider, Impersonation: Brand, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • attachments
  • body
  • body.links
  • headers
  • headers.auth_summary
  • headers.hops
  • recipients
  • sender.email
  • subject
  • type

Rule body

type.inbound
and (
  sender.email.domain.root_domain == "onmicrosoft.com"
  or (
    strings.icontains(sender.display_name, "onmicrosoft.com")
    and sender.email.domain.valid == false
  )
)
and length(recipients.to) < 2
and length(body.links) > 0

// bounce-back negations
and not strings.like(sender.email.local_part,
                     "*postmaster*",
                     "*mailer-daemon*",
                     "*administrator*"
)
and not any(attachments,
            .content_type in (
              "message/rfc822",
              "message/delivery-status",
              "text/calendar"
            )
            or (.content_type == "text/plain" and .file_extension == "ics")
)

// negating legit replies
and not (
  (
    strings.istarts_with(subject.subject, "RE:")
    or strings.istarts_with(subject.subject, "FW:")
    or strings.istarts_with(subject.subject, "FWD:")
    or regex.imatch(subject.subject,
                    '(\[[^\]]+\]\s?){0,3}(re|fwd?|automat.*)\s?:.*'
    )
    or strings.istarts_with(subject.subject, "Réponse automatique")
  )
  and (length(headers.references) > 0 and headers.in_reply_to is not null)
)

// negate auto-replies
and not (
  any(headers.hops,
      any(.fields, .name =~ "auto-submitted" and .value =~ "auto-generated")
      and any(.fields,
              .name =~ "X-MS-Exchange-Generated-Message-Source"
              and .value not in ("Antispam Quarantine Agent")
      )
  )
)

// Microsoft has some legit onmicrosoft domains...
and not (
  sender.email.domain.domain == "microsoft.onmicrosoft.com"
  and headers.auth_summary.spf.pass
  and all(body.links, .href_url.domain.root_domain == "microsoft.com")
)

// negate legitimate microsoft b2b applications invitations
and not (
  length(body.links) > 0
  and (
    sender.email.local_part == "invites"
    and sender.email.domain.root_domain == "onmicrosoft.com"
    // infra validated message id
    and strings.icontains(headers.message_id, "pepf")
  )
)

// construct the proper sender domain and check against known recipients
and not strings.concat(sender.email.domain.subdomain,
                       ".",
                       sender.email.domain.tld
) in $recipient_domains

// sender profiles
and (
  not profile.by_sender().solicited
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)

// negate org domains
and not (
  sender.email.domain.domain in $org_domains
  and coalesce(headers.auth_summary.dmarc.pass, false)
)

Detection logic

Scope: inbound message.

The default Microsoft Exchange Online sender domain, onmicrosoft.com, is commonly used to send unwanted and malicious email. Enable this rule in your environment if receiving email from the onmicrosoft.com domain is unexpected behaviour.

  1. inbound message
  2. any of:
    • sender.email.domain.root_domain is 'onmicrosoft.com'
    • all of:
      • sender.display_name contains 'onmicrosoft.com'
      • sender.email.domain.valid is False
  3. length(recipients.to) < 2
  4. length(body.links) > 0
  5. not:
    • sender.email.local_part matches any of 3 patterns
      • *postmaster*
      • *mailer-daemon*
      • *administrator*
  6. not:
    • any of attachments where any holds:
      • .content_type in ('message/rfc822', 'message/delivery-status', 'text/calendar')
      • all of:
        • .content_type is 'text/plain'
        • .file_extension is 'ics'
  7. not:
    • all of:
      • any of:
        • subject.subject starts with 'RE:'
        • subject.subject starts with 'FW:'
        • subject.subject starts with 'FWD:'
        • subject.subject matches '(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?|automat.*)\\s?:.*'
        • subject.subject starts with 'Réponse automatique'
      • all of:
        • length(headers.references) > 0
        • headers.in_reply_to is set
  8. not:
    • any of headers.hops where all hold:
      • any of .fields where all hold:
        • .name is 'auto-submitted'
        • .value is 'auto-generated'
      • any of .fields where all hold:
        • .name is 'X-MS-Exchange-Generated-Message-Source'
        • .value not in ('Antispam Quarantine Agent')
  9. not:
    • all of:
      • sender.email.domain.domain is 'microsoft.onmicrosoft.com'
      • headers.auth_summary.spf.pass
      • all of body.links where:
        • .href_url.domain.root_domain is 'microsoft.com'
  10. not:
    • all of:
      • length(body.links) > 0
      • all of:
        • sender.email.local_part is 'invites'
        • sender.email.domain.root_domain is 'onmicrosoft.com'
        • headers.message_id contains 'pepf'
  11. not:
    • strings.concat(sender.email.domain.subdomain, '.') in $recipient_domains
  12. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
  13. not:
    • all of:
      • sender.email.domain.domain in $org_domains
      • coalesce(headers.auth_summary.dmarc.pass)

Inspects: attachments[].content_type, attachments[].file_extension, body.links, body.links[].href_url.domain.root_domain, headers.auth_summary.dmarc.pass, headers.auth_summary.spf.pass, headers.hops, headers.hops[].fields, headers.hops[].fields[].name, headers.hops[].fields[].value, headers.in_reply_to, headers.message_id, headers.references, recipients.to, sender.display_name, sender.email.domain.domain, sender.email.domain.root_domain, sender.email.domain.subdomain, sender.email.domain.tld, sender.email.domain.valid, sender.email.local_part, subject.subject, type.inbound. Sensors: profile.by_sender, regex.imatch, strings.concat, strings.icontains, strings.istarts_with, strings.like. Reference lists: $org_domains, $recipient_domains.

Indicators matched (3)

FieldMatchValue
sender.email.domain.root_domainequalsonmicrosoft.com
strings.icontainssubstringonmicrosoft.com
headers.hops[].fields[].valuememberAntispam Quarantine Agent

Stages and Predicates

Stage 1: mql_rule

and
  not
    any(headers.hops)
      and
        any(headers.hops.fields)
          and
            not
              headers.hops.fields[].value eq "Antispam Quarantine Agent"
            headers.hops.fields[].name eq "X-MS-Exchange-Generated-Message-Source"
        any(headers.hops.fields)
          and
            headers.hops.fields[].name eq "auto-submitted"
            headers.hops.fields[].value eq "auto-generated"
  not
    any(attachments)
      or
        and
          attachments.content_type eq "text/plain"
          attachments.file_extension eq "ics"
        attachments.content_type in ["message/delivery-status", "message/rfc822", "text/calendar"]
  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
      or
        subject.subject regex_match "(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?|automat.*)\\s?:.*"
        subject.subject starts_with "FW:"
        subject.subject starts_with "FWD:"
        subject.subject starts_with "RE:"
        subject.subject starts_with "Réponse automatique"
      headers.in_reply_to is_not_null
      headers.references length_compare "0"
  not
    and
      body.links length_compare "0"
      headers.message_id contains "pepf"
      sender.email.domain.root_domain eq "onmicrosoft.com"
      sender.email.local_part eq "invites"
  not
    and
      coalesce func_call "coalesce(headers.auth_summary.dmarc.pass)"
       macro "sender.email.domain.domain in org_domains"
  not
    and
      headers.auth_summary.spf.pass eq "true"
      sender.email.domain.domain eq "microsoft.onmicrosoft.com"
       macro "all(body.links)"
  or
    and
      sender.display_name contains "onmicrosoft.com"
      sender.email.domain.valid eq "false"
    sender.email.domain.root_domain eq "onmicrosoft.com"
  not
    or
      sender.email.local_part match "administrator"
      sender.email.local_part match "mailer-daemon"
      sender.email.local_part match "postmaster"
  not
    strings.concat func_call "strings.concat(sender.email.domain.subdomain, \".\") in recipient_domains"
  body.links length_compare "0"
  recipients.to length_compare "2"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
headers.hopsarray_any(no value, null check)excludes:headers.hops
attachmentsarray_any(no value, null check)excludes:attachments
subject.subjectregex_match([[^]]+]\s?){0,3}(re|fwd?|automat.*)\s?:.*excludes:subject.subject field:"subject.subject" value:"([[^]]+]\s?){0,3}(re|fwd?|automat.*)\s?:.*"
subject.subjectstarts_withFW:excludes:subject.subject field:"subject.subject" value:"FW:"
subject.subjectstarts_withFWD:excludes:subject.subject field:"subject.subject" value:"FWD:"
subject.subjectstarts_withRE:excludes:subject.subject field:"subject.subject" value:"RE:"
subject.subjectstarts_withRéponse automatiqueexcludes:subject.subject field:"subject.subject" value:"Réponse automatique"
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"
body.linkslength_compare0excludes:body.links field:"body.links" value:"0"
headers.message_idcontainspepfexcludes:headers.message_id field:"headers.message_id" value:"pepf"
sender.email.domain.root_domaineqonmicrosoft.comexcludes:sender.email.domain.root_domain field:"sender.email.domain.root_domain" value:"onmicrosoft.com"
sender.email.local_parteqinvitesexcludes:sender.email.local_part field:"sender.email.local_part" value:"invites"
headers.auth_summary.spf.passeqtrueexcludes:headers.auth_summary.spf.pass field:"headers.auth_summary.spf.pass" value:"true"
sender.email.domain.domaineqmicrosoft.onmicrosoft.comexcludes:sender.email.domain.domain field:"sender.email.domain.domain" value:"microsoft.onmicrosoft.com"
sender.email.local_partmatchadministratorexcludes:sender.email.local_part field:"sender.email.local_part" value:"administrator"
sender.email.local_partmatchmailer-daemonexcludes:sender.email.local_part field:"sender.email.local_part" value:"mailer-daemon"
sender.email.local_partmatchpostmasterexcludes:sender.email.local_part field:"sender.email.local_part" value:"postmaster"

Indicators

These rows show field, operator, and value matches.

Spam: Fake dating profile notification

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

Detects dating-themed messages from free email providers containing links with the recipient's email address embedded in URL parameters, combined with suspicious language or topics in the message body.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesFree email provider, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and sender.email.domain.root_domain in $free_email_providers
// not a reply
and length(headers.references) == 0
and 0 < length(distinct(body.current_thread.links, .href_url.domain.root_domain)
) <= 3
and any(body.links,
        (
          any(values(.href_url.query_params_decoded),
              any(.,
                  strings.parse_email(.).email in map(recipients.to,
                                                      .email.email
                  )
              )
          )
        )
        or regex.icontains(.display_text,
                           '(?:(open|view|read|private).{0,15}message)'
        )
        or any([.href_url.path, .display_text],
               any((ml.nlu_classifier(.).topics),
                   .name in ("Romance", "Sexually Explicit Messages")
               )
        )
        // visit the URL and check the effecitve url
        or any(values(ml.link_analysis(.).effective_url.query_params_decoded),
               any(.,
                   strings.parse_email(.).email in map(recipients.to,
                                                       .email.email
                   )
               )
        )
)
and (
  any(ml.nlu_classifier(body.current_thread.text).entities,
      .name == "org"
      and strings.ilike(.text,
                        "*Flirt*",
                        "*Singles*",
                        "*Date*",
                        "*Dating*",
                        "*Girls*",
                        "*Love*",
                        "*Hook*up*"
      )
  )
  or any(ml.nlu_classifier(body.current_thread.text).topics, .name == "Romance")
)

Detection logic

Scope: inbound message.

Detects dating-themed messages from free email providers containing links with the recipient's email address embedded in URL parameters, combined with suspicious language or topics in the message body.

  1. inbound message
  2. sender.email.domain.root_domain in $free_email_providers
  3. length(headers.references) is 0
  4. all of:
    • length(distinct(body.current_thread.links, .href_url.domain.root_domain)) > 0
    • length(distinct(body.current_thread.links, .href_url.domain.root_domain)) ≤ 3
  5. any of body.links where any holds:
    • any of values(.href_url.query_params_decoded) where:
      • any of . where:
        • strings.parse_email(.).email in map(recipients.to, .email.email)
    • .display_text matches '(?:(open|view|read|private).{0,15}message)'
    • any of [.href_url.path, .display_text] where:
      • any of ml.nlu_classifier(.).topics where:
        • .name in ('Romance', 'Sexually Explicit Messages')
    • any of values(...) where:
      • any of . where:
        • strings.parse_email(.).email in map(recipients.to, .email.email)
  6. any of:
    • any of ml.nlu_classifier(body.current_thread.text).entities where all hold:
      • .name is 'org'
      • .text matches any of 7 patterns
        • *Flirt*
        • *Singles*
        • *Date*
        • *Dating*
        • *Girls*
        • *Love*
        • *Hook*up*
    • any of ml.nlu_classifier(body.current_thread.text).topics where:
      • .name is 'Romance'

Inspects: body.current_thread.links, body.current_thread.links[].href_url.domain.root_domain, body.current_thread.text, body.links, body.links[].display_text, body.links[].href_url.path, body.links[].href_url.query_params_decoded, headers.references, recipients.to, recipients.to[].email.email, sender.email.domain.root_domain, type.inbound. Sensors: ml.link_analysis, ml.nlu_classifier, regex.icontains, strings.ilike, strings.parse_email. Reference lists: $free_email_providers.

Indicators matched (12)

FieldMatchValue
regex.icontainsregex(?:(open|view|read|private).{0,15}message)
ml.nlu_classifier([body.links[].href_url.path, body.links[].display_text][]).topics[].namememberRomance
ml.nlu_classifier([body.links[].href_url.path, body.links[].display_text][]).topics[].namememberSexually Explicit Messages
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsorg
strings.ilikesubstring*Flirt*
strings.ilikesubstring*Singles*
strings.ilikesubstring*Date*
strings.ilikesubstring*Dating*
strings.ilikesubstring*Girls*
strings.ilikesubstring*Love*
strings.ilikesubstring*Hook*up*
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsRomance

Stages and Predicates

Stage 1: mql_rule

and
  any(body.links)
    or
      any([body.links.href_url.path, body.links.display_text])
        any(ml.nlu_classifier([body.links.href_url.path, body.links.display_text]).topics)
          ml.nlu_classifier([body.links.href_url.path, body.links.display_text]).topics.name in ["Romance", "Sexually Explicit Messages"]
      any(values(...))
        any(values(...))
          strings.parse_email func_call "strings.parse_email(values(...)[][]).email in map(recipients.to, values(...)[][].email.email)"
      any(values(body.links.href_url.query_params_decoded))
        any(values(body.links.href_url.query_params_decoded))
          strings.parse_email func_call "strings.parse_email(values(body.links[].href_url.query_params_decoded)[][]).email in map(recipients.to, values(body.links[].href_url.query_params_decoded)[][].email.email)"
      body.links.display_text regex_match "(?:(open|view|read|private).{0,15}message)"
  or
    any(ml.nlu_classifier(body.current_thread.text).entities)
      and
        or
          ml.nlu_classifier(body.current_thread.text).entities.text match "Date"
          ml.nlu_classifier(body.current_thread.text).entities.text match "Dating"
          ml.nlu_classifier(body.current_thread.text).entities.text match "Flirt"
          ml.nlu_classifier(body.current_thread.text).entities.text match "Girls"
          ml.nlu_classifier(body.current_thread.text).entities.text match "Love"
          ml.nlu_classifier(body.current_thread.text).entities.text match "Singles"
          ml.nlu_classifier(body.current_thread.text).entities.text wildcard "*Hook*up*"
        ml.nlu_classifier(body.current_thread.text).entities.name eq "org"
    any(ml.nlu_classifier(body.current_thread.text).topics)
      ml.nlu_classifier(body.current_thread.text).topics.name eq "Romance"
  distinct(body.current_thread.links, .href_url.domain.root_domain) length_compare "0"
  distinct(body.current_thread.links, .href_url.domain.root_domain) length_compare "3"
  headers.references length_compare "0"
  type.inbound eq "true"
   macro "sender.email.domain.root_domain in free_email_providers"

Indicators

These rows show field, operator, and value matches.

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

Spam: Fake photo share

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

Message contains pretexting language about sharing photos ("found these photos and thought you'd like them", "remember these photos?") and a link with a newly registered domain. Fake threads and plain text bodies have been seen in the wild, indicating active evasion techniques.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and length(attachments) == 0
and (
  (
    (
      (
        (length(body.plain.raw) < 500 and length(body.current_thread.text) == 0)
        or (
          length(body.html.display_text) < 500
          and length(body.current_thread.text) == 0
        )
        or 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 (
        strings.ilike(subject.subject,
                      "*picture*",
                      "*photo*",
                      "*image*",
                      "*sad news*",
                      "*sad announcement*",
                      "*sad update*",
                      "*new pics*",
                      "*Reunion*",
                      "*planing*",
                      "*back memories*",
                      "*any memories*",
                      "*old memories*",
                      "*evoke memories*",
                      "*bittersweet memories*"
        )
        or regex.icontains(subject.base, 'pics?\s*\(\d{1,2}\)')
      )
    )
    or (
      (
        (
          length(body.html.display_text) < 500
          and length(body.current_thread.text) == 0
        )
        and strings.ilike(body.html.display_text,
                          "*picture*",
                          "*photo*",
                          "*image*",
                          "*sad news*",
                          "*sad announcement*",
                          "*sad update*",
                          "*new pics*",
                          "*back memories*",
                          "*any memories*",
                          "*old memories*",
                          "*evoke memories*",
                          "*bittersweet memories*"
        )
      )
      or (
        (length(body.plain.raw) < 500 and length(body.current_thread.text) == 0)
        and strings.ilike(body.plain.raw,
                          "*picture*",
                          "*photo*",
                          "*image*",
                          "*sad news*",
                          "*sad announcement*",
                          "*sad update*",
                          "*new pics*",
                          "*back memories*",
                          "*any memories*",
                          "*old memories*",
                          "*evoke memories*",
                          "*bittersweet memories*"
        )
        and not strings.icontains(body.plain.raw, "[cid:image")
      )
      or (
        length(body.current_thread.text) < 500
        and strings.ilike(body.current_thread.text,
                          "*picture*",
                          "*photo*",
                          "*image*",
                          "*sad news*",
                          "*sad announcement*",
                          "*sad update*",
                          "*new pics*",
                          "*back memories*",
                          "*any memories*",
                          "*old memories*",
                          "*evoke memories*",
                          "*bittersweet memories*"
        )
      )
    )
    or (
      body.plain.raw is not null
      and body.html.display_text is null
      and (
        length(body.current_thread.text) == 0
        or (
          length(body.current_thread.text) < 500
          // fake forward indicator in the plain text body
          and (
            regex.contains(body.plain.raw,
                           'On (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote'
            )
            or strings.icontains(body.plain.raw, 'Original Message')
          )
          and not regex.contains(body.current_thread.text,
                                 'On (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote'
          )
          and not strings.icontains(body.current_thread.text,
                                    'Original Message'
          )
          and not any(headers.hops, any(.fields, .name == "Resent-From"))
        )
      )
      and strings.ilike(body.plain.raw,
                        "*picture*",
                        "*photo*",
                        "*image*",
                        "*sad news*",
                        "*sad announcement*",
                        "*sad update*",
                        "*pics*",
                        "*back memories*",
                        "*any memories*",
                        "*old memories*",
                        "*evoke memories*",
                        "*bittersweet memories*"
      )
      and not strings.istarts_with(body.plain.raw, "[cid:image")
      and strings.icontains(subject.subject, sender.display_name)
    )
    or (
      strings.icontains(subject.subject, sender.display_name)
      and sender.email.domain.root_domain in $free_email_providers
      and length(body.links) == 2
      and length(filter(body.links, .display_text == "h")) == 1
      and length(filter(body.links, .display_url.scheme == "ttp")) == 1
    )
  )
  and length(body.links) < 5
  and any(body.links,
          (
            (
              network.whois(.href_url.domain).days_old < 30
              or network.whois(.display_url.domain).days_old < 30
              or not network.whois(.href_url.domain).found
              or network.whois(.href_url.domain).found is null
            )
            and .href_url.domain.root_domain != sender.email.domain.root_domain
          )
          or (
            length(.display_text) == 1
            and .href_url.domain.root_domain in ("facebook.com", "youtube.com")
          )
          or (
            // random 5-6 character subdomain
            regex.icontains(.href_url.domain.domain,
                            '^[a-z]{5,6}\.[a-z]{5,}\.[a-z]+'
            )
            // subdomain contains 3+ consecutive consonants
            and regex.icontains(.href_url.domain.domain,
                                '^[a-z]*[b-df-hj-np-tv-z]{3,}[a-z]*\.'
            )
            and network.whois(.href_url.domain).days_old < 365
          )
  )
)
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, "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?)\s?:')
      )
    )
  )
  or length(headers.references) == 0
)
// 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.

Message contains pretexting language about sharing photos ("found these photos and thought you'd like them", "remember these photos?") and a link with a newly registered domain. Fake threads and plain text bodies have been seen in the wild, indicating active evasion techniques.

  1. inbound message
  2. length(attachments) is 0
  3. all of:
    • any of:
      • all of:
        • any of:
          • all of:
            • length(body.plain.raw) < 500
            • length(body.current_thread.text) is 0
          • all of:
            • length(body.html.display_text) < 500
            • length(body.current_thread.text) is 0
          • length(body.current_thread.text) < 500
          • any of map(...) where:
            • length(body.current_thread.text) - length(.) < 500
        • any of:
          • subject.subject matches any of 14 patterns
            • *picture*
            • *photo*
            • *image*
            • *sad news*
            • *sad announcement*
            • *sad update*
            • *new pics*
            • *Reunion*
            • *planing*
            • *back memories*
            • *any memories*
            • *old memories*
            • *evoke memories*
            • *bittersweet memories*
          • subject.base matches 'pics?\\s*\\(\\d{1,2}\\)'
      • any of:
        • all of:
          • all of:
            • length(body.html.display_text) < 500
            • length(body.current_thread.text) is 0
          • body.html.display_text matches any of 12 patterns
            • *picture*
            • *photo*
            • *image*
            • *sad news*
            • *sad announcement*
            • *sad update*
            • *new pics*
            • *back memories*
            • *any memories*
            • *old memories*
            • *evoke memories*
            • *bittersweet memories*
        • all of:
          • all of:
            • length(body.plain.raw) < 500
            • length(body.current_thread.text) is 0
          • body.plain.raw matches any of 12 patterns
            • *picture*
            • *photo*
            • *image*
            • *sad news*
            • *sad announcement*
            • *sad update*
            • *new pics*
            • *back memories*
            • *any memories*
            • *old memories*
            • *evoke memories*
            • *bittersweet memories*
          • not:
            • body.plain.raw contains '[cid:image'
        • all of:
          • length(body.current_thread.text) < 500
          • body.current_thread.text matches any of 12 patterns
            • *picture*
            • *photo*
            • *image*
            • *sad news*
            • *sad announcement*
            • *sad update*
            • *new pics*
            • *back memories*
            • *any memories*
            • *old memories*
            • *evoke memories*
            • *bittersweet memories*
      • all of:
        • body.plain.raw is set
        • body.html.display_text is missing
        • any of:
          • length(body.current_thread.text) is 0
          • all of:
            • length(body.current_thread.text) < 500
            • any of:
              • body.plain.raw matches 'On (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote'
              • body.plain.raw contains 'Original Message'
            • not:
              • body.current_thread.text matches 'On (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote'
            • not:
              • body.current_thread.text contains 'Original Message'
            • not:
              • any of headers.hops where:
                • any of .fields where:
                  • .name is 'Resent-From'
        • body.plain.raw matches any of 12 patterns
          • *picture*
          • *photo*
          • *image*
          • *sad news*
          • *sad announcement*
          • *sad update*
          • *pics*
          • *back memories*
          • *any memories*
          • *old memories*
          • *evoke memories*
          • *bittersweet memories*
        • not:
          • body.plain.raw starts with '[cid:image'
        • strings.icontains(subject.subject)
      • all of:
        • strings.icontains(subject.subject)
        • sender.email.domain.root_domain in $free_email_providers
        • length(body.links) is 2
        • length(filter(body.links, .display_text == 'h')) is 1
        • length(filter(body.links, .display_url.scheme == 'ttp')) is 1
    • length(body.links) < 5
    • any of body.links where any holds:
      • all of:
        • any of:
          • network.whois(.href_url.domain).days_old < 30
          • network.whois(.display_url.domain).days_old < 30
          • not:
            • network.whois(.href_url.domain).found
          • network.whois(.href_url.domain).found is missing
        • .href_url.domain.root_domain is not sender.email.domain.root_domain
      • all of:
        • length(.display_text) is 1
        • .href_url.domain.root_domain in ('facebook.com', 'youtube.com')
      • all of:
        • .href_url.domain.domain matches '^[a-z]{5,6}\\.[a-z]{5,}\\.[a-z]+'
        • .href_url.domain.domain matches '^[a-z]*[b-df-hj-np-tv-z]{3,}[a-z]*\\.'
        • network.whois(.href_url.domain).days_old < 365
  4. 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 '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?:'
    • length(headers.references) is 0
  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: body.current_thread.text, body.html.display_text, body.links, body.links[].display_text, body.links[].display_url.domain, body.links[].display_url.scheme, body.links[].href_url.domain, body.links[].href_url.domain.domain, body.links[].href_url.domain.root_domain, body.plain.raw, headers.auth_summary.dmarc.pass, headers.hops, headers.hops[].fields, headers.hops[].fields[].name, headers.in_reply_to, headers.references, sender.display_name, sender.email.domain.root_domain, subject.base, subject.subject, type.inbound. Sensors: ml.nlu_classifier, network.whois, regex.contains, regex.icontains, regex.imatch, strings.icontains, strings.ilike, strings.istarts_with. Reference lists: $free_email_providers, $high_trust_sender_root_domains.

Indicators matched (25)

FieldMatchValue
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsdisclaimer
strings.ilikesubstring*picture*
strings.ilikesubstring*photo*
strings.ilikesubstring*image*
strings.ilikesubstring*sad news*
strings.ilikesubstring*sad announcement*
strings.ilikesubstring*sad update*
strings.ilikesubstring*new pics*
strings.ilikesubstring*Reunion*
strings.ilikesubstring*planing*
strings.ilikesubstring*back memories*
strings.ilikesubstring*any memories*
13 more
strings.ilikesubstring*old memories*
strings.ilikesubstring*evoke memories*
strings.ilikesubstring*bittersweet memories*
regex.icontainsregexpics?\s*\(\d{1,2}\)
regex.containsregexOn (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote
strings.icontainssubstringOriginal Message
strings.ilikesubstring*pics*
body.links[].display_textequalsh
body.links[].display_url.schemeequalsttp
body.links[].href_url.domain.root_domainmemberfacebook.com
body.links[].href_url.domain.root_domainmemberyoutube.com
regex.icontainsregex^[a-z]{5,6}\.[a-z]{5,}\.[a-z]+
regex.icontainsregex^[a-z]*[b-df-hj-np-tv-z]{3,}[a-z]*\.

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        and
          not
            any(headers.hops)
              any(headers.hops.fields)
                headers.hops.fields.name eq "Resent-From"
          not
            body.current_thread.text contains "Original Message"
          not
            body.current_thread.text regex_match "On (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote"
          or
            body.plain.raw contains "Original Message"
            body.plain.raw regex_match "On (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote"
          body.current_thread.text length_compare "500"
        body.current_thread.text length_compare "0"
      or
        body.plain.raw match "any memories"
        body.plain.raw match "back memories"
        body.plain.raw match "bittersweet memories"
        body.plain.raw match "evoke memories"
        body.plain.raw match "image"
        body.plain.raw match "old memories"
        body.plain.raw match "photo"
        body.plain.raw match "pics"
        body.plain.raw match "picture"
        body.plain.raw match "sad announcement"
        body.plain.raw match "sad news"
        body.plain.raw match "sad update"
      not
        body.plain.raw starts_with "[cid:image"
      body.html.display_text is_null
      body.plain.raw is_not_null
      strings.icontains func_call "strings.icontains(subject.subject)"
    and
      or
        and
          body.current_thread.text length_compare "0"
          body.html.display_text length_compare "500"
        and
          body.current_thread.text length_compare "0"
          body.plain.raw length_compare "500"
        any(map(...))
           macro "(length(body.current_thread.text) - length(map(...)[])) < 500"
        body.current_thread.text length_compare "500"
      or
        subject.base regex_match "pics?\\s*\\(\\d{1,2}\\)"
        subject.subject match "Reunion"
        subject.subject match "any memories"
        subject.subject match "back memories"
        subject.subject match "bittersweet memories"
        subject.subject match "evoke memories"
        subject.subject match "image"
        subject.subject match "new pics"
        subject.subject match "old memories"
        subject.subject match "photo"
        subject.subject match "picture"
        subject.subject match "planing"
        subject.subject match "sad announcement"
        subject.subject match "sad news"
        subject.subject match "sad update"
    and
      or
        body.current_thread.text match "any memories"
        body.current_thread.text match "back memories"
        body.current_thread.text match "bittersweet memories"
        body.current_thread.text match "evoke memories"
        body.current_thread.text match "image"
        body.current_thread.text match "new pics"
        body.current_thread.text match "old memories"
        body.current_thread.text match "photo"
        body.current_thread.text match "picture"
        body.current_thread.text match "sad announcement"
        body.current_thread.text match "sad news"
        body.current_thread.text match "sad update"
      body.current_thread.text length_compare "500"
    and
      or
        body.html.display_text match "any memories"
        body.html.display_text match "back memories"
        body.html.display_text match "bittersweet memories"
        body.html.display_text match "evoke memories"
        body.html.display_text match "image"
        body.html.display_text match "new pics"
        body.html.display_text match "old memories"
        body.html.display_text match "photo"
        body.html.display_text match "picture"
        body.html.display_text match "sad announcement"
        body.html.display_text match "sad news"
        body.html.display_text match "sad update"
      body.current_thread.text length_compare "0"
      body.html.display_text length_compare "500"
    and
      not
        body.plain.raw contains "[cid:image"
      or
        body.plain.raw match "any memories"
        body.plain.raw match "back memories"
        body.plain.raw match "bittersweet memories"
        body.plain.raw match "evoke memories"
        body.plain.raw match "image"
        body.plain.raw match "new pics"
        body.plain.raw match "old memories"
        body.plain.raw match "photo"
        body.plain.raw match "picture"
        body.plain.raw match "sad announcement"
        body.plain.raw match "sad news"
        body.plain.raw match "sad update"
      body.current_thread.text length_compare "0"
      body.plain.raw length_compare "500"
    and
      body.links length_compare "2"
      filter(body.links, .display_text == 'h') length_compare "1"
      filter(body.links, .display_url.scheme == 'ttp') length_compare "1"
      strings.icontains func_call "strings.icontains(subject.subject)"
       macro "sender.email.domain.root_domain in free_email_providers"
  any(body.links)
    or
      and
        or
          not
            network.whois func_call "network.whois(body.links[].href_url.domain).found"
          network.whois func_call "network.whois(body.links[].display_url.domain).days_old < 30"
          network.whois func_call "network.whois(body.links[].href_url.domain).days_old < 30"
          network.whois(body.links[].href_url.domain).found is_null
        body.links.href_url.domain.root_domain cross_field_compare "sender.email.domain.root_domain"
      and
        body.links.display_text length_compare "1"
        body.links.href_url.domain.root_domain in ["facebook.com", "youtube.com"]
      and
        body.links.href_url.domain.domain regex_match "^[a-z]*[b-df-hj-np-tv-z]{3,}[a-z]*\\."
        body.links.href_url.domain.domain regex_match "^[a-z]{5,6}\\.[a-z]{5,}\\.[a-z]+"
        network.whois func_call "network.whois(body.links[].href_url.domain).days_old < 365"
  or
    and
      not
        or
          subject.subject regex_match "(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?)\\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 "TR:"
          subject.subject starts_with "答复:"
      or
        headers.in_reply_to is_null
        headers.references length_compare "0"
    headers.references length_compare "0"
  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"
  attachments length_compare "0"
  body.links length_compare "5"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textwildcard
  • *any memories*
  • *back memories*
  • *bittersweet memories*
  • *evoke memories*
  • *image*
  • *new pics*
  • *old memories*
  • *photo*
  • *picture*
  • *sad announcement*
  • *sad news*
  • *sad update*
field:"body.current_thread.text" kind:wildcard
body.html.display_textis_null
  • (no value, null check)
field:"body.html.display_text" kind:is_null
body.html.display_textwildcard
  • *any memories*
  • *back memories*
  • *bittersweet memories*
  • *evoke memories*
  • *image*
  • *new pics*
  • *old memories*
  • *photo*
  • *picture*
  • *sad announcement*
  • *sad news*
  • *sad update*
field:"body.html.display_text" kind:wildcard
body.plain.rawcontains
  • Original Message
field:"body.plain.raw" kind:contains value:"Original Message"
body.plain.rawis_not_null
  • (no value, null check)
field:"body.plain.raw" kind:is_not_null
body.plain.rawregex_match
  • On (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote
field:"body.plain.raw" kind:regex_match value:"On (Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday).{0,50} wrote"
body.plain.rawwildcard
  • *any memories*
  • *back memories*
  • *bittersweet memories*
  • *evoke memories*
  • *image*
  • *new pics*
  • *old memories*
  • *photo*
  • *pics*
  • *picture*
  • *sad announcement*
  • *sad news*
  • *sad update*
field:"body.plain.raw" kind:wildcard
headers.in_reply_tois_null
  • (no value, null check)
field:"headers.in_reply_to" kind:is_null
subject.baseregex_match
  • pics?\s*(\d{1,2})
field:"subject.base" kind:regex_match value:"pics?\s*(\d{1,2})"
subject.subjectwildcard
  • *Reunion*
  • *any memories*
  • *back memories*
  • *bittersweet memories*
  • *evoke memories*
  • *image*
  • *new pics*
  • *old memories*
  • *photo*
  • *picture*
  • *planing*
  • *sad announcement*
  • *sad news*
  • *sad update*
field:"subject.subject" kind:wildcard
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: Firebase password reset from suspicious sender

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

Detects Firebase password reset messages from suspicious or new senders that may be attempting to abuse the Firebase authentication service.

Threat classification

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

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

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and sender.email.domain.root_domain == "firebaseapp.com"
and length(filter(body.links,
                  .href_url.domain.root_domain not in ("aka.ms")
                  and .href_url.path is not null
           )
) == 1
and any(body.links,
        .href_url.domain.domain == sender.email.domain.domain
        and .href_url.path == "/__/auth/action"
        and any(.href_url.query_params_decoded["mode"], . == "resetPassword")
)
and (
  (
    not profile.by_sender().solicited
    and profile.by_sender().prevalence == "new"
  )
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
  or not headers.auth_summary.dmarc.pass
)

Detection logic

Scope: inbound message.

Detects Firebase password reset messages from suspicious or new senders that may be attempting to abuse the Firebase authentication service.

  1. inbound message
  2. sender.email.domain.root_domain is 'firebaseapp.com'
  3. length(filter(body.links, .href_url.domain.root_domain not in ('aka.ms') and .href_url.path is not null)) is 1
  4. any of body.links where all hold:
    • .href_url.domain.domain is sender.email.domain.domain
    • .href_url.path is '/__/auth/action'
    • any of .href_url.query_params_decoded['mode'] where:
      • . is 'resetPassword'
  5. any of:
    • all of:
      • not:
        • profile.by_sender().solicited
      • profile.by_sender().prevalence is 'new'
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
    • not:
      • headers.auth_summary.dmarc.pass

Inspects: body.links, body.links[].href_url.domain.domain, body.links[].href_url.domain.root_domain, body.links[].href_url.path, body.links[].href_url.query_params_decoded['mode'], headers.auth_summary.dmarc.pass, sender.email.domain.domain, sender.email.domain.root_domain, type.inbound. Sensors: profile.by_sender.

Indicators matched (3)

FieldMatchValue
sender.email.domain.root_domainequalsfirebaseapp.com
body.links[].href_url.pathequals/__/auth/action
body.links[].href_url.query_params_decoded['mode'][]equalsresetPassword

Stages and Predicates

Stage 1: mql_rule

and
  any(body.links)
    and
      any(body.links.href_url.query_params_decoded['mode'])
        body.links.href_url.query_params_decoded['mode'] eq "resetPassword"
      body.links.href_url.domain.domain cross_field_compare "sender.email.domain.domain"
      body.links.href_url.path eq "/__/auth/action"
  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 == new"
    not
      headers.auth_summary.dmarc.pass eq "true"
  filter(body.links, .href_url.domain.root_domain not in ('aka.ms') and .href_url.path is not null) length_compare "1"
  sender.email.domain.root_domain eq "firebaseapp.com"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Spam: Ghostwriting services scam with manipulative language

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

Detects unsolicited messages promoting ghostwriting or book publishing services that use manipulative language patterns commonly seen in scams, such as offering complimentary samples, expressing fascination with the recipient's achievements, or requesting personal information under the guise of writing assistance.

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
  • subject
  • type

Rule body

type.inbound
and length(body.current_thread.text) < 1500
and (
  // Ghostwriting and book-related terms in subject
  strings.icontains(subject.subject, "book project")
  or strings.icontains(subject.subject, "ghostwriting")
  or strings.icontains(subject.subject, "becoming an author")
  or strings.icontains(subject.subject, "your book")
  or strings.icontains(subject.subject, "writing project")
  or strings.icontains(subject.subject, "publish")
  or strings.icontains(subject.subject, " author ")

  // Body contains ghostwriting service offers
  or strings.icontains(body.current_thread.text, "ghostwriting")
  or strings.icontains(body.current_thread.text, "ghostwriter")
  or strings.icontains(body.current_thread.text, "writing firm")
  or strings.icontains(body.current_thread.text, "book writing")
  or strings.icontains(body.current_thread.text, "publishing")
)
// Common scam language patterns
and (
  (
    strings.icontains(body.current_thread.text, "complimentary")
    and (
      strings.icontains(body.current_thread.text, "sample")
      or strings.icontains(body.current_thread.text, "chapter")
      or strings.icontains(body.current_thread.text, "consultation")
    )
  )
  or (
    strings.icontains(body.current_thread.text, "fascinated")
    and strings.icontains(body.current_thread.text, "what you have done")
  )
  or strings.icontains(body.current_thread.text, "inspiring stories")
  or strings.icontains(body.current_thread.text, "gather some information")
  or strings.icontains(body.current_thread.text, "few minutes of your time")
  or strings.icontains(body.current_thread.text, "absolutely no obligation")
  or strings.icontains(body.current_thread.text, "writing team")
)
and not (
  any(ml.nlu_classifier(body.current_thread.text).topics,
      .name in (
        "Newsletters and Digests",
        "Events and Webinars",
        "Educational and Research",
        "Financial Communications",
        "Advertising and Promotions",
        "Legal and Compliance",
        "Professional and Career Development"
      )
      and .confidence == "high"
  )
)

// Standard negations
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 unsolicited messages promoting ghostwriting or book publishing services that use manipulative language patterns commonly seen in scams, such as offering complimentary samples, expressing fascination with the recipient's achievements, or requesting personal information under the guise of writing assistance.

  1. inbound message
  2. length(body.current_thread.text) < 1500
  3. any of:
    • subject.subject contains 'book project'
    • subject.subject contains 'ghostwriting'
    • subject.subject contains 'becoming an author'
    • subject.subject contains 'your book'
    • subject.subject contains 'writing project'
    • subject.subject contains 'publish'
    • subject.subject contains ' author '
    • body.current_thread.text contains 'ghostwriting'
    • body.current_thread.text contains 'ghostwriter'
    • body.current_thread.text contains 'writing firm'
    • body.current_thread.text contains 'book writing'
    • body.current_thread.text contains 'publishing'
  4. any of:
    • all of:
      • body.current_thread.text contains 'complimentary'
      • any of:
        • body.current_thread.text contains 'sample'
        • body.current_thread.text contains 'chapter'
        • body.current_thread.text contains 'consultation'
    • all of:
      • body.current_thread.text contains 'fascinated'
      • body.current_thread.text contains 'what you have done'
    • body.current_thread.text contains 'inspiring stories'
    • body.current_thread.text contains 'gather some information'
    • body.current_thread.text contains 'few minutes of your time'
    • body.current_thread.text contains 'absolutely no obligation'
    • body.current_thread.text contains 'writing team'
  5. not:
    • any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
      • .name in ('Newsletters and Digests', 'Events and Webinars', 'Educational and Research', 'Financial Communications', 'Advertising and Promotions', 'Legal and Compliance', 'Professional and Career Development')
      • .confidence is 'high'
  6. any of:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • not:
        • headers.auth_summary.dmarc.pass
    • sender.email.domain.root_domain not in $high_trust_sender_root_domains

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

Indicators matched (22)

FieldMatchValue
strings.icontainssubstringbook project
strings.icontainssubstringghostwriting
strings.icontainssubstringbecoming an author
strings.icontainssubstringyour book
strings.icontainssubstringwriting project
strings.icontainssubstringpublish
strings.icontainssubstring author
strings.icontainssubstringghostwriter
strings.icontainssubstringwriting firm
strings.icontainssubstringbook writing
strings.icontainssubstringpublishing
strings.icontainssubstringcomplimentary
10 more
strings.icontainssubstringsample
strings.icontainssubstringchapter
strings.icontainssubstringconsultation
strings.icontainssubstringfascinated
strings.icontainssubstringwhat you have done
strings.icontainssubstringinspiring stories
strings.icontainssubstringgather some information
strings.icontainssubstringfew minutes of your time
strings.icontainssubstringabsolutely no obligation
strings.icontainssubstringwriting team

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        body.current_thread.text contains "chapter"
        body.current_thread.text contains "consultation"
        body.current_thread.text contains "sample"
      body.current_thread.text contains "complimentary"
    and
      body.current_thread.text contains "fascinated"
      body.current_thread.text contains "what you have done"
    body.current_thread.text contains "absolutely no obligation"
    body.current_thread.text contains "few minutes of your time"
    body.current_thread.text contains "gather some information"
    body.current_thread.text contains "inspiring stories"
    body.current_thread.text contains "writing team"
  or
    and
      not
        headers.auth_summary.dmarc.pass eq "true"
       macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
     macro "sender.email.domain.root_domain not in high_trust_sender_root_domains"
  not
    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 ["Advertising and Promotions", "Educational and Research", "Events and Webinars", "Financial Communications", "Legal and Compliance", "Newsletters and Digests", "Professional and Career Development"]
  or
    body.current_thread.text contains "book writing"
    body.current_thread.text contains "ghostwriter"
    body.current_thread.text contains "ghostwriting"
    body.current_thread.text contains "publishing"
    body.current_thread.text contains "writing firm"
    subject.subject contains " author "
    subject.subject contains "becoming an author"
    subject.subject contains "book project"
    subject.subject contains "ghostwriting"
    subject.subject contains "publish"
    subject.subject contains "writing project"
    subject.subject contains "your book"
  body.current_thread.text length_compare "1500"
  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
body.current_thread.textcontains
  • absolutely no obligation
  • book writing
  • chapter
  • complimentary
  • consultation
  • fascinated
  • few minutes of your time
  • gather some information
  • ghostwriter
  • ghostwriting
  • inspiring stories
  • publishing
  • sample
  • what you have done
  • writing firm
  • writing team
field:"body.current_thread.text" kind:contains
subject.subjectcontains
  • author
  • becoming an author
  • book project
  • ghostwriting
  • publish
  • writing project
  • your book
field:"subject.subject" kind:contains
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: Item giveaway spam template

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

This detection rule matches on observed html templates impersonating multiple popular brands used to deliver spam. Often the lure leverages a theme of item giveaways or a chance to win an item for completing a survey.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesImage as content

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and (not profile.by_sender().solicited or sender.email.email == "")
// 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 regex.icontains(body.html.raw,
                    '<(?:div|body)[^\>]*\>\s*<a href=(?:\x22[^\>]+)\>\s*[^\n]*<br>\s*<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\><\/a>\s*<br>\s*<a href=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\><\/a>\s*<br>',
                    '<center>(?:<[^\>]+>)*<a href=(?:\x22[^\>]+)\>\s*[^\n]*<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\>\s*<br>\s*\<img src=(?:\x22[^\>]+)\><\/a>'
)

Detection logic

Scope: inbound message.

This detection rule matches on observed html templates impersonating multiple popular brands used to deliver spam. Often the lure leverages a theme of item giveaways or a chance to win an item for completing a survey.

  1. inbound message
  2. any of:
    • not:
      • profile.by_sender().solicited
    • sender.email.email is ''
  3. 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
  4. body.html.raw matches any of 2 patterns
    • <(?:div|body)[^\>]*\>\s*<a href=(?:\x22[^\>]+)\>\s*[^\n]*<br>\s*<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\><\/a>\s*<br>\s*<a href=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\><\/a>\s*<br>
    • <center>(?:<[^\>]+>)*<a href=(?:\x22[^\>]+)\>\s*[^\n]*<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\>\s*<br>\s*\<img src=(?:\x22[^\>]+)\><\/a>

Inspects: body.html.raw, headers.auth_summary.dmarc.pass, sender.email.domain.root_domain, sender.email.email, type.inbound. Sensors: profile.by_sender, regex.icontains. Reference lists: $high_trust_sender_root_domains.

Indicators matched (3)

FieldMatchValue
sender.email.emailequals
regex.icontainsregex<(?:div|body)[^\>]*\>\s*<a href=(?:\x22[^\>]+)\>\s*[^\n]*<br>\s*<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\><\/a>\s*<br>\s*<a href=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\><\/a>\s*<br>
regex.icontainsregex<center>(?:<[^\>]+>)*<a href=(?:\x22[^\>]+)\>\s*[^\n]*<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\>\s*<br>\s*\<img src=(?:\x22[^\>]+)\><\/a>

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
    not
      profile.by_sender func_call "profile.by_sender().solicited"
    sender.email.email eq ""
  or
    body.html.raw regex_match "<(?:div|body)[^\\>]*\\>\\s*<a href=(?:\\x22[^\\>]+)\\>\\s*[^\\n]*<br>\\s*<img src=(?:\\x22[^\\>]+)\\>\\s*\\<img src=(?:\\x22[^\\>]+)\\><\\/a>\\s*<br>\\s*<a href=(?:\\x22[^\\>]+)\\>\\s*\\<img src=(?:\\x22[^\\>]+)\\>\\s*\\<img src=(?:\\x22[^\\>]+)\\><\\/a>\\s*<br>"
    body.html.raw regex_match "<center>(?:<[^\\>]+>)*<a href=(?:\\x22[^\\>]+)\\>\\s*[^\\n]*<img src=(?:\\x22[^\\>]+)\\>\\s*\\<img src=(?:\\x22[^\\>]+)\\>\\s*<br>\\s*\\<img src=(?:\\x22[^\\>]+)\\><\\/a>"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.html.rawregex_match
  • <(?:div|body)[^\>]*\>\s*<a href=(?:\x22[^\>]+)\>\s*[^\n]*<br>\s*<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\><\/a>\s*<br>\s*<a href=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\><\/a>\s*<br>
  • <center>(?:<[^\>]+>)*<a href=(?:\x22[^\>]+)\>\s*[^\n]*<img src=(?:\x22[^\>]+)\>\s*\<img src=(?:\x22[^\>]+)\>\s*<br>\s*\<img src=(?:\x22[^\>]+)\><\/a>
field:"body.html.raw" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: Large financial amount mention from newly registered sender domain

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

Detects inbound messages from domains less than 180 days old that solicit investment interest from named organizations or individuals on behalf of a purported private family office. Senders use financial-sounding names and rotate first names and target company names while keeping the subject line structure nearly identical. The outreach is designed to appear as a legitimate investment inquiry to bait a response.

Threat classification

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

CategoryValues
Attack typesBEC/Fraud, Spam
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.html
  • sender.email
  • subject
  • type

Rule body

type.inbound
and network.whois(sender.email.domain).days_old < 180
and any([body.html.display_text, subject.base],
        regex.icontains(., '\$\d\p{Pd}\$\d{1,2}(?:m\b|million)')
)

Detection logic

Scope: inbound message.

Detects inbound messages from domains less than 180 days old that solicit investment interest from named organizations or individuals on behalf of a purported private family office. Senders use financial-sounding names and rotate first names and target company names while keeping the subject line structure nearly identical. The outreach is designed to appear as a legitimate investment inquiry to bait a response.

  1. inbound message
  2. network.whois(sender.email.domain).days_old < 180
  3. any of [body.html.display_text, subject.base] where:
    • . matches '\\$\\d\\p{Pd}\\$\\d{1,2}(?:m\\b|million)'

Inspects: body.html.display_text, sender.email.domain, subject.base, type.inbound. Sensors: network.whois, regex.icontains.

Indicators matched (1)

FieldMatchValue
regex.icontainsregex\$\d\p{Pd}\$\d{1,2}(?:m\b|million)

Stages and Predicates

Stage 1: mql_rule

and
  any([body.html.display_text, subject.base])
    [body.html.display_text, subject.base] regex_match "\\$\\d\\p{Pd}\\$\\d{1,2}(?:m\\b|million)"
  network.whois func_call "network.whois(sender.email.domain).days_old < 180"
  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"

Spam: Link to blob.core.windows.net from new domain (<30d)

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

This rule detects messages containing a link to blob.core.windows.net from a sender domain less than 30 days old. There is a single recipient present, but the recipient is a random email address, and not someone at the organization.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesFree subdomain host

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and length(recipients.to) == 1
and any(recipients.to, .email.domain.root_domain not in $org_domains)
and network.whois(sender.email.domain).days_old < 30
and length(body.links) < 3
and any(body.links,
        strings.ends_with(.href_url.domain.subdomain, "blob.core")
        and .href_url.domain.root_domain == "windows.net"
)
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.

This rule detects messages containing a link to blob.core.windows.net from a sender domain less than 30 days old. There is a single recipient present, but the recipient is a random email address, and not someone at the organization.

  1. inbound message
  2. length(recipients.to) is 1
  3. any of recipients.to where:
    • .email.domain.root_domain not in $org_domains
  4. network.whois(sender.email.domain).days_old < 30
  5. length(body.links) < 3
  6. any of body.links where all hold:
    • .href_url.domain.subdomain ends with 'blob.core'
    • .href_url.domain.root_domain is 'windows.net'
  7. any of:
    • not:
      • profile.by_sender().solicited
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
  8. not:
    • profile.by_sender().any_messages_benign

Inspects: body.links, body.links[].href_url.domain.root_domain, body.links[].href_url.domain.subdomain, recipients.to, recipients.to[].email.domain.root_domain, sender.email.domain, type.inbound. Sensors: network.whois, profile.by_sender, strings.ends_with. Reference lists: $org_domains.

Indicators matched (2)

FieldMatchValue
strings.ends_withsuffixblob.core
body.links[].href_url.domain.root_domainequalswindows.net

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"
  any(body.links)
    and
      body.links.href_url.domain.root_domain eq "windows.net"
      body.links.href_url.domain.subdomain ends_with "blob.core"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  any(recipients.to)
     macro "recipients.to[].email.domain.root_domain not in org_domains"
  body.links length_compare "3"
  network.whois func_call "network.whois(sender.email.domain).days_old < 30"
  recipients.to length_compare "1"
  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"

Spam: Mastercard promotional content with image-based body

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

Detects messages promoting untrustworthy Mastercard credit cards that contain both financial communications and promotional content topics, with the message body primarily consisting of image content rather than text. Excludes legitimate payment-related Mastercard communications and applies additional scrutiny to high-trust sender domains that fail DMARC authentication.

Threat classification

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

CategoryValues
Attack typesCredential Phishing, Spam
Tactics and techniquesImage as content, Impersonation: Brand, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and length(attachments) == 0
and not subject.is_forward
and any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics,
        .name == "Financial Communications"
)
and any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics,
        .name == "Advertising and Promotions"
)

// mastercard mention
and strings.icontains(beta.ocr(file.message_screenshot()).text, "mastercard")
and not strings.icontains(beta.ocr(file.message_screenshot()).text,
                          "paying with mastercard"
)

// body is image
and (
  length(beta.ocr(file.message_screenshot()).text) / length(body.current_thread.text
  )
) > 10
and length(body.previous_threads) == 0

// 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 messages promoting untrustworthy Mastercard credit cards that contain both financial communications and promotional content topics, with the message body primarily consisting of image content rather than text. Excludes legitimate payment-related Mastercard communications and applies additional scrutiny to high-trust sender domains that fail DMARC authentication.

  1. inbound message
  2. length(attachments) is 0
  3. not:
    • subject.is_forward
  4. any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics where:
    • .name is 'Financial Communications'
  5. any of ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics where:
    • .name is 'Advertising and Promotions'
  6. beta.ocr(file.message_screenshot()).text contains 'mastercard'
  7. not:
    • beta.ocr(file.message_screenshot()).text contains 'paying with mastercard'
  8. length(beta.ocr(file.message_screenshot()).text) / length(body.current_thread.text) > 10
  9. length(body.previous_threads) is 0
  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.previous_threads, headers.auth_summary.dmarc.pass, sender.email.domain.root_domain, subject.is_forward, type.inbound. Sensors: beta.ocr, file.message_screenshot, ml.nlu_classifier, strings.icontains. Reference lists: $high_trust_sender_root_domains.

Indicators matched (3)

FieldMatchValue
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics[].nameequalsFinancial Communications
ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics[].nameequalsAdvertising and Promotions
strings.icontainssubstringmastercard

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"
  not
    beta.ocr(file.message_screenshot()).text contains "paying with mastercard"
  any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics)
    ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics.name eq "Advertising and Promotions"
  any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics)
    ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).topics.name eq "Financial Communications"
  not
    subject.is_forward eq "true"
  attachments length_compare "0"
  beta.ocr(file.message_screenshot()).text contains "mastercard"
  body.previous_threads length_compare "0"
  type.inbound eq "true"
   macro "(length(beta.ocr(file.message_screenshot()).text) / length(body.current_thread.text)) > 10"

Exclusions

The rule actively suppresses these predicates.

Indicators

These rows show field, operator, and value matches.

Spam: New job cold outreach from unsolicited sender

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

Detects unsolicited messages congratulating recipients on new jobs or roles that contain unsubscribe links, calendar booking links, or exhibit B2B cold outreach characteristics from senders who have not been previously contacted.

Threat classification

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

CategoryValues
Attack typesSpam

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// direct outreach to sender
and length(recipients.to) == 1
// talking about new job
and any([body.current_thread.text, subject.subject],
        regex.icontains(., 'congrat(?:ulation)?s on the new (?:job|gig|role)')
)
// body link to unsubscribe or to book time, otherwise use NLU on the body current thread
and (
  any(body.links,
      strings.icontains(.href_url.url, "unsubscribe")
      or strings.icontains(.href_url.url, "opt-out")
      or strings.icontains(.href_url.url, "preferences")
      or strings.icontains(.href_url.url, "calendar")
  )
  or any(ml.nlu_classifier(body.current_thread.text).topics,
         .name == "B2B Cold Outreach" and .confidence != "low"
  )
)
// not someone they have reached out to before
and not profile.by_sender().solicited

Detection logic

Scope: inbound message.

Detects unsolicited messages congratulating recipients on new jobs or roles that contain unsubscribe links, calendar booking links, or exhibit B2B cold outreach characteristics from senders who have not been previously contacted.

  1. inbound message
  2. length(recipients.to) is 1
  3. any of [body.current_thread.text, subject.subject] where:
    • . matches 'congrat(?:ulation)?s on the new (?:job|gig|role)'
  4. any of:
    • any of body.links where any holds:
      • .href_url.url contains 'unsubscribe'
      • .href_url.url contains 'opt-out'
      • .href_url.url contains 'preferences'
      • .href_url.url contains 'calendar'
    • any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
      • .name is 'B2B Cold Outreach'
      • .confidence is not 'low'
  5. not:
    • profile.by_sender().solicited

Inspects: body.current_thread.text, body.links, body.links[].href_url.url, recipients.to, subject.subject, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, regex.icontains, strings.icontains.

Indicators matched (6)

FieldMatchValue
regex.icontainsregexcongrat(?:ulation)?s on the new (?:job|gig|role)
strings.icontainssubstringunsubscribe
strings.icontainssubstringopt-out
strings.icontainssubstringpreferences
strings.icontainssubstringcalendar
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsB2B Cold Outreach

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(body.links)
      or
        body.links.href_url.url contains "calendar"
        body.links.href_url.url contains "opt-out"
        body.links.href_url.url contains "preferences"
        body.links.href_url.url contains "unsubscribe"
    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"
  any([body.current_thread.text, subject.subject])
    [body.current_thread.text, subject.subject] regex_match "congrat(?:ulation)?s on the new (?:job|gig|role)"
  not
    profile.by_sender func_call "profile.by_sender().solicited"
  recipients.to length_compare "1"
  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"

Spam: New link domain (<=10d) and emojis

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

Detects spam from freemail senders, where the linked domain is less than 10 days old and emojis present.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesFree email provider

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.links
  • body.plain
  • sender.email
  • subject
  • type

Rule body

type.inbound

// sender is a freemail
and sender.email.domain.root_domain in $free_email_providers

// linked domain is less than 10 days old
and any(body.links, network.whois(.href_url.domain).days_old < 10)

// has an emoji in the subject or body
and (
  regex.contains(body.plain.raw,
                 '[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]'
  )
  or regex.contains(subject.subject,
                    '[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]'
  )
)
and (
  profile.by_sender().prevalence in ("new", "outlier")
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)

Detection logic

Scope: inbound message.

Detects spam from freemail senders, where the linked domain is less than 10 days old and emojis present.

  1. inbound message
  2. sender.email.domain.root_domain in $free_email_providers
  3. any of body.links where:
    • network.whois(.href_url.domain).days_old < 10
  4. any of:
    • body.plain.raw matches '[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}]'
    • subject.subject matches '[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}]'
  5. any of:
    • profile.by_sender().prevalence in ('new', 'outlier')
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign

Inspects: body.links, body.links[].href_url.domain, body.plain.raw, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: network.whois, profile.by_sender, regex.contains. Reference lists: $free_email_providers.

Indicators matched (1)

FieldMatchValue
regex.containsregex[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]

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"
    profile.by_sender func_call "profile.by_sender().prevalence in (new, outlier)"
  or
    body.plain.raw regex_match "[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}]"
    subject.subject regex_match "[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}]"
  any(body.links)
    network.whois func_call "network.whois(body.links[].href_url.domain).days_old < 10"
  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.plain.rawregex_match
  • [\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]
field:"body.plain.raw" kind:regex_match value:"[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]"
subject.subjectregex_match
  • [\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]
field:"subject.subject" kind:regex_match value:"[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: Personalized subject and greetings via Salesforce Marketing Cloud

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

Detects messages sent through Salesforce Marketing Cloud infrastructure that contain a fake previous email thread, where both the current and previous threads start with the same greeting pattern extracted from the subject line.

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
  • body.previous_threads
  • headers
  • headers.domains[0]
  • headers.hops
  • subject
  • type

Rule body

type.inbound
// attempt to find SF sending infra 
and (
  headers.domains[0].root_domain == "exacttarget.com"
  or strings.iends_with(headers.message_id, '.xt.local>')
  or any(headers.hops,
         any(.fields,
             .name =~ "X-SFMC-Stack"
             or (.name =~ "x-job" and regex.match(.value, '^\d+_\d+$'))
         )
  )
)
// the message contains a fake previous thread
and length(body.previous_threads) == 1

// extract the name from the subject
and any(regex.iextract(subject.base, '(?:^|: )(?P<first_name>[A-Z][a-z]+)$'),
        // the current thread starts with "Hi <extracted from subject>
        strings.istarts_with(body.current_thread.text,
                             strings.concat('Hi ', .named_groups["first_name"])
        )
        // the previous thread starts with "Hi <extracted from subject>
        and any(body.previous_threads,
                strings.istarts_with(.text,
                                     strings.concat('Hi ',
                                                    ..named_groups["first_name"]
                                     )
                )
        )
)

Detection logic

Scope: inbound message.

Detects messages sent through Salesforce Marketing Cloud infrastructure that contain a fake previous email thread, where both the current and previous threads start with the same greeting pattern extracted from the subject line.

  1. inbound message
  2. any of:
    • headers.domains[0].root_domain is 'exacttarget.com'
    • headers.message_id ends with '.xt.local>'
    • any of headers.hops where:
      • any of .fields where any holds:
        • .name is 'X-SFMC-Stack'
        • all of:
          • .name is 'x-job'
          • .value matches '^\\d+_\\d+$'
  3. length(body.previous_threads) is 1
  4. any of regex.iextract(subject.base) where all hold:
    • strings.istarts_with(body.current_thread.text)
    • any of body.previous_threads where:
      • strings.istarts_with(.text)

Inspects: body.current_thread.text, body.previous_threads, body.previous_threads[].text, headers.domains[0].root_domain, headers.hops, headers.hops[].fields, headers.hops[].fields[].name, headers.hops[].fields[].value, headers.message_id, subject.base, type.inbound. Sensors: regex.iextract, regex.match, strings.concat, strings.iends_with, strings.istarts_with.

Indicators matched (6)

FieldMatchValue
headers.domains[0].root_domainequalsexacttarget.com
strings.iends_withsuffix.xt.local>
headers.hops[].fields[].nameequalsX-SFMC-Stack
headers.hops[].fields[].nameequalsx-job
regex.matchregex^\d+_\d+$
regex.iextractregex(?:^|: )(?P<first_name>[A-Z][a-z]+)$

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(headers.hops)
      any(headers.hops.fields)
        or
          and
            headers.hops.fields[].name eq "x-job"
            headers.hops.fields[].value regex_match "^\\d+_\\d+$"
          headers.hops.fields[].name eq "X-SFMC-Stack"
    headers.domains[0].root_domain eq "exacttarget.com"
    headers.message_id ends_with ".xt.local>"
  any(regex.iextract(subject.base))
    and
      any(body.previous_threads)
        strings.istarts_with func_call "strings.istarts_with(body.previous_threads[].text)"
      strings.istarts_with func_call "strings.istarts_with(body.current_thread.text)"
  body.previous_threads length_compare "1"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Spam: Sendersrv.com with financial communications and unsubscribe language

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

Detects messages from sendersrv.com infrastructure containing unsubscribe language and financial communication topics, indicating potential abuse of the bulk email service for unauthorized financial solicitations.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • headers
  • headers.domains
  • type

Rule body

type.inbound
and any(headers.domains, .root_domain == "sendersrv.com")
and strings.icontains(body.current_thread.text, 'unsubscribe')
and any(ml.nlu_classifier(body.current_thread.text).topics,
        .name == "Financial Communications" and .confidence != "low"
)

Detection logic

Scope: inbound message.

Detects messages from sendersrv.com infrastructure containing unsubscribe language and financial communication topics, indicating potential abuse of the bulk email service for unauthorized financial solicitations.

  1. inbound message
  2. any of headers.domains where:
    • .root_domain is 'sendersrv.com'
  3. body.current_thread.text contains 'unsubscribe'
  4. any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
    • .name is 'Financial Communications'
    • .confidence is not 'low'

Inspects: body.current_thread.text, headers.domains, headers.domains[].root_domain, type.inbound. Sensors: ml.nlu_classifier, strings.icontains.

Indicators matched (3)

FieldMatchValue
headers.domains[].root_domainequalssendersrv.com
strings.icontainssubstringunsubscribe
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsFinancial Communications

Stages and Predicates

Stage 1: mql_rule

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 "Financial Communications"
  any(headers.domains)
    headers.domains.root_domain eq "sendersrv.com"
  body.current_thread.text contains "unsubscribe"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Spam: Sexually explicit content with emoji in subject from freemail provider

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

Detects messages from free email providers that contain sexually explicit content and include emojis in the subject line.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesFree email provider, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// sender is a freemail domain
and sender.email.domain.root_domain in $free_email_providers
// look for commonly used emojis in sexually explicit messages
and regex.icontains(subject.subject,
                    '(\x{1F346}|\x{1F608}|\x{1F609}|\x{1F351}|\x{2764}|\x{1F60D}|\x{1F618}|\x{1F48B}|\x{1F63B}|\x{1F445}|\x{1F51E}|\x{1F525}|\x{1F4F7})'
)
and any(ml.nlu_classifier(body.current_thread.text).topics,
        .name == 'Sexually Explicit Messages'
)

Detection logic

Scope: inbound message.

Detects messages from free email providers that contain sexually explicit content and include emojis in the subject line.

  1. inbound message
  2. sender.email.domain.root_domain in $free_email_providers
  3. subject.subject matches '(\\x{1F346}|\\x{1F608}|\\x{1F609}|\\x{1F351}|\\x{2764}|\\x{1F60D}|\\x{1F618}|\\x{1F48B}|\\x{1F63B}|\\x{1F445}|\\x{1F51E}|\\x{1F525}|\\x{1F4F7})'
  4. any of ml.nlu_classifier(body.current_thread.text).topics where:
    • .name is 'Sexually Explicit Messages'

Inspects: body.current_thread.text, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: ml.nlu_classifier, regex.icontains. Reference lists: $free_email_providers.

Indicators matched (2)

FieldMatchValue
regex.icontainsregex(\x{1F346}|\x{1F608}|\x{1F609}|\x{1F351}|\x{2764}|\x{1F60D}|\x{1F618}|\x{1F48B}|\x{1F63B}|\x{1F445}|\x{1F51E}|\x{1F525}|\x{1F4F7})
ml.nlu_classifier(body.current_thread.text).topics[].nameequalsSexually Explicit Messages

Stages and Predicates

Stage 1: mql_rule

and
  any(ml.nlu_classifier(body.current_thread.text).topics)
    ml.nlu_classifier(body.current_thread.text).topics.name eq "Sexually Explicit Messages"
  subject.subject regex_match "(\\x{1F346}|\\x{1F608}|\\x{1F609}|\\x{1F351}|\\x{2764}|\\x{1F60D}|\\x{1F618}|\\x{1F48B}|\\x{1F63B}|\\x{1F445}|\\x{1F51E}|\\x{1F525}|\\x{1F4F7})"
  type.inbound eq "true"
   macro "sender.email.domain.root_domain in free_email_providers"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
subject.subjectregex_match
  • (\x{1F346}|\x{1F608}|\x{1F609}|\x{1F351}|\x{2764}|\x{1F60D}|\x{1F618}|\x{1F48B}|\x{1F63B}|\x{1F445}|\x{1F51E}|\x{1F525}|\x{1F4F7})
field:"subject.subject" kind:regex_match value:"(\x{1F346}|\x{1F608}|\x{1F609}|\x{1F351}|\x{2764}|\x{1F60D}|\x{1F618}|\x{1F48B}|\x{1F63B}|\x{1F445}|\x{1F51E}|\x{1F525}|\x{1F4F7})"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: Sexually explicit Google Drive share

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

Detects suspicious Google Drive Share which containing inappropriate content or suspicious patterns. The rule looks for reports from non-organizational domains that contain emojis or explicit keywords within the report.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesSocial engineering, Free email provider

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// 
//  Warning: This rule contains sexually explicit keywords
// 
and sender.email.email == "drive-shares-noreply@google.com"
// the invite is not from an $org_domain user
and all(headers.reply_to,
        .email.domain.domain not in $org_domains
        and .email.email not in $recipient_emails
        and .email.email not in $sender_emails
)
// the subject or the body contain sexually explicit keywords
and any([subject.subject, body.current_thread.text],
        // this regex should be kept in sync between the Google Group and the Looker Studio rules
        regex.icontains(.,
                        '(?:sex|horny|cock|fuck|\bass\b|pussy|dick|tits|\bcum\b|girlfriend|boyfriend|naked|porn|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|\bxxx\b|nudes?|sexting|cheating|affair|erotic|\blust\b|desire|intimate|explicit|fetish|kinky|seduce|adult\s*(?:\w+\s+){0,2}\s*community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)'
        )
)

Detection logic

Scope: inbound message.

Detects suspicious Google Drive Share which containing inappropriate content or suspicious patterns. The rule looks for reports from non-organizational domains that contain emojis or explicit keywords within the report.

  1. inbound message
  2. sender.email.email is 'drive-shares-noreply@google.com'
  3. all of headers.reply_to where all hold:
    • .email.domain.domain not in $org_domains
    • .email.email not in $recipient_emails
    • .email.email not in $sender_emails
  4. any of [subject.subject, body.current_thread.text] where:
    • . matches '(?:sex|horny|cock|fuck|\\bass\\b|pussy|dick|tits|\\bcum\\b|girlfriend|boyfriend|naked|porn|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|\\bxxx\\b|nudes?|sexting|cheating|affair|erotic|\\blust\\b|desire|intimate|explicit|fetish|kinky|seduce|adult\\s*(?:\\w+\\s+){0,2}\\s*community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)'

Inspects: body.current_thread.text, headers.reply_to, headers.reply_to[].email.domain.domain, headers.reply_to[].email.email, sender.email.email, subject.subject, type.inbound. Sensors: regex.icontains. Reference lists: $org_domains, $recipient_emails, $sender_emails.

Indicators matched (2)

FieldMatchValue
sender.email.emailequalsdrive-shares-noreply@google.com
regex.icontainsregex(?:sex|horny|cock|fuck|\bass\b|pussy|dick|tits|\bcum\b|girlfriend|boyfriend|naked|porn|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|\bxxx\b|nudes?|sexting|cheating|affair|erotic|\blust\b|desire|intimate|explicit|fetish|kinky|seduce|adult\s*(?:\w+\s+){0,2}\s*community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)

Stages and Predicates

Stage 1: mql_rule

and
  any([subject.subject, body.current_thread.text])
    [subject.subject, body.current_thread.text] regex_match "(?:sex|horny|cock|fuck|\\bass\\b|pussy|dick|tits|\\bcum\\b|girlfriend|boyfriend|naked|porn|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|\\bxxx\\b|nudes?|sexting|cheating|affair|erotic|\\blust\\b|desire|intimate|explicit|fetish|kinky|seduce|adult\\s*(?:\\w+\\s+){0,2}\\s*community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)"
  sender.email.email eq "drive-shares-noreply@google.com"
  type.inbound eq "true"
   macro "all(headers.reply_to)"

Indicators

These rows show field, operator, and value matches.

Spam: Sexually explicit Google group invitation

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

Detects suspicious Google Groups invitations containing inappropriate content or suspicious patterns. The rule looks for invites from non-organizational domains that contain random alphanumeric strings, explicit keywords, or suspicious call-to-action phrases in the group names or descriptions.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesFree email provider, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// 
//  Warning: This rule contains sexually explicit keywords
// 
and sender.email.email == "noreply@groups.google.com"
and (
  strings.istarts_with(subject.base, 'Invitation to join ')
  or strings.istarts_with(subject.base, 'You have been added to ')
  // the group name contains sexually explicit keywords
  // this regex should be kept in sync between the Google Group, Google Drive Share, and Looker Studio rules
  or regex.icontains(subject.base,
                     '(?:Invitation to join|You have been added to) .*(?:sex|horny|cock|fuck|\bass\b|pussy|dick|tits|cum|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|\blust\b|desire|intimate|explicit|fetish|kinky|seduce|adult community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)'
  )
)
// the invite is not from an $org_domain user
and not any($org_domains,
            strings.icontains(body.current_thread.text,
                              strings.concat('@',
                                             .,
                                             ' invited you to join the '
                              )
            )
            or strings.icontains(body.current_thread.text,
                                 strings.concat('@', ., ' added you to the ')
            )
)
and (
  // the group name contains 7 char sets at the start and end and must contain a number
  regex.icontains(subject.base,
                  '(?:added to|to join) [A-Z0-9]{5,7} .*(?:[[:^ascii:]]|[[:^alpha:]]) [A-Z0-9]{5,7}$'
  )
  // calls to action in the group name
  or regex.icontains(subject.base,
                     '(?:added to|to join) .*(join|(?:click|go|tap) here)'
  )
  // it contains an emoji in the group name
  or regex.icontains(subject.base,
                     '(?:added to|to join) .*[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]'
  )
  // the description of the group contains sexually explicit keywords
  // this regex should be kept in sync between the Google Group and the Looker Studio rules
  or regex.icontains(body.current_thread.text,
                     '(?:about this group|message from).*(?:sex|horny|cock|fuck|\bass\b|pussy|dick|tits|cum|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|lust|desire|intimate|explicit|fetish|kinky|seduce|adult community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner).*https?://'
  )
  // the invitor is an email domain which contains 3 labels
  or (
    regex.icontains(body.current_thread.text,
                    '\n[^\@]+@(?:[a-zA-Z0-9-]{1,255}\.){2}[a-zA-Z0-9-]{2,255} (?:added you to the|invited you to join the)'
    )
    // where the group name contains "lists."
    and not regex.icontains(body.current_thread.text,
                            '\n[^\@]+@(?:[a-zA-Z0-9-]{1,255}\.){2}[a-zA-Z0-9-]{2,255} (?:added you to the|invited you to join the) [^\@]+\@lists\.'
    )
  )
)

Detection logic

Scope: inbound message.

Detects suspicious Google Groups invitations containing inappropriate content or suspicious patterns. The rule looks for invites from non-organizational domains that contain random alphanumeric strings, explicit keywords, or suspicious call-to-action phrases in the group names or descriptions.

  1. inbound message
  2. sender.email.email is 'noreply@groups.google.com'
  3. any of:
    • subject.base starts with 'Invitation to join '
    • subject.base starts with 'You have been added to '
    • subject.base matches '(?:Invitation to join|You have been added to) .*(?:sex|horny|cock|fuck|\\bass\\b|pussy|dick|tits|cum|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|\\blust\\b|desire|intimate|explicit|fetish|kinky|seduce|adult community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)'
  4. not:
    • any of $org_domains where any holds:
      • strings.icontains(body.current_thread.text)
      • strings.icontains(body.current_thread.text)
  5. any of:
    • subject.base matches '(?:added to|to join) [A-Z0-9]{5,7} .*(?:[[:^ascii:]]|[[:^alpha:]]) [A-Z0-9]{5,7}$'
    • subject.base matches '(?:added to|to join) .*(join|(?:click|go|tap) here)'
    • subject.base matches '(?:added to|to join) .*[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}]'
    • body.current_thread.text matches '(?:about this group|message from).*(?:sex|horny|cock|fuck|\\bass\\b|pussy|dick|tits|cum|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|lust|desire|intimate|explicit|fetish|kinky|seduce|adult community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner).*https?://'
    • all of:
      • body.current_thread.text matches '\\n[^\\@]+@(?:[a-zA-Z0-9-]{1,255}\\.){2}[a-zA-Z0-9-]{2,255} (?:added you to the|invited you to join the)'
      • not:
        • body.current_thread.text matches '\\n[^\\@]+@(?:[a-zA-Z0-9-]{1,255}\\.){2}[a-zA-Z0-9-]{2,255} (?:added you to the|invited you to join the) [^\\@]+\\@lists\\.'

Inspects: body.current_thread.text, sender.email.email, subject.base, type.inbound. Sensors: regex.icontains, strings.concat, strings.icontains, strings.istarts_with. Reference lists: $org_domains.

Indicators matched (9)

FieldMatchValue
sender.email.emailequalsnoreply@groups.google.com
strings.istarts_withprefixInvitation to join
strings.istarts_withprefixYou have been added to
regex.icontainsregex(?:Invitation to join|You have been added to) .*(?:sex|horny|cock|fuck|\bass\b|pussy|dick|tits|cum|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|\blust\b|desire|intimate|explicit|fetish|kinky|seduce|adult community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)
regex.icontainsregex(?:added to|to join) [A-Z0-9]{5,7} .*(?:[[:^ascii:]]|[[:^alpha:]]) [A-Z0-9]{5,7}$
regex.icontainsregex(?:added to|to join) .*(join|(?:click|go|tap) here)
regex.icontainsregex(?:added to|to join) .*[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]
regex.icontainsregex(?:about this group|message from).*(?:sex|horny|cock|fuck|\bass\b|pussy|dick|tits|cum|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|lust|desire|intimate|explicit|fetish|kinky|seduce|adult community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner).*https?://
regex.icontainsregex\n[^\@]+@(?:[a-zA-Z0-9-]{1,255}\.){2}[a-zA-Z0-9-]{2,255} (?:added you to the|invited you to join the)

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      not
        body.current_thread.text regex_match "\\n[^\\@]+@(?:[a-zA-Z0-9-]{1,255}\\.){2}[a-zA-Z0-9-]{2,255} (?:added you to the|invited you to join the) [^\\@]+\\@lists\\."
      body.current_thread.text regex_match "\\n[^\\@]+@(?:[a-zA-Z0-9-]{1,255}\\.){2}[a-zA-Z0-9-]{2,255} (?:added you to the|invited you to join the)"
    body.current_thread.text regex_match "(?:about this group|message from).*(?:sex|horny|cock|fuck|\\bass\\b|pussy|dick|tits|cum|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|lust|desire|intimate|explicit|fetish|kinky|seduce|adult community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner).*https?://"
    subject.base regex_match "(?:added to|to join) .*(join|(?:click|go|tap) here)"
    subject.base regex_match "(?:added to|to join) .*[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}]"
    subject.base regex_match "(?:added to|to join) [A-Z0-9]{5,7} .*(?:[[:^ascii:]]|[[:^alpha:]]) [A-Z0-9]{5,7}$"
  not
    any($org_domains)
      strings.icontains func_call "strings.icontains(body.current_thread.text)"
  or
    subject.base regex_match "(?:Invitation to join|You have been added to) .*(?:sex|horny|cock|fuck|\\bass\\b|pussy|dick|tits|cum|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|\\blust\\b|desire|intimate|explicit|fetish|kinky|seduce|adult community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)"
    subject.base starts_with "Invitation to join "
    subject.base starts_with "You have been added to "
  sender.email.email eq "noreply@groups.google.com"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
$org_domainsarray_any(no value, null check)excludes:$org_domains

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • (?:about this group|message from).*(?:sex|horny|cock|fuck|\bass\b|pussy|dick|tits|cum|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|lust|desire|intimate|explicit|fetish|kinky|seduce|adult community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner).*https?://
  • \n[^\@]+@(?:[a-zA-Z0-9-]{1,255}.){2}[a-zA-Z0-9-]{2,255} (?:added you to the|invited you to join the)
field:"body.current_thread.text" kind:regex_match
sender.email.emaileq
  • noreply@groups.google.com
field:"sender.email.email" kind:eq value:"noreply@groups.google.com"
subject.baseregex_match
  • (?:Invitation to join|You have been added to) .*(?:sex|horny|cock|fuck|\bass\b|pussy|dick|tits|cum|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|\blust\b|desire|intimate|explicit|fetish|kinky|seduce|adult community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)
  • (?:added to|to join) .*(join|(?:click|go|tap) here)
  • (?:added to|to join) .*[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]
  • (?:added to|to join) [A-Z0-9]{5,7} .*(?:[[:^ascii:]]|[[:^alpha:]]) [A-Z0-9]{5,7}$
field:"subject.base" kind:regex_match
subject.basestarts_with
  • Invitation to join
  • You have been added to
field:"subject.base" kind:starts_with
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: Sexually explicit Looker Studio report

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

Detects suspicious Looker Studio Reports which containing inappropriate content or suspicious patterns. The rule looks for reports from non-organizational domains that contain emojis or explicit keywords within the report.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesSocial engineering, Free email provider

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// 
//  Warning: This rule contains sexually explicit keywords
// 
and sender.email.email == "looker-studio-noreply@google.com"
// the invite is not from an $org_domain user
and all(headers.reply_to,
        .email.domain.domain not in $org_domains
        and .email.email not in $recipient_emails
        and .email.email not in $sender_emails
)
// the subject or the body contain sexually explicit keywords
and any([subject.subject, body.current_thread.text],
        // this regex should be kept in sync between the Google Group, Google Drive Share, and Looker Studio rules
        regex.icontains(.,
                        '(?:sex|horny|cock|fuck|\bass\b|pussy|dick|tits|cum\b|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|\blust\b|desire|intimate|explicit|fetish|kinky|seduce|adult\s*(?:\w+\s+){0,2}\s*community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)'
        )
)

Detection logic

Scope: inbound message.

Detects suspicious Looker Studio Reports which containing inappropriate content or suspicious patterns. The rule looks for reports from non-organizational domains that contain emojis or explicit keywords within the report.

  1. inbound message
  2. sender.email.email is 'looker-studio-noreply@google.com'
  3. all of headers.reply_to where all hold:
    • .email.domain.domain not in $org_domains
    • .email.email not in $recipient_emails
    • .email.email not in $sender_emails
  4. any of [subject.subject, body.current_thread.text] where:
    • . matches '(?:sex|horny|cock|fuck|\\bass\\b|pussy|dick|tits|cum\\b|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|\\blust\\b|desire|intimate|explicit|fetish|kinky|seduce|adult\\s*(?:\\w+\\s+){0,2}\\s*community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)'

Inspects: body.current_thread.text, headers.reply_to, headers.reply_to[].email.domain.domain, headers.reply_to[].email.email, sender.email.email, subject.subject, type.inbound. Sensors: regex.icontains. Reference lists: $org_domains, $recipient_emails, $sender_emails.

Indicators matched (2)

FieldMatchValue
sender.email.emailequalslooker-studio-noreply@google.com
regex.icontainsregex(?:sex|horny|cock|fuck|\bass\b|pussy|dick|tits|cum\b|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|\blust\b|desire|intimate|explicit|fetish|kinky|seduce|adult\s*(?:\w+\s+){0,2}\s*community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)

Stages and Predicates

Stage 1: mql_rule

and
  any([subject.subject, body.current_thread.text])
    [subject.subject, body.current_thread.text] regex_match "(?:sex|horny|cock|fuck|\\bass\\b|pussy|dick|tits|cum\\b|girlfriend|boyfriend|naked|porn|video|webcam|masturbate|orgasm|breasts|penis|vagina|strip|suck|blowjob|hardcore|xxx|nudes?|sexting|cheating|affair|erotic|\\blust\\b|desire|intimate|explicit|fetish|kinky|seduce|adult\\s*(?:\\w+\\s+){0,2}\\s*community|cam shows|local (?:girls?|women|single)|hook.?up|bed partner)"
  sender.email.email eq "looker-studio-noreply@google.com"
  type.inbound eq "true"
   macro "all(headers.reply_to)"

Indicators

These rows show field, operator, and value matches.

Spam: Single recipient duplicated in cc

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

Detects spam emails where the 'To' and 'CC' fields match, using indicators such as short body length with spam keywords, unsolicited content, dmarc failures, fake threads, and suspicious links.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesImpersonation: Brand, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound

// one recipient and 1 cc
and length(recipients.to) == 1
and length(recipients.cc) == 1

// unsolicited
and not profile.by_sender().solicited

// recipients email matches the cc email
and any(recipients.to, any(recipients.cc, .email.email == ..email.email))

// body is short with spam keywords
and (
  (
    length(body.current_thread.text) < 150
    and strings.ilike(body.current_thread.text,
                      "*congrat*",
                      "*win*",
                      "*expired*",
                      "*subscription*",
                      "*won*",
                      "*gift*",
                      "*CARTE CADEAU*",
                      "*Votre chance*",
                      "*survey*",
                      "*livraison*",
                      "*delivery*",
                      "*package*"
    )
  )

  // body is super short
  or length(body.current_thread.text) < 10

  // body has no spaces
  or regex.imatch(body.current_thread.text, '[^ ]+')

  // subject is null
  or subject.subject == ""

  // dmarc failure
  or not headers.auth_summary.dmarc.pass

  // or display text contains suspicious terms
  or any(body.links,
         regex.icontains(.display_text,
                         'Congrat|Survey|package|delivery|\bclaim\b'
         )
         and not .href_url.domain.root_domain == "surveymonkey.com"
  )

  // compauth failure
  or any(headers.hops,
         .authentication_results.compauth.verdict not in (
           "pass",
           "softpass",
           "none"
         )
  )

  // all links display text is null or aka.ms
  or (
    length(filter(body.links,
                  (
                    (
                      .display_text is null
                      and .href_url.domain.root_domain != sender.email.domain.root_domain
                    )
                    or .href_url.domain.root_domain in (
                      "aka.ms",
                      "mimecast.com",
                      "mimecastprotect.com",
                      "cisco.com"
                    )
                  )
           )
    ) == length(body.links)
  )
  or (
    // fake thread check
    regex.imatch(subject.subject, '(\[[^\]]+\]\s?){0,3}(re|fwd?)\s?:.*')
    and (
      (length(headers.references) == 0 and headers.in_reply_to is null)
      or headers.in_reply_to is null
    )
  )
)

Detection logic

Scope: inbound message.

Detects spam emails where the 'To' and 'CC' fields match, using indicators such as short body length with spam keywords, unsolicited content, dmarc failures, fake threads, and suspicious links.

  1. inbound message
  2. length(recipients.to) is 1
  3. length(recipients.cc) is 1
  4. not:
    • profile.by_sender().solicited
  5. any of recipients.to where:
    • any of recipients.cc where:
      • .email.email is .email.email
  6. any of:
    • all of:
      • length(body.current_thread.text) < 150
      • body.current_thread.text matches any of 12 patterns
        • *congrat*
        • *win*
        • *expired*
        • *subscription*
        • *won*
        • *gift*
        • *CARTE CADEAU*
        • *Votre chance*
        • *survey*
        • *livraison*
        • *delivery*
        • *package*
    • length(body.current_thread.text) < 10
    • body.current_thread.text matches '[^ ]+'
    • subject.subject is ''
    • not:
      • headers.auth_summary.dmarc.pass
    • any of body.links where all hold:
      • .display_text matches 'Congrat|Survey|package|delivery|\\bclaim\\b'
      • not:
        • .href_url.domain.root_domain is 'surveymonkey.com'
    • any of headers.hops where:
      • .authentication_results.compauth.verdict not in ('pass', 'softpass', 'none')
    • length(filter(body.links, .display_text is null and .href_url.domain.root_domain != sender.email.domain.root_domain or .href_url.domain.root_domain in ('aka.ms', 'mimecast.com', 'mimecastprotect.com', 'cisco.com'))) is length(body.links)
    • all of:
      • subject.subject matches '(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?)\\s?:.*'
      • any of:
        • all of:
          • length(headers.references) is 0
          • headers.in_reply_to is missing
        • headers.in_reply_to is missing

Inspects: body.current_thread.text, body.links, body.links[].display_text, body.links[].href_url.domain.root_domain, headers.auth_summary.dmarc.pass, headers.hops, headers.hops[].authentication_results.compauth.verdict, headers.in_reply_to, headers.references, recipients.cc, recipients.cc[].email.email, recipients.to, recipients.to[].email.email, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: profile.by_sender, regex.icontains, regex.imatch, strings.ilike.

Indicators matched (20)

FieldMatchValue
strings.ilikesubstring*congrat*
strings.ilikesubstring*win*
strings.ilikesubstring*expired*
strings.ilikesubstring*subscription*
strings.ilikesubstring*won*
strings.ilikesubstring*gift*
strings.ilikesubstring*CARTE CADEAU*
strings.ilikesubstring*Votre chance*
strings.ilikesubstring*survey*
strings.ilikesubstring*livraison*
strings.ilikesubstring*delivery*
strings.ilikesubstring*package*
8 more
regex.imatchregex[^ ]+
subject.subjectequals
regex.icontainsregexCongrat|Survey|package|delivery|\bclaim\b
body.links[].href_url.domain.root_domainmemberaka.ms
body.links[].href_url.domain.root_domainmembermimecast.com
body.links[].href_url.domain.root_domainmembermimecastprotect.com
body.links[].href_url.domain.root_domainmembercisco.com
regex.imatchregex(\[[^\]]+\]\s?){0,3}(re|fwd?)\s?:.*

Stages and Predicates

Stage 1: mql_rule

and
  or
    any(body.links)
      and
        not
          body.links.href_url.domain.root_domain eq "surveymonkey.com"
        body.links.display_text regex_match "Congrat|Survey|package|delivery|\\bclaim\\b"
    and
      or
        and
          headers.in_reply_to is_null
          headers.references length_compare "0"
        headers.in_reply_to is_null
      subject.subject regex_match "(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?)\\s?:.*"
    and
      or
        body.current_thread.text match "CARTE CADEAU"
        body.current_thread.text match "Votre chance"
        body.current_thread.text match "congrat"
        body.current_thread.text match "delivery"
        body.current_thread.text match "expired"
        body.current_thread.text match "gift"
        body.current_thread.text match "livraison"
        body.current_thread.text match "package"
        body.current_thread.text match "subscription"
        body.current_thread.text match "survey"
        body.current_thread.text match "win"
        body.current_thread.text match "won"
      body.current_thread.text length_compare "150"
    any(headers.hops)
      not
        headers.hops.authentication_results.compauth.verdict in ["none", "pass", "softpass"]
    not
      headers.auth_summary.dmarc.pass eq "true"
    body.current_thread.text length_compare "10"
    body.current_thread.text regex_match "[^ ]+"
    length func_call "length(filter(body.links, .display_text is null and .href_url.domain.root_domain != sender.email.domain.root_domain or .href_url.domain.root_domain in ('aka.ms', 'mimecast.com', 'mimecastprotect.com', 'cisco.com'))) == length(body.links)"
    subject.subject eq ""
  any(recipients.to)
    any(recipients.cc)
      recipients.cc.email.email cross_field_compare "recipients.to.email.email"
  not
    profile.by_sender func_call "profile.by_sender().solicited"
  recipients.cc length_compare "1"
  recipients.to length_compare "1"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textregex_match
  • [^ ]+
field:"body.current_thread.text" kind:regex_match value:"[^ ]+"
body.current_thread.textwildcard
  • *CARTE CADEAU*
  • *Votre chance*
  • *congrat*
  • *delivery*
  • *expired*
  • *gift*
  • *livraison*
  • *package*
  • *subscription*
  • *survey*
  • *win*
  • *won*
field:"body.current_thread.text" kind:wildcard
headers.in_reply_tois_null
  • (no value, null check)
field:"headers.in_reply_to" kind:is_null
subject.subjectregex_match
  • ([[^]]+]\s?){0,3}(re|fwd?)\s?:.*
field:"subject.subject" kind:regex_match value:"([[^]]+]\s?){0,3}(re|fwd?)\s?:.*"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: SMTP & Proxy Communications in Email Body

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

An email containing SMTP and Proxy (socks5) command and control information within the body of the message.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesFree email provider

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • type

Rule body

type.inbound
and length(attachments) == 0
and 1 < length(body.links) < 5
and regex.icontains(body.current_thread.text,
                    '(?:\:socks5:|::127.0.0.1:1080|:0:127.0.0.1:1080)'
)

Detection logic

Scope: inbound message.

An email containing SMTP and Proxy (socks5) command and control information within the body of the message.

  1. inbound message
  2. length(attachments) is 0
  3. all of:
    • length(body.links) > 1
    • length(body.links) < 5
  4. body.current_thread.text matches '(?:\\:socks5:|::127.0.0.1:1080|:0:127.0.0.1:1080)'

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

Indicators matched (1)

FieldMatchValue
regex.icontainsregex(?:\:socks5:|::127.0.0.1:1080|:0:127.0.0.1:1080)

Stages and Predicates

Stage 1: mql_rule

and
  attachments length_compare "0"
  body.current_thread.text regex_match "(?:\\:socks5:|::127.0.0.1:1080|:0:127.0.0.1:1080)"
  body.links length_compare "1"
  body.links length_compare "5"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

Spam: Unsolicited malformed PDF

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

This rule is designed to identify spam messages featuring a single malformed PDF attachment often leading to romance scam, pornographic, or dating websites. These emails typically contain short body text and intentionally distorted PDFs to avoid detection.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesEvasion, Free email provider, PDF

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// body text is very short
and length(body.current_thread.text) < 50

// one attachment
and length(attachments) == 1

// attachment is named a pdf, but mime type doesn't match with high entropy
and any(attachments,
        .file_type == "unknown"
        and strings.ends_with(.file_name, "pdf")
        and any(file.explode(.),
                .flavors.mime in (
                  "application/octet-stream",
                  "application/marc"
                )
                and .scan.entropy.entropy >= 6
        )
)
// a free email address was hyperlinked 
and any(body.links,
        .display_text is null and .href_url.url in $free_email_providers
)

// multiple freemail senders found in recipients.to
and length(filter(recipients.to,
                  .email.domain.root_domain in $free_email_providers
           )
) >= 3

// unsolicited and no false positives or previously flagged
and (
  profile.by_sender().prevalence in ("new", "outlier")
  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.

This rule is designed to identify spam messages featuring a single malformed PDF attachment often leading to romance scam, pornographic, or dating websites. These emails typically contain short body text and intentionally distorted PDFs to avoid detection.

  1. inbound message
  2. length(body.current_thread.text) < 50
  3. length(attachments) is 1
  4. any of attachments where all hold:
    • .file_type is 'unknown'
    • .file_name ends with 'pdf'
    • any of file.explode(.) where all hold:
      • .flavors.mime in ('application/octet-stream', 'application/marc')
      • .scan.entropy.entropy ≥ 6
  5. any of body.links where all hold:
    • .display_text is missing
    • .href_url.url in $free_email_providers
  6. length(filter(recipients.to, .email.domain.root_domain in $free_email_providers)) ≥ 3
  7. any of:
    • profile.by_sender().prevalence in ('new', 'outlier')
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
  8. not:
    • profile.by_sender().any_messages_benign

Inspects: attachments[].file_name, attachments[].file_type, body.current_thread.text, body.links, body.links[].display_text, body.links[].href_url.url, recipients.to, recipients.to[].email.domain.root_domain, type.inbound. Sensors: file.explode, profile.by_sender, strings.ends_with. Reference lists: $free_email_providers.

Indicators matched (4)

FieldMatchValue
attachments[].file_typeequalsunknown
strings.ends_withsuffixpdf
file.explode(attachments[])[].flavors.mimememberapplication/octet-stream
file.explode(attachments[])[].flavors.mimememberapplication/marc

Stages and Predicates

Stage 1: mql_rule

and
  any(attachments)
    and
      any(file.explode(attachments))
        and
          file.explode(attachments[])[].flavors.mime in ["application/marc", "application/octet-stream"]
          file.explode(attachments[])[].scan.entropy.entropy ge "6"
      attachments.file_name ends_with "pdf"
      attachments.file_type eq "unknown"
  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"
    profile.by_sender func_call "profile.by_sender().prevalence in (new, outlier)"
  any(body.links)
    and
      body.links.display_text is_null
       macro "body.links[].href_url.url in free_email_providers"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  attachments length_compare "1"
  body.current_thread.text length_compare "50"
  filter(recipients.to, .email.domain.root_domain in $free_email_providers) length_compare "3"
  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"

Spam: Unsolicited WordPress account creation or password reset request

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

Detects messages containing WordPress password reset links where the login parameter does not match the recipient's email address. The rule triggers for unsolicited senders, previously malicious senders, or when DMARC authentication fails.

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.html
  • body.links
  • headers
  • headers.auth_summary
  • recipients
  • recipients.to
  • type

Rule body

type.inbound
and body.html.raw is null
and any(body.links,
        .href_url.path == "/wp-login.php"
        and any(.href_url.query_params_decoded["key"], . is not null)
        and any(.href_url.query_params_decoded["login"],
                any(recipients.to,
                    .email.local_part != .. and .email.email != ..
                )
        )
)
and (
  (
    not profile.by_sender().solicited
    and profile.by_sender().prevalence == "new"
  )
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
  or not headers.auth_summary.dmarc.pass
)

Detection logic

Scope: inbound message.

Detects messages containing WordPress password reset links where the login parameter does not match the recipient's email address. The rule triggers for unsolicited senders, previously malicious senders, or when DMARC authentication fails.

  1. inbound message
  2. body.html.raw is missing
  3. any of body.links where all hold:
    • .href_url.path is '/wp-login.php'
    • any of .href_url.query_params_decoded['key'] where:
      • . is set
    • any of .href_url.query_params_decoded['login'] where:
      • any of recipients.to where all hold:
        • .email.local_part is not .
        • .email.email is not .
  4. any of:
    • all of:
      • not:
        • profile.by_sender().solicited
      • profile.by_sender().prevalence is 'new'
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign
    • not:
      • headers.auth_summary.dmarc.pass

Inspects: body.html.raw, body.links, body.links[].href_url.path, body.links[].href_url.query_params_decoded['key'], body.links[].href_url.query_params_decoded['login'], headers.auth_summary.dmarc.pass, recipients.to, recipients.to[].email.email, recipients.to[].email.local_part, type.inbound. Sensors: profile.by_sender.

Indicators matched (1)

FieldMatchValue
body.links[].href_url.pathequals/wp-login.php

Stages and Predicates

Stage 1: mql_rule

and
  any(body.links)
    and
      any(body.links.href_url.query_params_decoded['login'])
        any(recipients.to)
          and
            recipients.to.email.email cross_field_compare "body.links.href_url.query_params_decoded['login'][]"
            recipients.to.email.local_part cross_field_compare "body.links.href_url.query_params_decoded['login'][]"
      any(body.links.href_url.query_params_decoded['key'])
        body.links.href_url.query_params_decoded['key'] is_not_null
      body.links.href_url.path eq "/wp-login.php"
  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 == new"
    not
      headers.auth_summary.dmarc.pass eq "true"
  body.html.raw is_null
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.html.rawis_null
  • (no value, null check)
field:"body.html.raw" kind:is_null
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: URL shortener with short body content and emojis

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

Detects spam from freemail senders, where the majority of the body is a URL shortener and emojis.

Threat classification

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

CategoryValues
Attack typesSpam
Tactics and techniquesFree email provider

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.links
  • body.plain
  • sender.email
  • subject
  • type

Rule body

type.inbound

// sender is a freemail
and sender.email.domain.root_domain in $free_email_providers

// has a URL shortener
and any(body.links,
        .href_url.domain.root_domain in $url_shorteners
        or .href_url.domain.root_domain in $social_landing_hosts
)

// short body, basically just the URL
and length(body.plain.raw) < 100

// has an emoji in the subject or body
and (
  regex.contains(body.plain.raw,
                 '[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]'
  )
  or regex.contains(subject.subject,
                    '[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]'
  )
)
and (
  profile.by_sender().prevalence in ("new", "outlier")
  or (
    profile.by_sender().any_messages_malicious_or_spam
    and not profile.by_sender().any_messages_benign
  )
)

Detection logic

Scope: inbound message.

Detects spam from freemail senders, where the majority of the body is a URL shortener and emojis.

  1. inbound message
  2. sender.email.domain.root_domain in $free_email_providers
  3. any of body.links where any holds:
    • .href_url.domain.root_domain in $url_shorteners
    • .href_url.domain.root_domain in $social_landing_hosts
  4. length(body.plain.raw) < 100
  5. any of:
    • body.plain.raw matches '[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}]'
    • subject.subject matches '[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}]'
  6. any of:
    • profile.by_sender().prevalence in ('new', 'outlier')
    • all of:
      • profile.by_sender().any_messages_malicious_or_spam
      • not:
        • profile.by_sender().any_messages_benign

Inspects: body.links, body.links[].href_url.domain.root_domain, body.plain.raw, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: profile.by_sender, regex.contains. Reference lists: $free_email_providers, $social_landing_hosts, $url_shorteners.

Indicators matched (1)

FieldMatchValue
regex.containsregex[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]

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"
    profile.by_sender func_call "profile.by_sender().prevalence in (new, outlier)"
  any(body.links)
    or
       macro "body.links[].href_url.domain.root_domain in social_landing_hosts"
       macro "body.links[].href_url.domain.root_domain in url_shorteners"
  or
    body.plain.raw regex_match "[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}]"
    subject.subject regex_match "[\\x{1F300}-\\x{1F5FF}\\x{1F600}-\\x{1F64F}\\x{1F680}-\\x{1F6FF}\\x{1F700}-\\x{1F77F}\\x{1F780}-\\x{1F7FF}\\x{1F900}-\\x{1F9FF}\\x{2600}-\\x{26FF}\\x{2700}-\\x{27BF}\\x{2300}-\\x{23FF}]"
  body.plain.raw length_compare "100"
  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.plain.rawregex_match
  • [\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]
field:"body.plain.raw" kind:regex_match value:"[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]"
subject.subjectregex_match
  • [\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]
field:"subject.subject" kind:regex_match value:"[\x{1F300}-\x{1F5FF}\x{1F600}-\x{1F64F}\x{1F680}-\x{1F6FF}\x{1F700}-\x{1F77F}\x{1F780}-\x{1F7FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{2300}-\x{23FF}]"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Spam: Website errors solicitation

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

This rule detects messages claiming to have identified errors on a website. The messages typically offer to send pricing or information upon request.

Threat classification

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

CategoryValues
Attack typesSpam

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • body.html
  • body.links
  • body.previous_threads
  • subject
  • type

Rule body

type.inbound
and not profile.by_sender().solicited
// no attachments
and length(attachments) == 0
// subject must contain SEO or web dev spam keywords or be short
and (
  (
    // SEO or web development service keywords
    regex.icontains(strings.replace_confusables(subject.subject),
                    '(?:proposal|cost|estimate|error|bug|audit|screenshot|strategy|rankings|issues|fix|website|design|review|price)'
    )
    or regex.icontains(subject.base,
                       '[^\x{2600}-\x{27BF}\x{1F300}-\x{1F9FF}][\x{2600}-\x{27BF}\x{1F300}-\x{1F9FF}]\x{FE0F}?$'
    )
    // report and follow up keywords
    or (
      strings.icontains(strings.replace_confusables(subject.subject), "report")
      and regex.icontains(strings.replace_confusables(body.current_thread.text),
                          "(?:free|send you|can i send|may i send|let me know|interested|get back to me|reply back|just reply)"
      )
    )
    // short subject
    or length(subject.base) < 7
    or regex.icontains(subject.base, '[.?!-]{4,}')
  )
  // or a reply or forward in a thread that mentions website or screenshots
  or (
    (length(subject.base) < 5 or subject.is_reply or subject.is_forward)
    and any(body.previous_threads,
            regex.icontains(strings.replace_confusables(.text),
                            "(?:screenshot|website)"
            )
    )
  )
)
// body structure and content patterns
and (
  // Single thread with no links
  (
    length(filter(body.current_thread.links,
                  not (.href_url.scheme == "mailto" and .parser == "plain")
           )
    ) == 0
    and length(body.previous_threads) == 0
    // short message between 20 and 500 chars
    and (
      20 < length(body.current_thread.text) < 500
      or any(map(filter(ml.nlu_classifier(body.current_thread.text).entities,
                        .name == "disclaimer"
                 ),
                 .text
             ),
             20 < (length(body.current_thread.text) - length(.)) < 500
      )
    )
    // service offering keywords
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        '(?:screenshot|errors? (?:list|report)|plan|quote|rank|professional|price|mistake|visibility|improvement|review|emailed.{0,10}more details|(?:may|can|shall|should) i (?:email|send|share|forward|show)(?:\s+you)?.{0,15}(?:the|more|those|these|some)?\s*(?:details|info(?:rmation)?|report|screenshots?|list))'
    )
    // generic greeting
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        'h(?:i|ello|ey)\b'
    )
    // problem or urgency keywords
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        '(?:errors?|report|issues|website|repair|redesign|upgrade|Google\s+.{0,15}find it|glitch|send you|SEO|broken)'
    )
    // website or page mention
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        "(?:site|website|page|package|SEO)"
    )
  )
  // Single thread with unsubscribe link or $org_domains link
  or (
    length(body.links) <= 3
    and (
      // unsubscribe mailto link
      regex.icontains(body.html.raw, "mailto:*[++unsubscribe@]")
      // or link to found in org_domains
      or any(body.links, .href_url.domain.root_domain in~ $org_domains)
    )
    and length(body.previous_threads) == 0
    // short message between 20 and 500 chars
    and 20 < length(body.current_thread.text) < 500
    // service offering keywords
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        "(?:screenshot|error list|plan|quote|rank|professional|price|mistake)"
    )
    // generic greeting
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        '(?:h(?:i|ello|ey)|morning)\b'
    )
    // problem or urgency keywords
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        '(?:error|report|issues|website|repair|redesign|upgrade|Google\s+.{0,15}find it|send you|SEO)'
    )
    // website or page mention
    and regex.icontains(strings.replace_confusables(body.current_thread.text),
                        "(?:site|website|page|package|SEO)"
    )
  )
  // Multiple thread messages
  or (
    length(body.links) == 0
    // small thread with less than 5 messages
    and length(body.previous_threads) < 5
    // check previous messages for spam characteristics
    and any(body.previous_threads,
            // short previous messages less than 400 chars
            length(.text) < 400
            and (
              // generic greeting
              regex.icontains(strings.replace_confusables(.text),
                              '(?:h(?:i|ello|ey)|morning)\b'
              )
              // service offering keywords
              and regex.icontains(strings.replace_confusables(.text),
                                  '(?:\berror(?:\s+list)?\b|screenshot|report|plan)'
              )
              // previous threads written in English
              and ml.nlu_classifier(.text).language == "english"
            )
    )
  )
)

Detection logic

Scope: inbound message.

This rule detects messages claiming to have identified errors on a website. The messages typically offer to send pricing or information upon request.

  1. inbound message
  2. not:
    • profile.by_sender().solicited
  3. length(attachments) is 0
  4. any of:
    • any of:
      • strings.replace_confusables(subject.subject) matches '(?:proposal|cost|estimate|error|bug|audit|screenshot|strategy|rankings|issues|fix|website|design|review|price)'
      • subject.base matches '[^\\x{2600}-\\x{27BF}\\x{1F300}-\\x{1F9FF}][\\x{2600}-\\x{27BF}\\x{1F300}-\\x{1F9FF}]\\x{FE0F}?$'
      • all of:
        • strings.replace_confusables(subject.subject) contains 'report'
        • strings.replace_confusables(body.current_thread.text) matches '(?:free|send you|can i send|may i send|let me know|interested|get back to me|reply back|just reply)'
      • length(subject.base) < 7
      • subject.base matches '[.?!-]{4,}'
    • all of:
      • any of:
        • length(subject.base) < 5
        • subject.is_reply
        • subject.is_forward
      • any of body.previous_threads where:
        • strings.replace_confusables(.text) matches '(?:screenshot|website)'
  5. any of:
    • all of:
      • length(filter(body.current_thread.links, not .href_url.scheme == 'mailto' and .parser == 'plain')) is 0
      • length(body.previous_threads) is 0
      • any of:
        • all of:
          • length(body.current_thread.text) > 20
          • length(body.current_thread.text) < 500
        • any of map(...) where all hold:
          • 20 < length(body.current_thread.text) - length(.)
          • length(body.current_thread.text) - length(.) < 500
      • strings.replace_confusables(body.current_thread.text) matches '(?:screenshot|errors? (?:list|report)|plan|quote|rank|professional|price|mistake|visibility|improvement|review|emailed.{0,10}more details|(?:may|can|shall|should) i (?:email|send|share|forward|show)(?:\\s+you)?.{0,15}(?:the|more|those|these|some)?\\s*(?:details|info(?:rmation)?|report|screenshots?|list))'
      • strings.replace_confusables(body.current_thread.text) matches 'h(?:i|ello|ey)\\b'
      • strings.replace_confusables(body.current_thread.text) matches '(?:errors?|report|issues|website|repair|redesign|upgrade|Google\\s+.{0,15}find it|glitch|send you|SEO|broken)'
      • strings.replace_confusables(body.current_thread.text) matches '(?:site|website|page|package|SEO)'
    • all of:
      • length(body.links) ≤ 3
      • any of:
        • body.html.raw matches 'mailto:*[++unsubscribe@]'
        • any of body.links where:
          • .href_url.domain.root_domain in $org_domains
      • length(body.previous_threads) is 0
      • all of:
        • length(body.current_thread.text) > 20
        • length(body.current_thread.text) < 500
      • strings.replace_confusables(body.current_thread.text) matches '(?:screenshot|error list|plan|quote|rank|professional|price|mistake)'
      • strings.replace_confusables(body.current_thread.text) matches '(?:h(?:i|ello|ey)|morning)\\b'
      • strings.replace_confusables(body.current_thread.text) matches '(?:error|report|issues|website|repair|redesign|upgrade|Google\\s+.{0,15}find it|send you|SEO)'
      • strings.replace_confusables(body.current_thread.text) matches '(?:site|website|page|package|SEO)'
    • all of:
      • length(body.links) is 0
      • length(body.previous_threads) < 5
      • any of body.previous_threads where all hold:
        • length(.text) < 400
        • all of:
          • strings.replace_confusables(.text) matches '(?:h(?:i|ello|ey)|morning)\\b'
          • strings.replace_confusables(.text) matches '(?:\\berror(?:\\s+list)?\\b|screenshot|report|plan)'
          • ml.nlu_classifier(.text).language is 'english'

Inspects: body.current_thread.links, body.current_thread.links[].href_url.scheme, body.current_thread.links[].parser, body.current_thread.text, body.html.raw, body.links, body.links[].href_url.domain.root_domain, body.previous_threads, body.previous_threads[].text, subject.base, subject.is_forward, subject.is_reply, subject.subject, type.inbound. Sensors: ml.nlu_classifier, profile.by_sender, regex.icontains, strings.icontains, strings.replace_confusables. Reference lists: $org_domains.

Indicators matched (16)

FieldMatchValue
regex.icontainsregex(?:proposal|cost|estimate|error|bug|audit|screenshot|strategy|rankings|issues|fix|website|design|review|price)
regex.icontainsregex[^\x{2600}-\x{27BF}\x{1F300}-\x{1F9FF}][\x{2600}-\x{27BF}\x{1F300}-\x{1F9FF}]\x{FE0F}?$
strings.icontainssubstringreport
regex.icontainsregex(?:free|send you|can i send|may i send|let me know|interested|get back to me|reply back|just reply)
regex.icontainsregex[.?!-]{4,}
regex.icontainsregex(?:screenshot|website)
ml.nlu_classifier(body.current_thread.text).entities[].nameequalsdisclaimer
regex.icontainsregex(?:screenshot|errors? (?:list|report)|plan|quote|rank|professional|price|mistake|visibility|improvement|review|emailed.{0,10}more details|(?:may|can|shall|should) i (?:email|send|share|forward|show)(?:\s+you)?.{0,15}(?:the|more|those|these|some)?\s*(?:details|info(?:rmation)?|report|screenshots?|list))
regex.icontainsregexh(?:i|ello|ey)\b
regex.icontainsregex(?:errors?|report|issues|website|repair|redesign|upgrade|Google\s+.{0,15}find it|glitch|send you|SEO|broken)
regex.icontainsregex(?:site|website|page|package|SEO)
regex.icontainsregexmailto:*[++unsubscribe@]
4 more
regex.icontainsregex(?:screenshot|error list|plan|quote|rank|professional|price|mistake)
regex.icontainsregex(?:h(?:i|ello|ey)|morning)\b
regex.icontainsregex(?:error|report|issues|website|repair|redesign|upgrade|Google\s+.{0,15}find it|send you|SEO)
regex.icontainsregex(?:\berror(?:\s+list)?\b|screenshot|report|plan)

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        any(map(...))
          and
             macro "(length(body.current_thread.text) - length(map(...)[])) < 500"
             macro "20 < (length(body.current_thread.text) - length(map(...)[]))"
        and
          body.current_thread.text length_compare "20"
          body.current_thread.text length_compare "500"
      body.previous_threads length_compare "0"
      filter(body.current_thread.links, not .href_url.scheme == 'mailto' and .parser == 'plain') length_compare "0"
      strings.replace_confusables(body.current_thread.text) regex_match "(?:errors?|report|issues|website|repair|redesign|upgrade|Google\\s+.{0,15}find it|glitch|send you|SEO|broken)"
      strings.replace_confusables(body.current_thread.text) regex_match "(?:screenshot|errors? (?:list|report)|plan|quote|rank|professional|price|mistake|visibility|improvement|review|emailed.{0,10}more details|(?:may|can|shall|should) i (?:email|send|share|forward|show)(?:\\s+you)?.{0,15}(?:the|more|those|these|some)?\\s*(?:details|info(?:rmation)?|report|screenshots?|list))"
      strings.replace_confusables(body.current_thread.text) regex_match "(?:site|website|page|package|SEO)"
      strings.replace_confusables(body.current_thread.text) regex_match "h(?:i|ello|ey)\\b"
    and
      any(body.previous_threads)
        and
          body.previous_threads.text length_compare "400"
          ml.nlu_classifier func_call "ml.nlu_classifier(body.previous_threads[].text).language == english"
          strings.replace_confusables(body.previous_threads[].text) regex_match "(?:\\berror(?:\\s+list)?\\b|screenshot|report|plan)"
          strings.replace_confusables(body.previous_threads[].text) regex_match "(?:h(?:i|ello|ey)|morning)\\b"
      body.links length_compare "0"
      body.previous_threads length_compare "5"
    and
      or
        any(body.links)
           macro "body.links[].href_url.domain.root_domain in org_domains"
        body.html.raw regex_match "mailto:*[++unsubscribe@]"
      body.current_thread.text length_compare "20"
      body.current_thread.text length_compare "500"
      body.links length_compare "3"
      body.previous_threads length_compare "0"
      strings.replace_confusables(body.current_thread.text) regex_match "(?:error|report|issues|website|repair|redesign|upgrade|Google\\s+.{0,15}find it|send you|SEO)"
      strings.replace_confusables(body.current_thread.text) regex_match "(?:h(?:i|ello|ey)|morning)\\b"
      strings.replace_confusables(body.current_thread.text) regex_match "(?:screenshot|error list|plan|quote|rank|professional|price|mistake)"
      strings.replace_confusables(body.current_thread.text) regex_match "(?:site|website|page|package|SEO)"
  or
    and
      any(body.previous_threads)
        strings.replace_confusables(body.previous_threads[].text) regex_match "(?:screenshot|website)"
      or
        subject.base length_compare "5"
        subject.is_forward eq "true"
        subject.is_reply eq "true"
    and
      strings.replace_confusables(body.current_thread.text) regex_match "(?:free|send you|can i send|may i send|let me know|interested|get back to me|reply back|just reply)"
      strings.replace_confusables(subject.subject) contains "report"
    strings.replace_confusables(subject.subject) regex_match "(?:proposal|cost|estimate|error|bug|audit|screenshot|strategy|rankings|issues|fix|website|design|review|price)"
    subject.base length_compare "7"
    subject.base regex_match "[.?!-]{4,}"
    subject.base regex_match "[^\\x{2600}-\\x{27BF}\\x{1F300}-\\x{1F9FF}][\\x{2600}-\\x{27BF}\\x{1F300}-\\x{1F9FF}]\\x{FE0F}?$"
  not
    profile.by_sender func_call "profile.by_sender().solicited"
  attachments length_compare "0"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.html.rawregex_match
  • mailto:*[++unsubscribe@]
field:"body.html.raw" kind:regex_match value:"mailto:*[++unsubscribe@]"
strings.replace_confusables(body.current_thread.text)regex_match
  • (?:errors?|report|issues|website|repair|redesign|upgrade|Google\s+.{0,15}find it|glitch|send you|SEO|broken)
  • (?:error|report|issues|website|repair|redesign|upgrade|Google\s+.{0,15}find it|send you|SEO)
  • (?:free|send you|can i send|may i send|let me know|interested|get back to me|reply back|just reply)
  • (?:h(?:i|ello|ey)|morning)\b
  • (?:screenshot|error list|plan|quote|rank|professional|price|mistake)
  • (?:screenshot|errors? (?:list|report)|plan|quote|rank|professional|price|mistake|visibility|improvement|review|emailed.{0,10}more details|(?:may|can|shall|should) i (?:email|send|share|forward|show)(?:\s+you)?.{0,15}(?:the|more|those|these|some)?\s*(?:details|info(?:rmation)?|report|screenshots?|list))
  • (?:site|website|page|package|SEO)
  • h(?:i|ello|ey)\b
field:"strings.replace_confusables(body.current_thread.text)" kind:regex_match
strings.replace_confusables(subject.subject)contains
  • report
field:"strings.replace_confusables(subject.subject)" kind:contains value:"report"
strings.replace_confusables(subject.subject)regex_match
  • (?:proposal|cost|estimate|error|bug|audit|screenshot|strategy|rankings|issues|fix|website|design|review|price)
field:"strings.replace_confusables(subject.subject)" kind:regex_match value:"(?:proposal|cost|estimate|error|bug|audit|screenshot|strategy|rankings|issues|fix|website|design|review|price)"
subject.baseregex_match
  • [.?!-]{4,}
  • [^\x{2600}-\x{27BF}\x{1F300}-\x{1F9FF}][\x{2600}-\x{27BF}\x{1F300}-\x{1F9FF}]\x{FE0F}?$
field:"subject.base" kind:regex_match
subject.is_forwardeq
  • true transforms: boolean
field:"subject.is_forward" kind:eq value:"true"
subject.is_replyeq
  • true transforms: boolean
field:"subject.is_reply" kind:eq value:"true"
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"