Detection rules › Sublime MQL
Sublime MQL rules: evasion
| Rule | Severity |
|---|---|
| Evasion: Hidden content divs from freemail sender | medium |
| Link: Credential harvesting with excess padding evasion | low |
Link: Credential harvesting with excess padding evasion
#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).
| Category | Values |
|---|---|
| Attack types | Credential Phishing |
| Tactics and techniques | Evasion, Social engineering |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Sublime | Inbound email message |
Message attributes
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)'
)
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 pushdown >= 1500px
or (
regex.icontains(body.html.raw,
'margin-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-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-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.
- inbound message
any of
body.current_thread.linkswhere all hold:- .display_text matches '(?:open|sign.?in|log.?in|retain|credential|secure|confirm|accept|release|review|document)'
- .href_url.domain.root_domain is not sender.email.domain.root_domain
not:
- .display_text matches 'open source'
- beta.parse_exif(file.message_screenshot()).image_height > 1500
- beta.parse_exif(file.message_screenshot()).image_height * 100 / regex.count(body.html.display_text) > 500
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-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-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-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)
| Field | Match | Value |
|---|---|---|
regex.icontains | regex | (?:open|sign.?in|log.?in|retain|credential|secure|confirm|accept|release|review|document) |
regex.icontains | regex | (?:<div>\s*<br\s*/?\s*>\s*</div>\s*){30,} |
regex.icontains | regex | (?:<div\s+style="[^"]+"\s*[^>]*>\s*<br\s*/?\s*>\s*</div>\s*){20,} |
regex.icontains | regex | (?:<div\s+[^>]+>\s*(?:&nbsp;|&#160;)\s*</div>\s*){20,} |
regex.icontains | regex | (?:<p>\s*(?:&nbsp;|&#160;)\s*</p>\s*){25,} |
regex.icontains | regex | margin-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)"
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-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-top\\s*:\\s*(?:1[5-9]\\d{2}|[2-9]\\d{3}|\\d{5,})px"
body.html.raw regex_match "margin-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.
| Field | Kind | Values | Search |
|---|---|---|---|
body.html.raw | regex_match |
| field:"body.html.raw" kind:regex_match |
type.inbound | eq |
| field:"type.inbound" kind:eq value:"true" |