Detection rules › Sublime MQL

Sublime MQL rules: recipient

Body HTML: Recipient SLD in HTML class

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

Detects when there is a single recipient within $org_domains where the domain SLD is concealed within HTML class attributes. The message comes from either an unauthenticated trusted sender or an untrusted source.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// not an org_domain which passed dmarc
and not (
  sender.email.domain.domain in $org_domains
  and coalesce(headers.auth_summary.dmarc.pass, false)
)

// a single recipient within the org_domains
and (
  length(recipients.to) == 1
  and all(recipients.to, .email.domain.domain in $org_domains)
)

// there are more than 30 class attributes containing the recipient's SLD
and length(filter(html.xpath(body.html, '//@class').nodes,
                  any(recipients.to,
                      // the class name is the same
                      ..raw =~ .email.domain.sld
                      // a specific observed pattern with a prefix of x_hz
                      or strings.istarts_with(..raw,
                                              strings.concat('x_hz',
                                                             .email.domain.sld
                                              )
                      )
                  )
           )
) > 30
// 80% or more the class attributes contain the recipient's SLD
and ratio(html.xpath(body.html, '//@class').nodes,
          any(recipients.to,
              // the class name is the same
              ..raw =~ .email.domain.sld
              // a specific observed pattern with a prefix of x_hz
              or strings.istarts_with(..raw,
                                      strings.concat('x_hz', .email.domain.sld)
              )
          )
) > 0.80

// not replies
and not (length(headers.references) > 0 or headers.in_reply_to is not null)
// 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 when there is a single recipient within $org_domains where the domain SLD is concealed within HTML class attributes. The message comes from either an unauthenticated trusted sender or an untrusted source.

  1. inbound message
  2. not:
    • all of:
      • sender.email.domain.domain in $org_domains
      • coalesce(headers.auth_summary.dmarc.pass)
  3. all of:
    • length(recipients.to) is 1
    • all of recipients.to where:
      • .email.domain.domain in $org_domains
  4. length(filter(html.xpath(body.html, '//@class').nodes, any(recipients.to, ..raw =~ .email.domain.sld or strings.istarts_with(..raw, strings.concat('x_hz', .email.domain.sld))))) > 30
  5. ratio(html.xpath(body.html, '//@class').nodes) > 0.8
  6. none of:
    • length(headers.references) > 0
    • headers.in_reply_to is set
  7. any of:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • not:
        • headers.auth_summary.dmarc.pass
    • sender.email.domain.root_domain not in $high_trust_sender_root_domains

Inspects: body.html, headers.auth_summary.dmarc.pass, headers.in_reply_to, headers.references, recipients.to, recipients.to[].email.domain.domain, recipients.to[].email.domain.sld, sender.email.domain.domain, sender.email.domain.root_domain, type.inbound. Sensors: html.xpath, strings.concat, strings.istarts_with. Reference lists: $high_trust_sender_root_domains, $org_domains.

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
    and
      coalesce func_call "coalesce(headers.auth_summary.dmarc.pass)"
       macro "sender.email.domain.domain in org_domains"
  not
    or
      headers.in_reply_to is_not_null
      headers.references length_compare "0"
  filter(html.xpath(body.html, '//@class').nodes, any(recipients.to, ..raw =~ .email.domain.sld or strings.istarts_with(..raw, strings.concat('x_hz', .email.domain.sld)))) length_compare "30"
  ratio func_call "ratio(html.xpath(body.html, '//@class').nodes) > 0.8"
  recipients.to length_compare "1"
  type.inbound eq "true"
   macro "all(recipients.to)"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
headers.in_reply_tois_not_null(no value, null check)excludes:headers.in_reply_to
headers.referenceslength_compare0excludes:headers.references field:"headers.references" value:"0"

Indicators

These rows show field, operator, and value matches.

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