Link: Suspicious SharePoint document name

The detection rule is intended to match on emails sent from SharePoint indicating a shared file to the recipient that contain suspicious content within the document name. The Link display text is leveraged to identify the name of the shared file.

Sublime rule (View on GitHub)

  1name: "Link: Suspicious SharePoint document name"
  2description: "The detection rule is intended to match on emails sent from SharePoint indicating a shared file to the recipient that contain suspicious content within the document name.  The Link display text is leveraged to identify the name of the shared file."
  3type: "rule"
  4severity: "low"
  5source: |
  6  type.inbound
  7  and strings.ilike(subject.subject, "*shared*", "*invit*")
  8  and strings.ilike(body.current_thread.text,
  9                    "*shared a file with you*",
 10                    "*shared with you*",
 11                    "*invited you to access a file*"
 12  )
 13  and not strings.ilike(body.current_thread.text, "invited you to edit")
 14  and (
 15    // use the display text of the link to determine the name of the file
 16    any(filter(body.links,
 17               (
 18                 .href_url.domain.root_domain == "sharepoint.com"
 19                 or .href_url.domain.root_domain == "1drv.ms"
 20                 // handle urls with mimecast rewriting
 21                 or (
 22                   .href_url.domain.root_domain == 'mimecastprotect.com'
 23                   and strings.icontains(.href_url.query_params,
 24                                         '.sharepoint.com'
 25                   )
 26                 )
 27               )
 28               and .display_text != "Open"
 29               and not .href_url.domain.domain in $tenant_domains
 30        ),
 31  
 32        // the file name does not include lowercase letters, while allowing for non letter chars
 33        regex.match(.display_text, '^[^a-z]+$')
 34  
 35        // file sharing service references
 36        or strings.icontains(.display_text, 'dropbox')
 37        or strings.icontains(.display_text, 'docusign')
 38  
 39        // file name lures
 40        // secure theme
 41        or regex.icontains(.display_text, 'secured?.*(?:file|document|docs|fax)')
 42        or regex.icontains(.display_text, 'important.*(?:file|document|docs|fax)')
 43        or regex.icontains(.display_text, 'shared?.*(?:file|document|docs|fax)')
 44        or regex.icontains(.display_text, 'protected.*(?:file|document|docs|fax)')
 45        or regex.icontains(.display_text, 'encrypted.*(?:file|document|docs|fax)')
 46  
 47        // scanner theme
 48        or strings.icontains(.display_text, 'scanne[rd]_')
 49        // image themed
 50        or strings.icontains(.display_text, '_IMG_')
 51        or regex.icontains(.display_text, '^IMG[_-](?:\d|\W)+$')
 52  
 53        // digits
 54        or regex.icontains(.display_text, 'doc(?:ument)?\s?\d+$')
 55        or regex.icontains(.display_text, '^\d+$')
 56  
 57        // onedrive theme
 58        or strings.icontains(.display_text, 'one_docx')
 59        or strings.icontains(.display_text, 'OneDrive')
 60        or regex.icontains(.display_text, 'A document.*One.?Drive')
 61  
 62        // action in file name
 63        or strings.icontains(.display_text, 'click here')
 64        or strings.icontains(.display_text, 'Download PDF')
 65        or strings.icontains(.display_text, 'Validate')
 66        or strings.icontains(.display_text, 'sent you ')
 67  
 68        // limited file name to "confidential"
 69        or .display_text =~ 'Confidentiality'
 70        or .display_text =~ 'Confidential'
 71  
 72        // invoice themes
 73        or any(ml.nlu_classifier(.display_text).entities, .name == "financial")
 74        or strings.icontains(.display_text, 'payment')
 75        or strings.icontains(.display_text, 'invoice')
 76        or regex.icontains(.display_text, 'INV(?:_|\s)?\d+$')
 77        // starts with INV_ or INV\x20
 78        or regex.icontains(.display_text, '^INV(?:_|\s)')
 79        or regex.icontains(.display_text, 'P[O0]\W+?\d+$')
 80        or strings.icontains(.display_text, 'receipt')
 81        or strings.icontains(.display_text, 'billing')
 82        or (
 83          strings.icontains(.display_text, 'statement')
 84          and not .display_text =~ "Privacy Statement"
 85        )
 86        or strings.icontains(.display_text, 'Past Due')
 87        or regex.icontains(.display_text, 'Remit(tance)?')
 88        or strings.icontains(.display_text, 'Purchase Order')
 89  
 90        // contract language
 91        or strings.icontains(.display_text, 'settlement')
 92        or strings.icontains(.display_text, 'contract agreement')
 93        or regex.icontains(.display_text, 'Pr[0o]p[0o]sal')
 94        or strings.icontains(.display_text, 'contract doc')
 95  
 96        // generic document name AND additional suspicious indicator
 97        or (
 98          regex.imatch(.display_text, 'documents?')
 99          and (
100            // Find the share comment in the HTML and check for reply/forward "impersonation"
101            regex.icontains(body.html.raw,
102                            '<p style="font-size:16px;color:#323130;margin:40px 20px 28px">(re|fwd?)'
103            )
104          )
105        )
106  
107        // Payroll/HR
108        // section also used in abuse_dropbox_sus_names.yml with modified input
109        or strings.icontains(.display_text, 'Payroll')
110        or strings.icontains(.display_text, 'Employee Pay\b')
111        or strings.icontains(.display_text, 'Salary')
112        or strings.icontains(.display_text, 'Benefit Enrollment')
113        or strings.icontains(.display_text, 'Employee Handbook')
114        or strings.icontains(.display_text, 'Reimbursement Approved')
115        or regex.icontains(.display_text,
116                           '(?:Faculty|Staff)\s*(?:\w+\s+){0,3}\s*Eval(?:uation)?'
117        )
118  
119        // pattern of `sld - open items`
120        or strings.istarts_with(.display_text,
121                                strings.concat(sender.email.domain.sld, ' - ')
122        )
123    )
124  )
125  and (
126    // and sender has never had email sent to them
127    profile.by_sender().solicited == false
128    // often times no-reply is soliticed due to various behaviors
129    or sender.email.email == "no-reply@sharepointonline.com"
130  )  
131attack_types:
132  - "Credential Phishing"
133tactics_and_techniques:
134  - "Free file host"
135  - "Evasion"
136detection_methods:
137  - "Content analysis"
138id: "f95fee6e-8127-5888-a9a9-4bbeabfe33a3"
to-top