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-Sign&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> </o:p></span></p>\s*){30,}'
65 )
66 or regex.icontains(body.html.raw, '(<p> </p>\s*){7,}')
67 or regex.icontains(body.html.raw, '(<p[^>]*>\s* <br>\s*</p>\s*){5,}')
68 or regex.icontains(body.html.raw, '(<p[^>]*> </p>\s*){7,}')
69 or strings.count(body.html.raw, '  ') > 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*(?: |\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"