Detection rules › Sublime MQL

Sublime MQL rules: mass

Mass campaign: Cross Site Scripting (XSS) attempt

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

Message subject or body contains Cross Site Scripting (XSS) indicators, and was sent to multiple unknown senders. Known spam technique.

Threat classification

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

CategoryValues
Attack typesMalware/Ransomware, Spam
Tactics and techniquesExploit, Free email provider, Scripting, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and (
  length(recipients.to) > 10
  and length(filter(recipients.to,
                    .email.domain.domain not in $org_domains
                    and .email.email not in $recipient_emails
                    and (
                      .email.domain.valid
                      or strings.icontains(.display_name, "undisclosed")
                    )
             )
  ) >= 10
)
and (
  strings.ilike(subject.subject,
                '*<*script*>*',
                '*xss.report*',
                '*eval(atob*',
                '*<*onload*>*',
                '*<*onerror*>*'
  )
  or strings.ilike(body.current_thread.text,
                   '*<*script*>*',
                   '*xss.report*',
                   '*eval(atob*',
                   '*<*onload*>*',
                   '*<*onerror*>*'
  )
)
and (
  length(body.links) == 0
  or (
    length(body.links) == 1
    and all(body.links, .href_url.domain.domain == "this.id")
  )
)
and profile.by_sender().prevalence != "common"
and not profile.by_sender().solicited
and not profile.by_sender().any_messages_benign

// negate highly trusted sender domains unless they fail DMARC authentication
and (
  (
    sender.email.domain.root_domain in $high_trust_sender_root_domains
    and (
      any(distinct(headers.hops, .authentication_results.dmarc is not null),
          strings.ilike(.authentication_results.dmarc, "*fail")
      )
    )
  )
  or sender.email.domain.root_domain not in $high_trust_sender_root_domains
)

Detection logic

Scope: inbound message.

