Detection rules › Sublime MQL

Body: HTML whitespace stuffing with short initial message

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

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

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Social engineering

Event coverage

Rule body MQL

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

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

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

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

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

Detection logic

Scope: inbound message.

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

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

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

Indicators matched (7)

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