Credential Phishing: Suspicious E-sign Agreement Document Notification

Detects phishing attempts disguised as e-signature requests, characterized by common document sharing phrases, unusual HTML padding, and suspicious link text.

Sublime rule (View on GitHub)

  1name: "Credential Phishing: Suspicious E-sign Agreement Document Notification"
  2description: "Detects phishing attempts disguised as e-signature requests, characterized by common document sharing phrases, unusual HTML padding, and suspicious link text."
  3type: "rule"
  4severity: "medium"
  5source: |
  6  type.inbound
  7  and any([subject.subject, sender.display_name],
  8          regex.icontains(strings.replace_confusables(.),
  9                          "DocuLink",
 10                          "Access.&.Approved",
 11                          "Agreement.{0,5}Review",
 12                          "Attend.and.Review",
 13                          "Completed.File",
 14                          "Dochsared",
 15                          "Docshared",
 16                          "DocsPoint",
 17                          "Document.Shared",
 18                          "DocuCentre",
 19                          "DocuCenter",
 20                          "DocCenter",
 21                          "DocsOnline",
 22                          "DocSend",
 23                          "docsign",
 24                          "Docu-eSin",
 25                          "Docu-management",
 26                          "\\beSign",
 27                          "e\\.sign",
 28                          "esign.online",
 29                          "e-doc",
 30                          "e-signature",
 31                          "eSignature",
 32                          "eSign&Return",
 33                          "eSignOnline",
 34                          "Fileshare",
 35                          "Review.and.Complete",
 36                          "Review.&.Sign",
 37                          "SignOnline",
 38                          "Signature.Request",
 39                          "Shared.Completed",
 40                          "Sign.and.Seal",
 41                          "viaSign",
 42                          "D0cuSign",
 43                          "DocsID",
 44                          "Complete.{0,10}DocuSign",
 45                          "Enroll & Sign",
 46                          "Review and Sign",
 47                          "SignReport",
 48                          "SignDoc",
 49                          "Docxxx",
 50                          "docufile",
 51                          "E­-­S­i­g­n­&Return",
 52                          "document.signature",
 53                          "Electronic.?Signature",
 54                          "Complete: ",
 55                          "Please Review",
 56                          "^REVIEW$"
 57          )
 58  )
 59  and (
 60    // unusual repeated patterns in HTML 
 61    regex.icontains(body.html.raw, '((<br\s*/?>\s*){20,}|\n{20,})')
 62    or regex.icontains(body.html.raw, '(<p[^>]*>\s*<br\s*/?>\s*</p>\s*){30,}')
 63    or regex.icontains(body.html.raw,
 64                       '(<p class=".*?"><span style=".*?"><o:p>&nbsp;</o:p></span></p>\s*){30,}'
 65    )
 66    or regex.icontains(body.html.raw, '(<p>&nbsp;</p>\s*){7,}')
 67    or regex.icontains(body.html.raw, '(<p[^>]*>\s*&nbsp;<br>\s*</p>\s*){5,}')
 68    or regex.icontains(body.html.raw, '(<p[^>]*>&nbsp;</p>\s*){7,}')
 69    or strings.count(body.html.raw, '&nbsp;‌&nbsp;‌&nbsp') > 50
 70    or regex.count(body.html.raw,
 71                   '<span\s*class\s*=\s*"[^\"]+"\s*>\s*[a-z]\s*<\/span><span\s*class\s*=\s*"[^\"]+"\s*>\s*[a-z]+\s*<\/span>'
 72    ) > 50
 73    // lookalike docusign
 74    or regex.icontains(body.html.raw, '>Docus[1l]gn<')
 75    or (
 76      regex.icontains(body.html.inner_text, 'Document')
 77      and length(body.html.inner_text) < 300
 78    )
 79    // common greetings via email.local_part
 80    or any(recipients.to,
 81           // use count to ensure the email address is not part of a disclaimer
 82           strings.icount(body.current_thread.text, .email.local_part) > 
 83           // sum allows us to add more logic as needed
 84           sum([
 85                 strings.icount(body.current_thread.text,
 86                                strings.concat('was sent to ', .email.email)
 87                 ),
 88                 strings.icount(body.current_thread.text,
 89                                strings.concat('intended for ', .email.email)
 90                 )
 91               ]
 92           )
 93    )
 94    // Abnormally high count of mailto links in raw html
 95    or regex.count(body.html.raw,
 96                   'mailto:[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}'
 97    ) > 50
 98  
 99    // High count of empty elements (padding) 
100    or regex.count(body.html.raw,
101                   '<(?:p|div|span|td)[^>]*>\s*(?:&nbsp;|\s)*\s*</(?:p|div|span|td)>'
102    ) > 30
103  
104    // HR impersonation
105    or strings.ilike(sender.display_name, "HR", "H?R", "*Human Resources*")
106  )
107  and (
108    any(body.links,
109        regex.icontains(.display_text,
110                        "activate",
111                        "re-auth",
112                        "verify",
113                        "acknowledg",
114                        "(keep|change).{0,20}(active|password|access)",
115                        '((verify|view|click|download|goto|keep|Vιew|release).{0,15}(attachment|current|download|fax|file|document|message|same)s?)',
116                        'use.same.pass',
117                        'validate.{0,15}account',
118                        'recover.{0,15}messages',
119                        '(retry|update).{0,10}payment',
120                        'check activity',
121                        '(listen|play).{0,10}(vm|voice)',
122                        'clarify.{0,20}(deposit|wallet|funds)',
123                        'enter.{0,15}teams',
124                        'Review and sign',
125                        'REVIEW.*DOCUMENT'
126        )
127    )
128    // check that the display_text is all lowercase
129    or any(body.links,
130           (
131             regex.contains(.display_text,
132                            "\\bVIEW",
133                            "DOWNLOAD",
134                            "CHECK",
135                            "KEEP.(SAME|MY)",
136                            "VERIFY",
137                            "ACCESS\\b",
138                            "SIGN\\b",
139                            "ENABLE\\b",
140                            "RETAIN",
141                            "PLAY",
142                            "LISTEN",
143             )
144             and regex.match(.display_text, "^[^a-z]*[A-Z][^a-z]*$")
145           )
146    )
147    or (
148      length(attachments) > 0
149      and any(attachments,
150              (
151                regex.icontains(beta.ocr(.).text,
152                                "activate",
153                                "re-auth",
154                                "verify",
155                                "acknowledg",
156                                "(keep|change).{0,20}(active|password|access)",
157                                '((verify|view|click|download|goto|keep|Vιew|release).{0,15}(attachment|current|download|fax|file|document|message|same)s?)',
158                                'use.same.pass',
159                                'validate.{0,15}account',
160                                'recover.{0,15}messages',
161                                '(retry|update).{0,10}payment',
162                                'check activity',
163                                '(listen|play).{0,10}(vm|voice)',
164                                'clarify.{0,20}(deposit|wallet|funds)',
165                                'enter.{0,15}teams',
166                                'Review and sign'
167                )
168              )
169              or (
170                any(file.explode(.),
171                    regex.icontains(.scan.ocr.raw,
172                                    "activate",
173                                    "re-auth",
174                                    "verify",
175                                    "acknowledg",
176                                    "(keep|change).{0,20}(active|password|access)",
177                                    '((verify|view|click|download|goto|keep|Vιew|release).{0,15}(attachment|current|download|fax|file|document|message|same)s?)',
178                                    'use.same.pass',
179                                    'validate.{0,15}account',
180                                    'recover.{0,15}messages',
181                                    '(retry|update).{0,10}payment',
182                                    'check activity',
183                                    '(listen|play).{0,10}(vm|voice)',
184                                    'clarify.{0,20}(deposit|wallet|funds)',
185                                    'enter.{0,15}teams',
186                                    'Review and sign'
187                    )
188                )
189              )
190      )
191    )
192  )
193  and (
194    not profile.by_sender_email().solicited
195    or profile.by_sender_email().prevalence == "new"
196    or (
197      profile.by_sender_email().any_messages_malicious_or_spam
198      and not profile.by_sender_email().any_messages_benign
199    )
200  )
201  and not profile.by_sender_email().any_messages_benign
202  
203  // negate replies/fowards containing legitimate docs
204  and not (
205    length(headers.references) > 0
206    or any(headers.hops, any(.fields, strings.ilike(.name, "In-Reply-To")))
207  )
208  
209  // negate highly trusted sender domains unless they fail DMARC authentication
210  and (
211    (
212      sender.email.domain.root_domain in $high_trust_sender_root_domains
213      and (
214        any(distinct(headers.hops, .authentication_results.dmarc is not null),
215            strings.ilike(.authentication_results.dmarc, "*fail")
216        )
217      )
218    )
219    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
220  )  
221attack_types:
222  - "Credential Phishing"
223tactics_and_techniques:
224  - "Social engineering"
225detection_methods:
226  - "Content analysis"
227  - "Header analysis"
228  - "HTML analysis"
229  - "URL analysis"
230  - "Sender analysis"
231id: "9b68c2d8-951e-5e04-9fa3-2ca67d9226a6"
to-top