Message subject or body contains Cross Site Scripting (XSS) indicators, and was sent to multiple unknown senders. Known spam technique.

  1. inbound message
  2. all of:
    • length(recipients.to) > 10
    • length(filter(recipients.to, .email.domain.domain not in $org_domains and .email.email not in $recipient_emails and .email.domain.valid or strings.icontains(.display_name, 'undisclosed'))) ≥ 10
  3. any of:
    • subject.subject matches any of 5 patterns
      • *<*script*>*
      • *xss.report*
      • *eval(atob*
      • *<*onload*>*
      • *<*onerror*>*
    • body.current_thread.text matches any of 5 patterns
      • *<*script*>*
      • *xss.report*
      • *eval(atob*
      • *<*onload*>*
      • *<*onerror*>*
  4. any of:
    • length(body.links) is 0
    • all of:
      • length(body.links) is 1
      • all of body.links where:
        • .href_url.domain.domain is 'this.id'
  5. profile.by_sender().prevalence is not 'common'
  6. not:
    • profile.by_sender().solicited
  7. not:
    • profile.by_sender().any_messages_benign
  8. any of:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • any of distinct(headers.hops) where:
        • .authentication_results.dmarc matches '*fail'
    • sender.email.domain.root_domain not in $high_trust_sender_root_domains

Inspects: body.current_thread.text, body.links, body.links[].href_url.domain.domain, headers.hops, headers.hops[].authentication_results.dmarc, recipients.to, recipients.to[].display_name, recipients.to[].email.domain.domain, recipients.to[].email.domain.valid, recipients.to[].email.email, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: profile.by_sender, strings.icontains, strings.ilike. Reference lists: $high_trust_sender_root_domains, $org_domains, $recipient_emails.

Indicators matched (8)

FieldMatchValue
strings.icontainssubstringundisclosed
strings.ilikesubstring*<*script*>*
strings.ilikesubstring*xss.report*
strings.ilikesubstring*eval(atob*
strings.ilikesubstring*<*onload*>*
strings.ilikesubstring*<*onerror*>*
body.links[].href_url.domain.domainequalsthis.id
strings.ilikesubstring*fail

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      any(distinct(headers.hops))
        distinct(headers.hops).authentication_results.dmarc ends_with "fail"
       macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
     macro "sender.email.domain.root_domain not in high_trust_sender_root_domains"
  or
    and
      body.links length_compare "1"
       macro "all(body.links)"
    body.links length_compare "0"
  or
    body.current_thread.text match "eval(atob"
    body.current_thread.text match "xss.report"
    body.current_thread.text wildcard "*<*onerror*>*"
    body.current_thread.text wildcard "*<*onload*>*"
    body.current_thread.text wildcard "*<*script*>*"
    subject.subject match "eval(atob"
    subject.subject match "xss.report"
    subject.subject wildcard "*<*onerror*>*"
    subject.subject wildcard "*<*onload*>*"
    subject.subject wildcard "*<*script*>*"
  not
    profile.by_sender func_call "profile.by_sender().any_messages_benign"
  not
    profile.by_sender func_call "profile.by_sender().solicited"
  filter(recipients.to, .email.domain.domain not in $org_domains and .email.email not in $recipient_emails and .email.domain.valid or strings.icontains(.display_name, 'undisclosed')) length_compare "10"
  profile.by_sender func_call "profile.by_sender().prevalence != common"
  recipients.to length_compare "10"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textwildcard
  • *<*onerror*>*
  • *<*onload*>*
  • *<*script*>*
  • *eval(atob*
  • *xss.report*
field:"body.current_thread.text" kind:wildcard
subject.subjectwildcard
  • *<*onerror*>*
  • *<*onload*>*
  • *<*script*>*
  • *eval(atob*
  • *xss.report*
field:"subject.subject" kind:wildcard
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"

Mass campaign: recipient address in subject, body, and link (untrusted sender)

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

This detects a pattern commonly observed in mass phishing campaigns. The local_part or the full email address of the recipient is used in the subject, body, and link query parameter to "personalize" the attack.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesSocial engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and length(recipients.to) + length(recipients.bcc) + length(recipients.cc) == 1

// exclude To: Undisclosed recipients:;
// since we won't have a valid recipient email
and any(recipients.to, .email.domain.valid == true)
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 (
  any(recipients.to,
      (
        strings.icontains(subject.subject, .email.email)
        or strings.icontains(subject.subject, .email.local_part)
      )
      and (
        .email.domain.valid or strings.icontains(.display_name, "undisclosed")
      )
  )
)
and any(recipients.to,
        strings.icontains(body.current_thread.text, .email.email)
)
and any(body.links,
        any(recipients.to,
            strings.icontains(..href_url.query_params, .email.email)
        )
        and (
          (
            not strings.icontains(.display_text, "unsubscribe")
            and not strings.icontains(.href_url.path, "unsubscribe")
          )
        )
)
and any(ml.nlu_classifier(body.current_thread.text).intents,
        .name in ("cred_theft") and .confidence == "high"
)

// 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.

This detects a pattern commonly observed in mass phishing campaigns. The local_part or the full email address of the recipient is used in the subject, body, and link query parameter to "personalize" the attack.

  1. inbound message
  2. length(recipients.to) + length(recipients.bcc) + length(recipients.cc) is 1
  3. any of recipients.to where:
    • .email.domain.valid is True
  4. 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
  5. any of recipients.to where all hold:
    • any of:
      • strings.icontains(subject.subject)
      • strings.icontains(subject.subject)
    • any of:
      • .email.domain.valid
      • .display_name contains 'undisclosed'
  6. any of recipients.to where:
    • strings.icontains(body.current_thread.text)
  7. any of body.links where all hold:
    • any of recipients.to where:
      • strings.icontains(.href_url.query_params)
    • all of:
      • not:
        • .display_text contains 'unsubscribe'
      • not:
        • .href_url.path contains 'unsubscribe'
  8. any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
    • .name in ('cred_theft')
    • .confidence is 'high'
  9. any of:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • not:
        • headers.auth_summary.dmarc.pass
    • sender.email.domain.root_domain not in $high_trust_sender_root_domains

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

Indicators matched (3)

FieldMatchValue
strings.icontainssubstringundisclosed
ml.nlu_classifier(body.current_thread.text).intents[].namemembercred_theft
ml.nlu_classifier(body.current_thread.text).intents[].confidenceequalshigh

Stages and Predicates

Stage 1: mql_rule

and
  any(body.links)
    and
      not
        body.links.display_text contains "unsubscribe"
      not
        body.links.href_url.path contains "unsubscribe"
      any(recipients.to)
        strings.icontains func_call "strings.icontains(body.links[].href_url.query_params)"
  or
    and
      not
        headers.auth_summary.dmarc.pass eq "true"
       macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
     macro "sender.email.domain.root_domain not in high_trust_sender_root_domains"
  or
    and
      not
        profile.by_sender func_call "profile.by_sender().any_messages_benign"
      profile.by_sender func_call "profile.by_sender().any_messages_malicious_or_spam"
    profile.by_sender func_call "profile.by_sender().prevalence in (new, outlier)"
  any(recipients.to)
    and
      or
        recipients.to.display_name contains "undisclosed"
        recipients.to.email.domain.valid eq "true"
      strings.icontains func_call "strings.icontains(subject.subject)"
  any(ml.nlu_classifier(body.current_thread.text).intents)
    and
      ml.nlu_classifier(body.current_thread.text).intents.confidence eq "high"
      ml.nlu_classifier(body.current_thread.text).intents.name eq "cred_theft"
  any(recipients.to)
    recipients.to.email.domain.valid eq "true"
  any(recipients.to)
    strings.icontains func_call "strings.icontains(body.current_thread.text)"
  type.inbound eq "true"
   macro "((length(recipients.to) + length(recipients.bcc)) + length(recipients.cc)) == 1"

Indicators

These rows show field, operator, and value matches.

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