Brand impersonation: Sharepoint fake file share

This rule detects messages impersonating a Sharepoint file sharing email where no links point to known Microsoft domains.

Sublime rule (View on GitHub)

  1name: "Brand impersonation: Sharepoint fake file share"
  2description: |
  3    This rule detects messages impersonating a Sharepoint file sharing email where no links point to known Microsoft domains.
  4type: "rule"
  5severity: "medium"
  6source: |
  7  type.inbound
  8  
  9  // Sharepoint body content looks like this
 10  and (
 11    (
 12      any([body.current_thread.text, body.plain.raw],
 13          strings.ilike(.,
 14                        "*shared a file with you*",
 15                        "*shared with you*",
 16                        "*invited you to access a file*"
 17          )
 18      )
 19      and (
 20        strings.ilike(subject.subject, "*shared*", "*updated*", "*sign*", "*review*")
 21        or subject.subject is null
 22      )
 23    )
 24    or any([
 25             "Contigo", // Spanish
 26             "Avec vous", // French
 27             "Mit Ihnen", // German
 28             "Con te", // Italian
 29             "Com você", // Portuguese
 30             "Met u", // Dutch
 31             "С вами", // Russian
 32             "与你", // Chinese (Simplified)
 33             "與您", // Chinese (Traditional)
 34             "あなたと", // Japanese
 35             "당신과", // Korean
 36             "معك", // Arabic
 37             "آپ کے ساتھ", // Urdu
 38             "আপনার সাথে", // Bengali
 39             "आपके साथ", // Hindi
 40             "Sizinle", // Turkish // Azerbaijani
 41             "Med dig", // Swedish
 42             "Z tobą", // Polish
 43             "З вами", // Ukrainian
 44             "Önnel", // Hungarian
 45             "Μαζί σας", // Greek
 46             "איתך", // Hebrew
 47             "กับคุณ", // Thai
 48             "Với bạn", // Vietnamese
 49             "Dengan Anda", // Indonesian // Malay
 50             "Nawe", // Swahili
 51             "Cu dumneavoastră", // Romanian
 52             "S vámi", // Czech
 53             "Med deg", // Norwegian
 54             "S vami", // Slovak
 55             "Med dig", // Danish
 56             "Amb vostè", // Catalan
 57             "Teiega", // Estonian
 58             "S vama", // Serbian
 59           ],
 60           strings.icontains(subject.subject, .)
 61    )
 62  )
 63  
 64  // contains logic that impersonates Microsoft
 65  and (
 66    any(ml.logo_detect(beta.message_screenshot()).brands,
 67        strings.starts_with(.name, "Microsoft")
 68    )
 69    or any(attachments,
 70           .file_type in $file_types_images
 71           and any(ml.logo_detect(.).brands,
 72                   strings.starts_with(.name, "Microsoft")
 73           )
 74    )
 75    or (
 76      regex.icontains(body.html.raw,
 77                      '<table[^>]*>\s*<tbody[^>]*>\s*<tr[^>]*>\s*(<td[^>]*bgcolor="#[0-9A-Fa-f]{6}"[^>]*>\s*&nbsp;\s*</td>\s*){2}\s*</tr>\s*<tr[^>]*>\s*(<td[^>]*bgcolor="#[0-9A-Fa-f]{6}"[^>]*>\s*&nbsp;\s*</td>\s*){2}'
 78      )
 79      or 3 of (
 80        regex.icontains(body.html.raw, '.password-expiration'),
 81        regex.icontains(body.html.raw, 'color: #2672ec;'),
 82        regex.icontains(body.html.raw, 'M­ic­ro­so­ft')
 83      )
 84      or 4 of (
 85        regex.icontains(body.html.raw, 'rgb\(246,\s?93,\s?53\)'),
 86        regex.icontains(body.html.raw, 'rgb\(129,\s?187,\s?5\)'),
 87        regex.icontains(body.html.raw, 'rgb\(4,\s?165,\s?240\)'),
 88        regex.icontains(body.html.raw, 'rgb\(255,\s?186,\s?7\)'),
 89      )
 90      or 4 of (
 91        regex.icontains(body.html.raw,
 92                        '(background-color:|background:|bgcolor=)(.)red'
 93        ),
 94        regex.icontains(body.html.raw, 'rgb\(19,\s?186,\s?132\)'),
 95        regex.icontains(body.html.raw, 'rgb\(4,\s?166,\s?240\)'),
 96        regex.icontains(body.html.raw, 'rgb\(255,\s?186,\s?8\)'),
 97      )
 98      or 4 of (
 99        regex.icontains(body.html.raw, 'rgb\(245,\s?189,\s?67\)'),
100        regex.icontains(body.html.raw, 'rgb\(137,\s?184,\s?57\)'),
101        regex.icontains(body.html.raw, 'rgb\(217,\s?83,\s?51\)'),
102        regex.icontains(body.html.raw, 'rgb\(71,\s?160,\s?218\)')
103      )
104      or 4 of (
105        regex.icontains(body.html.raw, 'rgb\(73,\s?161,\s?232\)'),
106        regex.icontains(body.html.raw, 'rgb\(224,\s?92,\s?53\)'),
107        regex.icontains(body.html.raw, 'rgb\(139,\s?183,\s?55\)'),
108        regex.icontains(body.html.raw, 'rgb\(244,\s?188,\s?65\)')
109      )
110      or 4 of (
111        regex.icontains(body.html.raw, 'rgb\(213,\s?56,\s?62\)'),
112        regex.icontains(body.html.raw, 'rgb\(0,\s?114,\s?30\)'),
113        regex.icontains(body.html.raw, 'rgb\(0,\s?110,\s?173\)'),
114        regex.icontains(body.html.raw, 'rgb\(227,\s?209,\s?43\)'),
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, 'rgb\(242,\s?80,\s?34\)'),
124        regex.icontains(body.html.raw, 'rgb\(127,\s?186,\s?0\)'),
125        regex.icontains(body.html.raw, 'rgb\(0,\s?164,\s?239\)'),
126        regex.icontains(body.html.raw, 'rgb\(255,\s?185,\s?0\)'),
127      )
128      or 4 of (
129        regex.icontains(body.html.raw, 'rgb\(243,\s?83,\s?37\)'),
130        regex.icontains(body.html.raw, 'rgb\(129,\s?188,\s?6\)'),
131        regex.icontains(body.html.raw, 'rgb\(5,\s?166,\s?240\)'),
132        regex.icontains(body.html.raw, 'rgb\(255,\s?186,\s?8\)')
133      )
134      or 4 of (
135        regex.icontains(body.html.raw, 'rgb\(243,\s?80,\s?34\)'),
136        regex.icontains(body.html.raw, 'rgb\(128,\s?187,\s?3\)'),
137        regex.icontains(body.html.raw, 'rgb\(3,\s?165,\s?240\)'),
138        regex.icontains(body.html.raw, 'rgb\(255,\s?185,\s?3\)')
139      )
140      or 4 of (
141        regex.icontains(body.html.raw,
142                        '(background-color:|background:|bgcolor=)(.)?(#)?(FF1940|eb5024|F25022|FF1941|red)'
143        ),
144        regex.icontains(body.html.raw,
145                        '(background-color:|background:|bgcolor=)(.)?(#)?(36ba57|3eb55d|7db606|7FBA00|36ba58|green)'
146        ),
147        regex.icontains(body.html.raw,
148                        '(background-color:|background:|bgcolor=)(.)?#(04a1d6|04B5F0|05a1e8|00A4EF|01a4ef|04a5f0)'
149        ),
150        regex.icontains(body.html.raw,
151                        '(background-color:|background:|bgcolor=)(.)?#(FFCA07|f7b408|FFB900|FFCA08|ffb901|ffba07)'
152        ),
153      )
154      or 4 of (
155        regex.icontains(body.html.raw,
156                        '(background-color:|background:|bgcolor=)(.)?#(f65314|f65d35|49a1e8|E74F23|F35325)'
157        ),
158        regex.icontains(body.html.raw,
159                        '(background-color:|background:|bgcolor=)(.)?#(7cbf42|81bb05|e05c35|7AB206|81BC06)'
160        ),
161        regex.icontains(body.html.raw,
162                        '(background-color:|background:|bgcolor=)(.)?#(00a4ef|0078d7|8bb737|04a5f0|059EE4|05A6F0)'
163        ),
164        regex.icontains(body.html.raw,
165                        '(background-color:|background:|bgcolor=)(.)?#(ffb900|ffba07|f4bc41|F2B108|FFBA08)'
166        ),
167      )
168      // fuzzy approach
169      or 4 of (
170        regex.icontains(body.html.raw,
171                        '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])\)'
172        ),
173        regex.icontains(body.html.raw,
174                        'rgb\((12[0-9]|13[0-9]),\s?(18[0-9]|190),\s?([0-9]|10)\)'
175        ),
176        regex.icontains(body.html.raw,
177                        'rgb\(([0-9]|1[0-5]),\s?(16[0-5]|166),\s?(23[0-9]|240)\)'
178        ),
179        regex.icontains(body.html.raw,
180                        'rgb\((25[0-5]),\s?(18[5-9]|19[0-9]),\s?([0-9]|10)\)'
181        )
182      )
183      or 4 of (
184        regex.icontains(body.html.raw, 'rgb\((25[0-5]),\s?(2[0-5]),\s?(6[0-4])\)'),
185        regex.icontains(body.html.raw, 'rgb\((6[0-2]),\s?(18[0-1]),\s?(9[0-3])\)'),
186        regex.icontains(body.html.raw, 'rgb\(([0-4]),\s?(18[0-1]),\s?(24[0])\)'),
187        regex.icontains(body.html.raw, 'rgb\((25[0-5]),\s?(20[0-2]),\s?([0-7])\)')
188      )
189      or (
190        any(recipients.to,
191            strings.icontains(body.current_thread.text,
192                              strings.concat(.email.domain.sld,
193                                             " shared a file with you"
194                              )
195            )
196        )
197      )
198    )
199  )
200  
201  // Negate messages when the message-id indciates the message is from MS actual. DKIM/SPF domains can be custom and therefore are unpredictable.
202  and not (
203    strings.starts_with(headers.message_id, '<Share-')
204    and strings.ends_with(headers.message_id, '@odspnotify>')
205  )
206  
207  // fake Sharepoint shares are easy to identify if there are any links
208  // that don't point to microsoft[.]com or *.sharepoint[.]com
209  and not all(body.links,
210              .href_url.domain.root_domain in (
211                "1drv.ms",
212                "aka.ms",
213                "microsoft.com",
214                "sharepoint.com"
215              )
216  )
217  and sender.email.domain.root_domain not in $org_domains
218  and sender.email.domain.root_domain not in (
219    "bing.com",
220    "microsoft.com",
221    "microsoftonline.com",
222    "microsoftsupport.com",
223    "microsoft365.com",
224    "office.com",
225    "onedrive.com",
226    "sharepointonline.com",
227    "yammer.com",
228    // ignore microsoft privacy statement links
229    "aka.ms"
230  )
231  
232  // negate highly trusted sender domains unless they fail DMARC authentication
233  and (
234    (
235      sender.email.domain.root_domain in $high_trust_sender_root_domains
236      and not headers.auth_summary.dmarc.pass
237    )
238    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
239  )
240  and (
241    (not profile.by_sender().solicited)
242    or (
243      profile.by_sender().any_messages_malicious_or_spam
244      and not profile.by_sender().any_false_positives
245    )
246  )
247  and not profile.by_sender().any_false_positives  
248attack_types:
249  - "Credential Phishing"
250  - "Malware/Ransomware"
251detection_methods:
252  - "Content analysis"
253  - "Header analysis"
254  - "URL analysis"
255  - "Computer Vision"
256tactics_and_techniques:
257  - "Impersonation: Brand"
258  - "Social engineering"
259id: "ff8b296b-aa0d-5df0-b4d2-0e599b688f6a"
to-top