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