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        )
 85        and not .href_url.domain.root_domain in ("docusign.com", "docusign.net")
 86        and not (
 87          .href_url.domain.root_domain == "mimecastprotect.com"
 88          and (
 89            .href_url.query_params is not null
 90            and (
 91              regex.icontains(.href_url.query_params,
 92                              'domain=(?:\w+.)?docusign.net'
 93              )
 94              or regex.icontains(.href_url.query_params,
 95                                 'domain=(?:\w+.)?docusign.com'
 96              )
 97            )
 98          )
 99        )
100    )
101    // Suspicious attachment
102    or any(attachments,
103           (
104             .file_extension in~ ("html", "htm", "shtml", "dhtml")
105             or .file_extension in~ $file_extensions_common_archives
106             or .file_type == "html"
107             or .content_type == "text/html"
108           )
109           and 1 of (
110             (
111               regex.icontains(file.parse_html(.).raw, '\s{0,}<script.*')
112               and regex.icontains(file.parse_html(.).raw, "</script>")
113             ),
114             strings.ilike(file.parse_html(.).raw,
115                           "*createElement*",
116                           "*appendChild*",
117                           "*createObjectURL*"
118             ),
119             strings.icount(file.parse_html(.).raw, "/*") > 10,
120             any($free_subdomain_hosts, strings.icontains(..file_name, .))
121           )
122    )
123  )
124  
125  // negate highly trusted sender domains unless they fail DMARC authentication
126  and (
127    coalesce(sender.email.domain.root_domain in $high_trust_sender_root_domains
128             and not headers.auth_summary.dmarc.pass,
129             false
130    )
131    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
132  )
133  
134  // adding negation for messages originating from docusigns api
135  // and the sender.display.name contains "via"
136  and not (
137    any(headers.hops,
138        any(.fields,
139            .name == "X-Api-Host" and strings.ends_with(.value, "docusign.net")
140        )
141    )
142    and strings.contains(sender.display_name, "via")
143  )
144  and (
145    not profile.by_sender().solicited
146    or (
147      profile.by_sender().any_messages_malicious_or_spam
148      and not profile.by_sender().any_false_positives
149    )
150  )  
151attack_types:
152  - "Credential Phishing"
153tactics_and_techniques:
154  - "Impersonation: Brand"
155  - "Lookalike domain"
156  - "Social engineering"
157  - "Spoofing"
158detection_methods:
159  - "Header analysis"
160  - "Sender analysis"
161  - "URL analysis"
162id: "4d29235c-08b9-5f9b-950e-60b05c4691fb"
to-top