Detection rules › Sublime MQL

Sublime MQL rules: evasion

Evasion: Hidden content divs from freemail sender

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

Detects inbound messages from freemail senders containing multiple hidden HTML div elements with specific styling properties (display:none, opacity:0, zero dimensions) that are commonly used to evade content filtering and detection systems.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Free email provider

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
and sender.email.domain.root_domain in $free_email_providers
and strings.count(body.html.raw,
                  '<div style="display:none;opacity:0;width:0;height:0;overflow:hidden" aria-hidden="true">'
) >= 3

Detection logic

Scope: inbound message.

Detects inbound messages from freemail senders containing multiple hidden HTML div elements with specific styling properties (display:none, opacity:0, zero dimensions) that are commonly used to evade content filtering and detection systems.

  1. inbound message
  2. sender.email.domain.root_domain in $free_email_providers
  3. strings.count(body.html.raw, '<div style="display:none;opacity:0;width:0;height:0;overflow:hidden" aria-hidden="true">') ≥ 3

Inspects: body.html.raw, sender.email.domain.root_domain, type.inbound. Sensors: strings.count. Reference lists: $free_email_providers.

Stages and Predicates

Stage 1: mql_rule

and
  strings.count func_call "strings.count(body.html.raw, \"<div style=\"display:none;opacity:0;width:0;height:0;overflow:hidden\" aria-hidden=\"true\">\") >= 3"
  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"

Evasion: Suspicious TLD link redirecting to Wikipedia

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

Flags inbound messages containing links with domains registered under suspicious top-level domains that redirect to Wikipedia when analyzed. This behavior indicates the link is detecting automated sandbox or security analysis tools and serving benign content to evade detection, while likely delivering malicious content to real users.

Threat classification

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

CategoryValues
Attack typesCredential Phishing, Spam
Tactics and techniquesEvasion, Open redirect

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • body
  • body.current_thread
  • type

Rule body

type.inbound
and any(body.current_thread.links,
        .href_url.domain.tld in $suspicious_tlds
        and ml.link_analysis(.).effective_url.url in (
          'https://www.wikipedia.org/'
        )
)

Detection logic

Scope: inbound message.

Flags inbound messages containing links with domains registered under suspicious top-level domains that redirect to Wikipedia when analyzed. This behavior indicates the link is detecting automated sandbox or security analysis tools and serving benign content to evade detection, while likely delivering malicious content to real users.

  1. inbound message
  2. any of body.current_thread.links where all hold:
    • .href_url.domain.tld in $suspicious_tlds
    • ml.link_analysis(.).effective_url.url in ('https://www.wikipedia.org/')

Inspects: body.current_thread.links, body.current_thread.links[].href_url.domain.tld, type.inbound. Sensors: ml.link_analysis. Reference lists: $suspicious_tlds.

Stages and Predicates

Stage 1: mql_rule

and
  any(body.current_thread.links)
    and
      ml.link_analysis func_call "ml.link_analysis(body.current_thread.links[]).effective_url.url in (https://www.wikipedia.org/)"
       macro "body.current_thread.links[].href_url.domain.tld in suspicious_tlds"
  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"

Link: Credential harvesting with excess padding evasion

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

Detects inbound messages containing credential-related action links with tall screenshot images and HTML padding techniques used to evade detection. The rule identifies messages with excessive empty div tags, non-breaking spaces, or large margin-top values that artificially increase content height while hiding malicious intent.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

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

Rule body

