Sharepoint Link Likely Unrelated to Sender

Detects when a sender links to a Sharepoint file where the subdomain significantly differs from the sender's domain. The rule checks for OneNote, PDF, or unknown file types and includes various domain validation checks.

Sublime rule (View on GitHub)

  1name: "Sharepoint Link Likely Unrelated to Sender"
  2description: "Detects when a sender links to a Sharepoint file where the subdomain significantly differs from the sender's domain. The rule checks for OneNote, PDF, or unknown file types and includes various domain validation checks."
  3type: "rule"
  4severity: "medium"
  5source: |
  6  type.inbound
  7  and any(beta.ml_topic(body.html.display_text).topics,
  8          .name == "File Sharing and Cloud Services" and .confidence == "high"
  9  )
 10  and 0 < length(body.links) < 10
 11  and length(body.html.display_text) < 2000
 12  and any(filter(body.links, .href_url.domain.root_domain == 'sharepoint.com'),
 13          // Normalize Levenshtein distance by string length (0 = identical, 0.7+ = different)
 14          // Working with what we have in MQL, considering we dont have max() or any other forms of string distancing
 15          (
 16            (
 17              strings.iends_with(.href_url.domain.subdomain,
 18                                 '-my'
 19              ) // common Sharepoint subdomain suffix
 20              and (
 21                (
 22                  strings.ilevenshtein(.href_url.domain.subdomain,
 23                                       sender.email.domain.sld
 24                  ) - 3 // subtract aforementioned suffix for more accurate calculation
 25                ) / (
 26                  (
 27                    (length(.href_url.domain.subdomain) - 3) + length(sender.email.domain.sld
 28                    )
 29   + (
 30                      (
 31                        (length(.href_url.domain.subdomain) - 3) - length(sender.email.domain.sld
 32                        )
 33                      ) + (
 34                        length(sender.email.domain.sld) - (
 35                          length(.href_url.domain.subdomain) - 3
 36                        )
 37                      )
 38                    )
 39                  ) / 2.0 // to ensure we keep the result as a float
 40                )
 41              ) > 0.7 // customizable threshold
 42            )
 43            or (
 44              not strings.iends_with(.href_url.domain.subdomain,
 45                                     '-my'
 46              ) // no suffix, continue with original calculation
 47              and (
 48                strings.ilevenshtein(.href_url.domain.subdomain,
 49                                     sender.email.domain.sld
 50                ) / (
 51                  (
 52                    length(.href_url.domain.subdomain) + length(sender.email.domain.sld
 53                    )
 54   + (
 55                      (
 56                        length(.href_url.domain.subdomain) - length(sender.email.domain.sld
 57                        )
 58                      ) + (
 59                        length(sender.email.domain.sld) - length(.href_url.domain.subdomain
 60                        )
 61                      )
 62                    )
 63                  ) / 2.0 // to ensure we keep the result as a float
 64                )
 65              ) > 0.7 // customizable threshold
 66            )
 67          )
 68          and not strings.icontains(.href_url.path, sender.email.local_part)
 69          and not any($org_slds, strings.icontains(..href_url.domain.subdomain, .))
 70  
 71          // it is either a OneNote or PDF file, or unknown
 72          and (
 73            strings.icontains(.href_url.path, '/:o:/p')
 74            or strings.icontains(.href_url.path, '/:b:/p')
 75            or strings.icontains(.href_url.path, '/:u:/p')
 76          )
 77  
 78  )
 79  
 80  // a way to negate long threads
 81  // the full thread must be less than 6 times the length of the current thread
 82  and length(body.html.inner_text) < 6 * length(body.current_thread.text)
 83  and sender.email.domain.root_domain not in (
 84    "sharepoint.com",
 85    "sharepointonline.com"
 86  )
 87  
 88  // negate highly trusted sender domains unless they fail DMARC authentication
 89  and (
 90    (
 91      sender.email.domain.root_domain in $high_trust_sender_root_domains
 92      and not headers.auth_summary.dmarc.pass
 93    )
 94    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
 95  )  
 96tags:
 97 - "Attack surface reduction"
 98attack_types:
 99  - "BEC/Fraud"
100  - "Credential Phishing"
101tactics_and_techniques:
102  - "Impersonation: Employee"
103  - "Lookalike domain"
104  - "OneNote"
105  - "PDF"
106  - "Social engineering"
107detection_methods:
108  - "URL analysis"
109  - "Sender analysis"
110  - "Header analysis"
111  - "HTML analysis"
112id: "6870f489-5581-53f0-a6f7-a03e259fb073"

Related rules

to-top