Brand impersonation: DocuSign

Attack impersonating a DocuSign request for signature.

Sublime rule (View on GitHub)

  1name: "Brand impersonation: DocuSign"
  2description: |
  3    Attack impersonating a DocuSign request for signature.
  4references:
  5  - "https://playground.sublimesecurity.com?id=2d2c6472-fabb-4952-b902-573a6294aa2f"
  6type: "rule"
  7severity: "high"
  8source: |
  9  type.inbound
 10  and (
 11    // orgs can have docusign.company.com
 12    strings.ilike(sender.email.email, '*docusign.net*', '*docusign.com*')
 13  
 14    // if the above is true, you'll see a "via Docusign"
 15    or strings.ilike(sender.display_name, '*docusign*')
 16  
 17    // detects 1 character variations,
 18    // such as DocuSlgn (with an "L" instead of an "I")
 19    or strings.ilevenshtein(sender.display_name, "docusign") == 1
 20    or strings.ilike(sender.display_name, "*docuonline*", "*via *signature*")
 21    or (
 22      strings.istarts_with(body.html.inner_text, "docusign")
 23      and not strings.istarts_with(body.current_thread.text, "docusign")
 24    )
 25    or (
 26      (
 27        regex.icontains(body.html.raw, '<font size="[0-9]"[^\>]*>DocuSign</font>')
 28        or regex.icontains(body.html.raw, '\nDocu(?:<[^\>]+>\s*)+Sign<')
 29        or regex.icontains(body.html.raw,
 30                           '<span[^>]*style="[^"]*">DocuSign<\/span>'
 31        )
 32        or regex.icontains(body.html.raw,
 33                           '<span[^>]*style="[^"]*">(Docu|D(?:ocu?)?)<\/span><span[^>]*style="[^"]*">(Sign|S(?:ign?)?)<\/span>'
 34        )
 35        or regex.icontains(body.html.raw, '<strong>DocuSign</strong>')
 36        or regex.icontains(body.html.raw, '<div class="logo">DocuSign</div>')
 37        or regex.icontains(body.html.raw,
 38                           'D&#917540;&#917540;o&#917540;&#917540;c︀uS&#917540;&#917540;i︀gn'
 39        )
 40      )
 41      and (
 42        regex.icontains(body.html.raw, 'background:\s*rgb\(30,\s*76,\s*161\)')
 43        or regex.icontains(body.html.raw,
 44                           'background-color:\s*rgb\(30,\s*76,\s*161\)'
 45        )
 46        or regex.icontains(body.html.raw,
 47                           'background-color:\s*rgb\(61,\s*170,\s*73\)'
 48        )
 49        or regex.icontains(body.html.raw,
 50                           '<div[^>]*BACKGROUND-COLOR: #1e4ca1[^>]*>',
 51                           '<td[^>]*BACKGROUND-COLOR: #1e4ca1[^>]*>',
 52                           '<table[^>]*background-color:\s*#1e4ca1'
 53        )
 54        or regex.icontains(body.html.raw, 'background-color:#214e9f;')
 55        or regex.icontains(body.html.raw, 'background-color:#3260a7')
 56        or regex.icontains(body.html.raw, 'background-color:#0056b3')
 57        or regex.icontains(body.html.raw, 'BACKGROUND-COLOR:#1e4ca1')
 58        or regex.icontains(body.html.raw, 'background-color:#214395')
 59        or regex.icontains(body.html.raw,
 60                           '<table[^>]*style="[^"]*background:\s*#1E4CA1[^"]*"[^>]*>(.*?)<\/table>'
 61        )
 62        or regex.icontains(body.html.raw, '<title>Document.{0,50}</title>')
 63        or any(body.links, regex.icontains(.display_text, 'view.{0,3}doc'))
 64        or any(body.links, regex.contains(.display_text, '\bDOCUMENT'))
 65      )
 66    )
 67  )
 68  
 69  // identifies the main CTA in the email, eg "Review now" or "Review document"
 70  // this should always be a known docusign domain,
 71  // even with branded docusign subdomains
 72  and (
 73    any(body.links,
 74        // we've observed invisible characters in the display name
 75        // such as U+034f(look carefully): "Revi͏ew Now"
 76        (
 77          strings.ilevenshtein(.display_text, "Review Now") <= 3
 78          or (
 79            strings.icontains(.display_text, "Review")
 80            and not strings.icontains(.display_text, "Review Us")
 81          )
 82          or strings.icontains(.display_text, "Now")
 83          or strings.icontains(.display_text, "document")
 84          or strings.icontains(.display_text, "docusign")
 85        )
 86        and not .href_url.domain.root_domain in ("docusign.com", "docusign.net")
 87        and not (
 88          .href_url.domain.root_domain == "mimecastprotect.com"
 89          and (
 90            .href_url.query_params is not null
 91            and (
 92              regex.icontains(.href_url.query_params,
 93                              'domain=(?:\w+.)?docusign.net'
 94              )
 95              or regex.icontains(.href_url.query_params,
 96                                 'domain=(?:\w+.)?docusign.com'
 97              )
 98            )
 99          )
100        )
101    )
102    // Suspicious attachment
103    or any(attachments,
104           (
105             .file_extension in~ ("html", "htm", "shtml", "dhtml")
106             or .file_extension in~ $file_extensions_common_archives
107             or .file_type == "html"
108             or .content_type == "text/html"
109           )
110           and 1 of (
111             (
112               regex.icontains(file.parse_html(.).raw, '\s{0,}<script.*')
113               and regex.icontains(file.parse_html(.).raw, "</script>")
114             ),
115             strings.ilike(file.parse_html(.).raw,
116                           "*createElement*",
117                           "*appendChild*",
118                           "*createObjectURL*"
119             ),
120             strings.icount(file.parse_html(.).raw, "/*") > 10,
121             any($free_subdomain_hosts, strings.icontains(..file_name, .))
122           )
123    )
124  )
125  
126  // negate highly trusted sender domains unless they fail DMARC authentication
127  and (
128    coalesce(sender.email.domain.root_domain in $high_trust_sender_root_domains
129             and not headers.auth_summary.dmarc.pass,
130             false
131    )
132    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
133  )
134  
135  // adding negation for messages originating from docusigns api
136  // and the sender.display.name contains "via"
137  and not (
138    any(headers.hops,
139        any(.fields,
140            .name == "X-Api-Host" and strings.ends_with(.value, "docusign.net")
141        )
142    )
143    and strings.contains(sender.display_name, "via")
144  )
145  and (
146    not profile.by_sender().solicited
147    or (
148      profile.by_sender().any_messages_malicious_or_spam
149      and not profile.by_sender().any_false_positives
150    )
151  )  
152attack_types:
153  - "Credential Phishing"
154tactics_and_techniques:
155  - "Impersonation: Brand"
156  - "Lookalike domain"
157  - "Social engineering"
158  - "Spoofing"
159detection_methods:
160  - "Header analysis"
161  - "Sender analysis"
162  - "URL analysis"
163id: "4d29235c-08b9-5f9b-950e-60b05c4691fb"
to-top