type.inbound
// CTA link with action-oriented display text pointing to a different domain than the sender
and any(body.current_thread.links,
        regex.icontains(.display_text,
                        '(?:open|sign.?in|log.?in|retain|credential|secure|confirm|accept|release|review|document|deliver)'
        )
        and .href_url.domain.root_domain != sender.email.domain.root_domain
        and not regex.icontains(.display_text, 'open source')
)
// tall rendered email with low word density
and beta.parse_exif(file.message_screenshot()).image_height > 1500
and beta.parse_exif(file.message_screenshot()).image_height * 100 / regex.count(body.html.display_text,
                                                                                '\S+'
) > 500
// html whitespace stuffing patterns
and (
  // bare div-br blocks repeated 30+ times
  regex.icontains(body.html.raw, '(?:<div>\s*<br\s*/?\s*>\s*</div>\s*){30,}')
  // style div-br blocks repeated 20+ times
  or regex.icontains(body.html.raw,
                     '(?:<div\s+style="[^"]+"\s*[^>]*>\s*<br\s*/?\s*>\s*</div>\s*){20,}'
  )
  // attributed empty div-nbsp blocks repeated 20+ times (styled/classed empty divs, e.g. Outlook Aptos)
  // requires an attribute to avoid bare <div>&nbsp;</div> newsletter spacers
  or (
    regex.icontains(body.html.raw,
                    '(?:<div\s+[^>]+>\s*(?:&nbsp;|&#160;)\s*</div>\s*){20,}'
    )
    // exclude collapsed/hidden empty divs (display:none, font-size:0, line-height:0)
    // these render to zero height and are ESP preheader artifacts, not visible stuffing
    and not regex.icontains(body.html.raw,
                            '(?:<div\s+[^>]*(?:display\s*:\s*none|font-size\s*:\s*0|line-height\s*:\s*0)[^>]*>\s*(?:&nbsp;|&#160;)\s*</div>\s*){20,}'
    )
  )
  // p-nbsp blocks repeated 25+ times
  or regex.icontains(body.html.raw,
                     '(?:<p>\s*(?:&nbsp;|&#160;)\s*</p>\s*){25,}'
  )
  // css margin-top or padding-top pushdown >= 1500px
  or (
    regex.icontains(body.html.raw,
                    '(?:margin|padding)-top\s*:\s*(?:1[5-9]\d{2}|[2-9]\d{3}|\d{5,})px'
    )
    and not regex.icontains(body.html.raw,
                            'position\s*:\s*absolute[^"]*(?:margin|padding)-top\s*:\s*(?:1[5-9]\d{2}|[2-9]\d{3}|\d{5,})px'
    )
    and not regex.icontains(body.html.raw,
                            'margin-left\s*:\s*\d{3,}px[^"]*(?:margin|padding)-top\s*:\s*(?:1[5-9]\d{2}|[2-9]\d{3}|\d{5,})px'
    )
  )
)

Detection logic

Scope: inbound message.

Detects inbound messages containing credential-related action links with tall screenshot images and HTML padding techniques used to evade detection. The rule identifies messages with excessive empty div tags, non-breaking spaces, or large margin-top values that artificially increase content height while hiding malicious intent.

  1. inbound message
  2. any of body.current_thread.links where all hold:
    • .display_text matches '(?:open|sign.?in|log.?in|retain|credential|secure|confirm|accept|release|review|document|deliver)'
    • .href_url.domain.root_domain is not sender.email.domain.root_domain
    • not:
      • .display_text matches 'open source'
  3. beta.parse_exif(file.message_screenshot()).image_height > 1500
  4. beta.parse_exif(file.message_screenshot()).image_height * 100 / regex.count(body.html.display_text) > 500
  5. any of:
    • body.html.raw matches '(?:<div>\\s*<br\\s*/?\\s*>\\s*</div>\\s*){30,}'
    • body.html.raw matches '(?:<div\\s+style="[^"]+"\\s*[^>]*>\\s*<br\\s*/?\\s*>\\s*</div>\\s*){20,}'
    • all of:
      • body.html.raw matches '(?:<div\\s+[^>]+>\\s*(?:&nbsp;|&#160;)\\s*</div>\\s*){20,}'
      • not:
        • body.html.raw matches '(?:<div\\s+[^>]*(?:display\\s*:\\s*none|font-size\\s*:\\s*0|line-height\\s*:\\s*0)[^>]*>\\s*(?:&nbsp;|&#160;)\\s*</div>\\s*){20,}'
    • body.html.raw matches '(?:<p>\\s*(?:&nbsp;|&#160;)\\s*</p>\\s*){25,}'
    • all of:
      • body.html.raw matches '(?:margin|padding)-top\\s*:\\s*(?:1[5-9]\\d{2}|[2-9]\\d{3}|\\d{5,})px'
      • not:
        • body.html.raw matches 'position\\s*:\\s*absolute[^"]*(?:margin|padding)-top\\s*:\\s*(?:1[5-9]\\d{2}|[2-9]\\d{3}|\\d{5,})px'
      • not:
        • body.html.raw matches 'margin-left\\s*:\\s*\\d{3,}px[^"]*(?:margin|padding)-top\\s*:\\s*(?:1[5-9]\\d{2}|[2-9]\\d{3}|\\d{5,})px'

