Spam: Image as content with hidden HTML element

This has been observed in the delivery of emails containing account/membership expiration lure themes of popular online services or delivery notifications.

Sublime rule (View on GitHub)

 1name: "Spam: Image as content with hidden HTML element"
 2description: "This has been observed in the delivery of emails containing account/membership expiration lure themes of popular online services or delivery notifications."
 3type: "rule"
 4severity: "low"
 5source: |
 6  type.inbound
 7  and (not profile.by_sender().solicited or sender.email.email == "")
 8  // not high trust sender domains
 9  and (
10    (
11      sender.email.domain.root_domain in $high_trust_sender_root_domains
12      and not headers.auth_summary.dmarc.pass
13    )
14    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
15  )
16  and (
17    // find the template - a link that is a centered image
18    (
19      // at the start of a center
20      regex.contains(body.html.raw,
21                     'center(?:\x22[^\>]*)?\>\s*<a href=\"https?:\/\/[^\x22]+\x22(?:\s[a-z]+=\x22[^\x22]+\x22)*>\s*[^\n]*?(?:\<img src=\x22[^\x22]+\x22>(?:<[a-z]+>\s*)*){1,}<\/a>'
22      )
23      // method two for the start of a center but includes an header line
24      or regex.contains(body.html.raw,
25                        'center(?:\x22[^\>]*)?\>\s*<a href=\"https?:\/\/[^\x22]+\x22(?:\s[a-z]+=\x22[^\x22]+\x22)*>\s*[^\n]*?<h\d>[^\<]+<\/h\d+>\s*(?:\<img src=\x22[^\x22]+\x22>(?:<[a-z]+>\s*)*){1,}<\/a>(?:<[a-z]+>\s*)*<\/'
26      )
27      // method three for the start of a center, but includes words not in a header line
28      or regex.contains(body.html.raw,
29                        'center(?:[\x22\x3b][^\>]*)?\>\s*<a href=\"https?:\/\/[^\x22]+\x22(?:\s[a-z]+=\x22[^\x22]+\x22)*>\s*[^\<][^\/][^\a]+\s*<\/a>\s*(?:<[a-z]+>\s*)*\s*<a href=\"https?:\/\/[^\x22]+\x22(?:\s[a-z]+=\x22[^\x22]+\x22)*>\s*(?:<img src=\x22[^\x22]+\x22>(?:<[a-z]+>\s*)*){1,}<\/a>'
30      )
31      // method four - the body starts with a centered div which is not visable, which contains a link and img within the link
32      or regex.contains(body.html.raw,
33                        '<body(?:\x22[^\>]+)?\>\s*<center>\s*<(?:span|div)[^\>]*style=\x22[^\x22]*\s*(?:display\s*\x3a\s*none|visibility\s*\x3a\s*hidden)\x3b[^\x22]*\x22(?:\s*\w+=\"\w+\")*>[^\<]+</div>\s*<a[^\>]*href="[^\x22]+\x22(?:\s[a-z]+=\x22[^\x22]+\x22)*>\s*(?:<img src=\x22[^\x22]+\x22(?:\s[a-z]+=\x22[^\x22]+\x22)*>\s*){1,}\<\/a>'
34      )
35      // or at the end of the center
36      or regex.contains(body.html.raw,
37                        '<a href=\"https?:\/\/[^\x22]+\x22(?:\s[a-z]+=\x22[^\x22]+\x22)*>\s*(?:\<img src=\x22[^\x22]+\x22>(?:<\/a>|(?:<[a-z]+>\s*))*){1,}<\/center>'
38      )
39      // at the start of the body
40      or regex.contains(body.html.raw,
41                        'body(?:\x22[^\>]+)?\>\s*<a href=\"https?:\/\/[^\x22]+\x22(?:\s[a-z]+=\x22[^\x22]+\x22)*>\s*[^\n]*?(?:\<img src=\x22[^\x22]+\x22>(?:<[a-z]+>\s*)*){1,}<\/a>'
42      )
43      // a href with background url which is centered very early on in the body.html.raw
44      or regex.contains(body.html.raw,
45                        '^(?:<[^\>]+>\s*){0,6}<a[^\>]*href=\x22[^\>]+\>\s*<(?:div|span)[^\>]*style=\x22[^\x22]*background:url\([^\)]+\)[^\x22]*center;[^\>]*\>\s*<\/(?:div|span)>\s*</a>'
46      )
47      or regex.contains(body.html.raw,
48                        '<(?:div|span)[^\>]*style="[^\"]*center;[^\"]*\"[^\>]*>\s*<a href=\"[^\>]+\>(?:.|\W)*<img src="[^\"]+">\s*</a>\s*<br>\s*<(?:div|span)[^\>]*style="[^\"]*display\s*:\s*none;[^\"]*\">'
49      )
50    )
51    and (
52      // where there is a span/div that is hidden with either &nbsp\x3b\x200c? or underscores repeating multiple times OR followed by a new metatag
53      regex.contains(body.html.raw,
54                     '<(?:span|div)[^\>]*style=\x22[^\x22]*\s*(?:display\s*\x3a\s*none|visibility\s*\x3a\s*hidden)\x3b[^\x22]*\x22(?:\s*\w+=\"\w+\")*>\s*(?:<\/?[^\>]+>\s*)*(?:(?:_|[\pCc\pCf\pCs]*&nbsp\x3b\s*[\pCc\pCf\pCs]*){3,}|\s+\<meta |\s+\<center )'
55      )
56      or 
57      // a custom css value is used to hide the body
58      // unable to use capture groups to capture the custom html tag to apply the hidden style
59      // instead we use [A-Za-z] to catch a single char. 
60      regex.contains(body.html.raw,
61                     'style\s+[^\>]*type\s*\x3d\s*\"text/css\"[^\>]*>\s*[^\<]*[A-Za-z]\s*\{[^\}]*(?:display\s*\x3a\s*none|visibility\s*\x3a\s*hidden)[^\}]*\}[^\<]+\</style><[A-Za-z]>'
62      )
63      or 
64      // the hidden span/div is before the body/meta
65      regex.contains(body.html.raw,
66                     '<(?:span|div)[^\>]*style=\x22[^\x22]*\s*(?:display\s*\x3a\s*none|visibility\s*\x3a\s*hidden)\x3b[^\x22]*\x22(?:\s*\w+=\"\w+\")*>\s*\<(?:body|meta|head|(?:<?div[^\>]+\>\s*(?:[^\<]*|<[a-z]+>\s*)<\/div>\s*){2,})'
67      )
68      // the length of the inner text is greather than or equal to 10x more than the display text
69      // this attempts to generically cover multiple methods of hiding text
70      or (
71        length(body.html.inner_text) > 0
72        and (
73          length(body.html.inner_text) >= (length(body.html.display_text) * 10)
74        )
75      )
76      // used to push down or move content out of view
77      or (
78        sum([
79              regex.count(body.html.display_text, '[\r\n].?[\r\n]'),
80              regex.icount(body.html.raw, '(?:<br>(?:[^\<]|\s){0,2}){3}'),
81              regex.icount(body.html.raw, '(?:<blockquote>(?:[^\<]|\s){0,2}){3}'),
82              regex.icount(body.html.raw, '<div>(?:.|\s){0,2}<\/div>'),
83              regex.icount(body.html.raw, '<span>(?:.|\s){0,2}<\/span>'),
84            ]
85        ) > 40
86      )
87    )
88  )  
89attack_types:
90  - "Spam"
91tactics_and_techniques:
92  - "Evasion"
93  - "Image as content"
94detection_methods:
95  - "Content analysis"
96  - "HTML analysis"
97  - "Sender analysis"
98id: "5de8861f-a343-521f-ac8c-b4b91e389a6e"
to-top