Attachment: EML with Sharepoint link likely unrelated to sender

Detects EML attachments containing SharePoint links where the subdomain differs significantly from the sender's domain, potentially indicating SharePoint impersonation or domain spoofing tactics.

Sublime rule (View on GitHub)

  1name: "Attachment: EML with Sharepoint link likely unrelated to sender"
  2description: "Detects EML attachments containing SharePoint links where the subdomain differs significantly from the sender's domain, potentially indicating SharePoint impersonation or domain spoofing tactics."
  3type: "rule"
  4severity: "medium"
  5source: |
  6  type.inbound
  7  and length(filter(attachments,
  8                    .file_extension == "eml" or .content_type == "message/rfc822"
  9             )
 10  ) == 1
 11  and any(attachments,
 12          any(filter(file.parse_eml(.).body.links,
 13                     .href_url.domain.root_domain == 'sharepoint.com'
 14              ),
 15              // Normalize Levenshtein distance by string length (0 = identical, 0.7+ = different)
 16              // Working with what we have in MQL, considering we dont have max() or any other forms of string distancing
 17              (
 18                (
 19                  strings.iends_with(.href_url.domain.subdomain,
 20                                     '-my'
 21                  ) // common Sharepoint subdomain suffix
 22                  and (
 23                    (
 24                      strings.ilevenshtein(.href_url.domain.subdomain,
 25                                           sender.email.domain.sld
 26                      ) - 3 // subtract aforementioned suffix for more accurate calculation
 27                    ) / (
 28                      (
 29                        (length(.href_url.domain.subdomain) - 3) + length(sender.email.domain.sld
 30                        )
 31   + (
 32                          (
 33                            (length(.href_url.domain.subdomain) - 3) - length(sender.email.domain.sld
 34                            )
 35                          ) + (
 36                            length(sender.email.domain.sld) - (
 37                              length(.href_url.domain.subdomain) - 3
 38                            )
 39                          )
 40                        )
 41                      ) / 2.0 // to ensure we keep the result as a float
 42                    )
 43                  ) > 0.7 // customizable threshold
 44                )
 45                or (
 46                  not strings.iends_with(.href_url.domain.subdomain,
 47                                         '-my'
 48                  ) // no suffix, continue with original calculation
 49                  and (
 50                    strings.ilevenshtein(.href_url.domain.subdomain,
 51                                         sender.email.domain.sld
 52                    ) / (
 53                      (
 54                        length(.href_url.domain.subdomain) + length(sender.email.domain.sld
 55                        )
 56   + (
 57                          (
 58                            length(.href_url.domain.subdomain) - length(sender.email.domain.sld
 59                            )
 60                          ) + (
 61                            length(sender.email.domain.sld) - length(.href_url.domain.subdomain
 62                            )
 63                          )
 64                        )
 65                      ) / 2.0 // to ensure we keep the result as a float
 66                    )
 67                  ) > 0.7 // customizable threshold
 68                )
 69              )
 70              and not strings.icontains(.href_url.path, sender.email.local_part)
 71              and not any($org_slds,
 72                          strings.icontains(..href_url.domain.subdomain, .)
 73              )
 74  
 75              // it is either a OneNote or PDF file, or unknown
 76              and regex.icontains(.href_url.path, '\/:[obu]:\/(?:p|g\/personal)')
 77          )
 78  
 79          // a way to negate long threads
 80          // the full thread must be less than 6 times the length of the current thread
 81          and length(body.html.inner_text) < 6 * length(body.current_thread.text)
 82          and sender.email.domain.root_domain not in (
 83            "sharepoint.com",
 84            "sharepointonline.com"
 85          )
 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  )
 96  // negate instances where proofpoint sends a review of a reported message via analyzer 
 97  and not (
 98    any(headers.domains, .root_domain == "pphosted.com")
 99    and headers.auth_summary.spf.pass
100    and headers.auth_summary.dmarc.pass
101  )   
102
103attack_types:
104  - "BEC/Fraud"
105  - "Credential Phishing"
106tactics_and_techniques:
107  - "Impersonation: Brand"
108  - "Social engineering"
109  - "Evasion"
110detection_methods:
111  - "File analysis"
112  - "URL analysis"
113  - "Header analysis"
114  - "Sender analysis"
115id: "0a4fd31b-3b85-5f84-959c-10d313cc8a4a"
to-top