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