Brand impersonation: Microsoft with low reputation links
Detects low reputation links with Microsoft specific indicators in the body.
Sublime rule (View on GitHub)
1name: "Brand impersonation: Microsoft with low reputation links"
2description: "Detects low reputation links with Microsoft specific indicators in the body."
3type: "rule"
4severity: "medium"
5source: |
6 type.inbound
7 and 0 < length(body.links) < 50
8 // suspicious link
9 and any(body.links,
10 (
11 .href_url.domain.root_domain not in $tranco_1m
12 or .href_url.domain.domain in $free_file_hosts
13 or .href_url.domain.root_domain in $free_file_hosts
14 or .href_url.domain.root_domain in $free_subdomain_hosts
15 or .href_url.domain.domain in $url_shorteners
16 or .href_url.domain.domain in $social_landing_hosts
17 or
18
19 // mass mailer link, masks the actual URL
20 .href_url.domain.root_domain in (
21 "hubspotlinks.com",
22 "mandrillapp.com",
23 "sendgrid.net",
24 "rs6.net"
25 )
26
27 // Google AMP redirect
28 or (
29 .href_url.domain.sld == "google"
30 and strings.starts_with(.href_url.path, "/amp/")
31 )
32
33 // Recipient email address in link
34 or any(body.links,
35 any(recipients.to,
36 strings.icontains(..href_url.url, .email.email)
37 and any(recipients.to, .email.domain.valid)
38 )
39 )
40 or .href_url.domain.root_domain == "beehiiv.com"
41 )
42
43 // exclude sources of potential FPs
44 and (
45 .href_url.domain.root_domain not in (
46 "svc.ms",
47 "sharepoint.com",
48 "1drv.ms",
49 "microsoft.com",
50 "aka.ms",
51 "msftauthimages.net",
52 "mimecastprotect.com",
53 "office.com",
54 "microsoftproject.com"
55 )
56 or any(body.links, .href_url.domain.domain in $free_file_hosts)
57 )
58 and .href_url.domain.root_domain not in $org_domains
59 and .href_url.domain.valid
60 )
61
62 // not a reply
63 and (
64 length(headers.references) == 0
65 or not any(headers.hops, any(.fields, strings.ilike(.name, "In-Reply-To")))
66 )
67
68 // Microsoft logo
69 and (
70 any(attachments,
71 .file_type in $file_types_images
72 and any(ml.logo_detect(.).brands, strings.starts_with(.name, "Microsoft"))
73 )
74 or strings.istarts_with(strings.replace_confusables(body.current_thread.text), "Microsoft ")
75 or (
76 regex.imatch(strings.replace_confusables(body.current_thread.text),
77 '[\n\s]*[o0O]ff[il1]ce\b.*'
78 )
79 and not regex.icontains(strings.replace_confusables(body.current_thread.text),
80 'office (for lease|rent|sale)'
81 )
82 )
83 or any(ml.logo_detect(beta.message_screenshot()).brands,
84 strings.starts_with(.name, "Microsoft")
85 )
86 or (
87 regex.icontains(body.html.raw,
88 '<table[^>]*>\s*<tbody[^>]*>\s*<tr[^>]*>\s*(<td[^>]*bgcolor="#[0-9A-Fa-f]{6}"[^>]*>\s* \s*</td>\s*){2}\s*</tr>\s*<tr[^>]*>\s*(<td[^>]*bgcolor="#[0-9A-Fa-f]{6}"[^>]*>\s* \s*</td>\s*){2}'
89 )
90 or regex.icontains(body.html.raw,
91 '<td style="background:\s*rgb\(246,\s*93,\s*53\);\s*height:\d+px;">.*?<td style="background:\s*rgb\(129,\s*187,\s*5\);\s*height:\d+px;">.*?<td style="background:\s*rgb\(4,\s*165,\s*240\);\s*height:\d+px;">.*?<td style="background:\s*rgb\(255,\s*186,\s*7\);\s*height:\d+px;">'
92 )
93 or 4 of (
94 regex.icontains(body.html.raw,
95 '<td style="width:.\d.px;.height:.\d.px;.background-color:.rgb\(245, 189, 67\);">.{0,10}</td>'
96 ),
97 regex.icontains(body.html.raw,
98 '<td style="width:.\d.px;.height:.\d.px;.background-color:.rgb\(137, 184, 57\);">.{0,10}</td>'
99 ),
100 regex.icontains(body.html.raw,
101 '<td style="width:.\d.px;.height:.\d.px;.background-color:.rgb\(217, 83, 51\);">.{0,10}</td>'
102 ),
103 regex.icontains(body.html.raw,
104 '<td style="width:.\d.px;.height:.\d.px;.background-color:.rgb\(71, 160, 218\);">.{0,10}</td>'
105 )
106 )
107 )
108 or regex.icontains(body.html.raw,
109 '<table[^>]*>\s*<tbody[^>]*>\s*<tr[^>]*>\s*(<td[^>]*bgcolor="#[0-9A-Fa-f]{6}"[^>]*>\s* \s*</td>\s*){2}\s*</tr>\s*<tr[^>]*>\s*(<td[^>]*bgcolor="#[0-9A-Fa-f]{6}"[^>]*>\s* \s*</td>\s*){2}'
110 )
111 or 3 of (
112 regex.icontains(body.html.raw, '.password-expiration'),
113 regex.icontains(body.html.raw, 'color: #2672ec;'),
114 regex.icontains(body.html.raw, 'Microsoft')
115 )
116 or 4 of (
117 regex.icontains(body.html.raw, 'rgb\(246,\s?93,\s?53\)'),
118 regex.icontains(body.html.raw, 'rgb\(129,\s?187,\s?5\)'),
119 regex.icontains(body.html.raw, 'rgb\(4,\s?165,\s?240\)'),
120 regex.icontains(body.html.raw, 'rgb\(255,\s?186,\s?7\)'),
121 )
122 or 4 of (
123 regex.icontains(body.html.raw,
124 '(background-color:|background:|bgcolor=)(.)red'
125 ),
126 regex.icontains(body.html.raw, 'rgb\(19,\s?186,\s?132\)'),
127 regex.icontains(body.html.raw, 'rgb\(4,\s?166,\s?240\)'),
128 regex.icontains(body.html.raw, 'rgb\(255,\s?186,\s?8\)'),
129 )
130 or 4 of (
131 regex.icontains(body.html.raw, 'rgb\(245,\s?189,\s?67\)'),
132 regex.icontains(body.html.raw, 'rgb\(137,\s?184,\s?57\)'),
133 regex.icontains(body.html.raw, 'rgb\(217,\s?83,\s?51\)'),
134 regex.icontains(body.html.raw, 'rgb\(71,\s?160,\s?218\)')
135 )
136 or 4 of (
137 regex.icontains(body.html.raw, 'rgb\(73,\s?161,\s?232\)'),
138 regex.icontains(body.html.raw, 'rgb\(224,\s?92,\s?53\)'),
139 regex.icontains(body.html.raw, 'rgb\(139,\s?183,\s?55\)'),
140 regex.icontains(body.html.raw, 'rgb\(244,\s?188,\s?65\)')
141 )
142 or 4 of (
143 regex.icontains(body.html.raw, 'rgb\(213,\s?56,\s?62\)'),
144 regex.icontains(body.html.raw, 'rgb\(0,\s?114,\s?30\)'),
145 regex.icontains(body.html.raw, 'rgb\(0,\s?110,\s?173\)'),
146 regex.icontains(body.html.raw, 'rgb\(227,\s?209,\s?43\)'),
147 )
148 or 4 of (
149 regex.icontains(body.html.raw, 'rgb\(246,\s?93,\s?53\)'),
150 regex.icontains(body.html.raw, 'rgb\(129,\s?187,\s?5\)'),
151 regex.icontains(body.html.raw, 'rgb\(4,\s?165,\s?240\)'),
152 regex.icontains(body.html.raw, 'rgb\(255,\s?186,\s?7\)')
153 )
154 or 4 of (
155 regex.icontains(body.html.raw, 'rgb\(242,\s?80,\s?34\)'),
156 regex.icontains(body.html.raw, 'rgb\(127,\s?186,\s?0\)'),
157 regex.icontains(body.html.raw, 'rgb\(0,\s?164,\s?239\)'),
158 regex.icontains(body.html.raw, 'rgb\(255,\s?185,\s?0\)'),
159 )
160 or 4 of (
161 regex.icontains(body.html.raw, 'rgb\(243,\s?83,\s?37\)'),
162 regex.icontains(body.html.raw, 'rgb\(129,\s?188,\s?6\)'),
163 regex.icontains(body.html.raw, 'rgb\(5,\s?166,\s?240\)'),
164 regex.icontains(body.html.raw, 'rgb\(255,\s?186,\s?8\)')
165 )
166 or 4 of (
167 regex.icontains(body.html.raw, 'rgb\(243,\s?80,\s?34\)'),
168 regex.icontains(body.html.raw, 'rgb\(128,\s?187,\s?3\)'),
169 regex.icontains(body.html.raw, 'rgb\(3,\s?165,\s?240\)'),
170 regex.icontains(body.html.raw, 'rgb\(255,\s?185,\s?3\)')
171 )
172 or 4 of (
173 regex.icontains(body.html.raw,
174 '(background-color:|background:|bgcolor=)(.)?(#)?(FF1940|eb5024|F25022|FF1941|red)'
175 ),
176 regex.icontains(body.html.raw,
177 '(background-color:|background:|bgcolor=)(.)?(#)?(36ba57|3eb55d|7db606|7FBA00|36ba58|green)'
178 ),
179 regex.icontains(body.html.raw,
180 '(background-color:|background:|bgcolor=)(.)?#(04a1d6|04B5F0|05a1e8|00A4EF|01a4ef|04a5f0)'
181 ),
182 regex.icontains(body.html.raw,
183 '(background-color:|background:|bgcolor=)(.)?#(FFCA07|f7b408|FFB900|FFCA08|ffb901|ffba07)'
184 ),
185 )
186 or 4 of (
187 regex.icontains(body.html.raw,
188 '(background-color:|background:|bgcolor=)(.)?#(f65314|f65d35|49a1e8|E74F23|F35325)'
189 ),
190 regex.icontains(body.html.raw,
191 '(background-color:|background:|bgcolor=)(.)?#(7cbf42|81bb05|e05c35|7AB206|81BC06)'
192 ),
193 regex.icontains(body.html.raw,
194 '(background-color:|background:|bgcolor=)(.)?#(00a4ef|0078d7|8bb737|04a5f0|059EE4|05A6F0)'
195 ),
196 regex.icontains(body.html.raw,
197 '(background-color:|background:|bgcolor=)(.)?#(ffb900|ffba07|f4bc41|F2B108|FFBA08)'
198 ),
199 )
200 // fuzzy approach
201 or 4 of (
202 regex.icontains(body.html.raw,
203 'rgb\((2[1-4][0-9]|250),\s?(7[0-9]|8[0-9]|9[0-3]),\s?(3[0-9]|4[0-9]|5[0-3])\)'
204 ),
205 regex.icontains(body.html.raw,
206 'rgb\((12[0-9]|13[0-9]),\s?(18[0-9]|190),\s?([0-9]|10)\)'
207 ),
208 regex.icontains(body.html.raw,
209 'rgb\(([0-9]|1[0-5]),\s?(16[0-5]|166),\s?(23[0-9]|240)\)'
210 ),
211 regex.icontains(body.html.raw,
212 'rgb\((25[0-5]),\s?(18[5-9]|19[0-9]),\s?([0-9]|10)\)'
213 )
214 )
215 )
216
217 // suspicious content
218 and (
219 // current thread is empty, but an image attachment is branded as microsoft.
220 // common in image as content
221 (
222 body.current_thread.text == ""
223 and any(attachments,
224 .file_type in $file_types_images
225 and any(ml.logo_detect(.).brands,
226 strings.starts_with(.name, "Microsoft")
227 )
228 )
229 )
230 or (
231 strings.ilike(body.plain.raw,
232 "*password*",
233 "*document*",
234 "*voicemail*",
235 "*cache*",
236 "*fax*",
237 "*storage*",
238 "*quota*",
239 "*message*"
240 )
241 and strings.ilike(body.plain.raw,
242 "*terminated*",
243 "*review*",
244 "*expire*",
245 "*click*",
246 "*view*",
247 "*exceed*",
248 "*clear*",
249 "*only works*",
250 "*failed*",
251 "*deleted*",
252 "*revalidated*",
253 "*renewal*"
254 )
255 )
256
257 or (
258 any(attachments,
259 .file_type in $file_types_images
260 and any(file.explode(.),
261 strings.ilike(.scan.ocr.raw,
262 "*password*",
263 "*document*",
264 "*voicemail*",
265 "*cache*",
266 "*fax*",
267 "*storage*",
268 "*quota*",
269 "*messages*"
270 )
271 and strings.ilike(.scan.ocr.raw,
272 "*terminated*",
273 "*review*",
274 "*expire*",
275 "*click*",
276 "*view*",
277 "*exceed*",
278 "*clear*",
279 "*only works*",
280 "*failed*",
281 "*deleted*"
282 )
283 )
284 )
285 )
286 or (
287 any(file.explode(beta.message_screenshot()),
288 strings.ilike(.scan.ocr.raw,
289 "*password*",
290 "*document*",
291 "*voicemail*",
292 "*cache*",
293 "*fax*",
294 "*storage*",
295 "*quota*",
296 "*messages*"
297 )
298 and strings.ilike(.scan.ocr.raw,
299 "*terminated*",
300 "*review*",
301 "*expire*",
302 "*click*",
303 "*view*",
304 "*exceed*",
305 "*clear*",
306 "*only works*",
307 "*failed*",
308 "*deleted*",
309 "*revalidated*",
310 "*renewal*"
311 )
312 )
313 )
314 or (
315 any(ml.nlu_classifier(body.current_thread.text).intents,
316 .name == "cred_theft" and .confidence in~ ("medium", "high")
317 )
318 or any(attachments,
319 .file_type in $file_types_images
320 and any(file.explode(.),
321 any(ml.nlu_classifier(.scan.ocr.raw).intents,
322 .name == "cred_theft"
323 and .confidence in ("medium", "high")
324 )
325 )
326 )
327 )
328 )
329 and sender.email.domain.root_domain not in (
330 "bing.com",
331 "microsoft.com",
332 "microsoftonline.com",
333 "microsoftproject.com",
334 "microsoftstoreemail.com",
335 "microsoftsupport.com",
336 "microsoft365.com",
337 "office.com",
338 "office365.com",
339 "onedrive.com",
340 "sharepointonline.com",
341 "yammer.com",
342 )
343
344 // negate legitimate Office 365 bouncebacks
345 and not (
346 length(attachments) > 0
347 and all(attachments,
348 .content_type in ("message/delivery-status", "message/rfc822")
349 )
350 and (sender.email.local_part in ('postmaster', 'mailer-daemon'))
351 )
352
353 // negate Microsoft "welcome to the X group" notifications
354 and not (
355 headers.auth_summary.dmarc.pass
356 and length(attachments) == 6
357 and length(filter(attachments,
358 strings.istarts_with(.file_name, "GuestWelcomeEmail")
359 )
360 ) == 5
361 and length(filter(body.links,
362 (
363 .href_url.domain.domain not in (
364 "outlook.office365.com",
365 "aka.ms",
366 "go.microsoft.com"
367 )
368 )
369 and not .href_url.domain.domain == sender.email.domain.domain
370 )
371 ) == 0
372 and subject.subject == strings.replace_confusables(subject.subject)
373 )
374
375 // negate org domains unless they fail DMARC authentication
376 and (
377 (
378 sender.email.domain.root_domain in $org_domains
379 and (
380 not headers.auth_summary.dmarc.pass
381 // MS quarantine digest emails from an org domain are router "internally" to MS, therefore, there is no authentication information
382 or not (
383 headers.auth_summary.dmarc.pass is null
384 and all(headers.domains,
385 .root_domain in ("outlook.com", "office365.com")
386 )
387 // typical emails from freemail Outlook accounts are from prod.outlook.com
388 and strings.ends_with(headers.message_id, "protection.outlook.com>")
389 )
390 )
391 )
392 or sender.email.domain.root_domain not in $org_domains
393 )
394
395 // negate highly trusted sender domains unless they fail DMARC authentication
396 and (
397 (
398 sender.email.domain.root_domain in $high_trust_sender_root_domains
399 and not headers.auth_summary.dmarc.pass
400 )
401 or sender.email.domain.root_domain not in $high_trust_sender_root_domains
402 )
403 and (
404 not profile.by_sender().solicited
405 or (
406 profile.by_sender().any_messages_malicious_or_spam
407 and not profile.by_sender().any_messages_benign
408 )
409 )
410 and not profile.by_sender().any_messages_benign
411
412 // exclude marketing jargon from ms partners
413 and not regex.icontains(body.current_thread.text,
414 '(schedul(e|ing)|set up).{0,20}(call|meeting|demo|zoom|conversation|time|tool|discussion)|book.{0,10}(meeting|demo|call|slot|time)|connect.{0,12}(with me|phone|email)|my.{0,10}(calendar|cal)|reserve.{0,10}s[pl]ot|break the ice|want to know more?|miss your chance|if you no longer wish|if you no longer want|if you wish to opt out|low-code (development|approach|solution|journey|platform)|(?:invite|virtual).{0,30}(webinar|presentation)'
415 )
416attack_types:
417 - "Credential Phishing"
418tactics_and_techniques:
419 - "Free file host"
420 - "Image as content"
421 - "Impersonation: Brand"
422 - "Social engineering"
423detection_methods:
424 - "Computer Vision"
425 - "Content analysis"
426 - "File analysis"
427 - "Header analysis"
428 - "Natural Language Understanding"
429 - "Optical Character Recognition"
430 - "Sender analysis"
431 - "URL analysis"
432id: "b59201b6-f253-55a6-9c0a-e1500a32a751"