Inspects: body.current_thread.links, body.current_thread.links[].display_text, body.current_thread.links[].href_url.domain.root_domain, body.html.raw, sender.email.domain.root_domain, type.inbound. Sensors: beta.parse_exif, file.message_screenshot, regex.icontains.

Indicators matched (6)

FieldMatchValue
regex.icontainsregex(?:open|sign.?in|log.?in|retain|credential|secure|confirm|accept|release|review|document|deliver)
regex.icontainsregex(?:<div>\s*<br\s*/?\s*>\s*</div>\s*){30,}
regex.icontainsregex(?:<div\s+style="[^"]+"\s*[^>]*>\s*<br\s*/?\s*>\s*</div>\s*){20,}
regex.icontainsregex(?:<div\s+[^>]+>\s*(?:&nbsp;|&#160;)\s*</div>\s*){20,}
regex.icontainsregex(?:<p>\s*(?:&nbsp;|&#160;)\s*</p>\s*){25,}
regex.icontainsregex(?:margin|padding)-top\s*:\s*(?:1[5-9]\d{2}|[2-9]\d{3}|\d{5,})px

Stages and Predicates

Stage 1: mql_rule

and
  any(body.current_thread.links)
    and
      not
        body.current_thread.links.display_text regex_match "open source"
      body.current_thread.links.display_text regex_match "(?:open|sign.?in|log.?in|retain|credential|secure|confirm|accept|release|review|document|deliver)"
      body.current_thread.links.href_url.domain.root_domain cross_field_compare "sender.email.domain.root_domain"
  or
    and
      not
        body.html.raw regex_match "(?:<div\\s+[^>]*(?:display\\s*:\\s*none|font-size\\s*:\\s*0|line-height\\s*:\\s*0)[^>]*>\\s*(?:&nbsp;|&#160;)\\s*</div>\\s*){20,}"
      body.html.raw regex_match "(?:<div\\s+[^>]+>\\s*(?:&nbsp;|&#160;)\\s*</div>\\s*){20,}"
    and
      not
        body.html.raw regex_match "margin-left\\s*:\\s*\\d{3,}px[^\"]*(?:margin|padding)-top\\s*:\\s*(?:1[5-9]\\d{2}|[2-9]\\d{3}|\\d{5,})px"
      not
        body.html.raw regex_match "position\\s*:\\s*absolute[^\"]*(?:margin|padding)-top\\s*:\\s*(?:1[5-9]\\d{2}|[2-9]\\d{3}|\\d{5,})px"
      body.html.raw regex_match "(?:margin|padding)-top\\s*:\\s*(?:1[5-9]\\d{2}|[2-9]\\d{3}|\\d{5,})px"
    body.html.raw regex_match "(?:<div>\\s*<br\\s*/?\\s*>\\s*</div>\\s*){30,}"
    body.html.raw regex_match "(?:<div\\s+style=\"[^\"]+\"\\s*[^>]*>\\s*<br\\s*/?\\s*>\\s*</div>\\s*){20,}"
    body.html.raw regex_match "(?:<p>\\s*(?:&nbsp;|&#160;)\\s*</p>\\s*){25,}"
  beta.parse_exif func_call "beta.parse_exif(file.message_screenshot()).image_height > 1500"
  type.inbound eq "true"
   macro "((beta.parse_exif(file.message_screenshot()).image_height * 100) / regex.count(body.html.display_text)) > 500"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.html.rawregex_match
  • (?:<div>\s*<br\s*/?\s*>\s*</div>\s*){30,}
  • (?:<div\s+[^>]+>\s*(?: | )\s*</div>\s*){20,}
  • (?:<div\s+style="[^"]+"\s*[^>]*>\s*<br\s*/?\s*>\s*</div>\s*){20,}
  • (?:<p>\s*(?: | )\s*</p>\s*){25,}
  • (?:margin|padding)-top\s*:\s*(?:1[5-9]\d{2}|[2-9]\d{3}|\d{5,})px
field:"body.html.raw" kind:regex_match
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"