Skip to content

Commit eeeeb5b

Browse files
author
Sublime Rule Testing Bot
committed
Sync from PR#2689
Update impersonation_docusign.yml by @zoomequipd #2689 Source SHA 68063a7 Triggered by @zoomequipd
1 parent 0787858 commit eeeeb5b

File tree

1 file changed

+23
-0
lines changed

1 file changed

+23
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: "Brand impersonation: DocuSign"
2+
description: |
3+
Attack impersonating a DocuSign request for signature.
4+
references:
5+
- "https://playground.sublimesecurity.com?id=2d2c6472-fabb-4952-b902-573a6294aa2f"
6+
type: "rule"
7+
severity: "high"
8+
source: "type.inbound\nand (\n // orgs can have docusign.company.com\n strings.ilike(sender.email.email, '*docusign.net*', '*docusign.com*')\n\n // if the above is true, you'll see a \"via Docusign\"\n or strings.ilike(sender.display_name, '*docusign*')\n\n // detects 1 character variations,\n // such as DocuSlgn (with an \"L\" instead of an \"I\")\n or strings.ilevenshtein(sender.display_name, \"docusign\") == 1\n or strings.ilike(sender.display_name, \"*docuonline*\", \"*via *signature*\")\n or (\n strings.istarts_with(body.html.inner_text, \"docusign\")\n and not strings.istarts_with(body.current_thread.text, \"docusign\")\n )\n // docusign is found in current thread AND contains docusign wording within current_thread or subject\n or (\n regex.icontains(body.current_thread.text, '\\bdocu.?sign\\b')\n and (\n // additional context from body.current_thread.text\n strings.istarts_with(body.current_thread.text, \"DOCUSIGN\\n\", )\n or regex.icontains(body.current_thread.text,\n 'You have received a ([^\\s]+\\s)?document',\n )\n or strings.icontains(body.current_thread.text,\n 'a document to review and sign',\n )\n or strings.icontains(body.current_thread.text,\n 'A document is available for you',\n )\n or strings.icontains(body.current_thread.text,\n 'a document ready for you',\n )\n or strings.icontains(body.current_thread.text,\n 'This email contains a secure link to DocuSign'\n )\n or strings.icontains(body.current_thread.text,\n 'All parties have completed with Docusign'\n )\n or strings.icontains(body.current_thread.text,\n 'the signing of this document has been completed'\n )\n or strings.icontains(body.current_thread.text,\n 'Please use the link above to Docusign'\n )\n or strings.icontains(body.current_thread.text, 'Review on Docusign')\n or strings.icontains(body.current_thread.text, 'Completed with Docusign')\n or strings.icontains(body.current_thread.text, 'Completed on Docusign')\n or strings.icontains(body.current_thread.text, 'Complete with Docusign')\n or strings.icontains(body.current_thread.text,\n 'please review and complete with DocuSign'\n )\n or strings.icontains(body.current_thread.text,\n 'We appreciate you choosing DocuSign'\n )\n or strings.icontains(body.current_thread.text,\n 'A document has been sent to you for'\n )\n or strings.icontains(body.current_thread.text, 'Please Sign docusign')\n or strings.icontains(body.current_thread.text, 'This email was sent via DocuSign')\n or strings.icontains(body.current_thread.text, 'review via DocuSign Electronic Signature')\n or strings.icontains(body.current_thread.text, 'sent to you by DocuSign')\n or strings.icontains(body.current_thread.text, 'Processed by DocuSign')\n or strings.icontains(body.current_thread.text,\n 'Please read and sign the document'\n )\n or strings.icontains(body.current_thread.text,\n 'Please kindly review and sign the '\n )\n or strings.icontains(body.current_thread.text,\n 'Your document is pending review and signature'\n )\n or strings.icontains(body.current_thread.text,\n 'pending document for your signature'\n )\n or strings.icontains(body.current_thread.text,\n 'your review and signature'\n )\n or strings.icontains(body.current_thread.text, 'a pending document for')\n or strings.icontains(body.current_thread.text, 'Your document is ready')\n or strings.icontains(body.current_thread.text,\n 'This email is automatically generated by DocuSign'\n )\n or strings.icontains(body.current_thread.text,\n 'Your document has been completed'\n )\n // docusign is \"near\" review and sign or sign and return\n or regex.icontains(body.current_thread.text,\n 'Review\\s*(?:and\\s*|&\\s*)Sign.{0,40}docusign',\n 'docusign.{0,40}Review\\s*(?:and\\s*|&\\s*)Sign',\n 'Sign\\s*(?:and\\s*|&\\s*)Return.{0,40}docusign',\n 'Sign\\s*(?:and\\s*|&\\s*)Return.docusign.{0,40}'\n )\n\n // additional context from subject.subject\n or strings.icontains(subject.subject, 'complete with docusign')\n or strings.icontains(subject.subject, 'signature request')\n or regex.icontains(subject.subject, 'Review\\s*(?:and\\s*|&\\s*)Sign')\n or regex.icontains(subject.subject, 'Sign\\s*(?:and\\s*|&\\s*)Return')\n or strings.icontains(subject.subject, 'complete with docusign')\n or strings.icontains(subject.subject, 'Please Docusign')\n or strings.icontains(subject.subject, 'Docusign has sent')\n )\n )\n or (\n // negate replies/forwards which involve a legit docusign message-id format\n not any(headers.references,\n strings.iends_with(., '@docusign.net')\n and regex.imatch(., '[0-9a-f]{32}@docusign.net')\n )\n and (\n (\n sender.display_name is not null\n and regex.icontains(sender.display_name, '\\bdocu\\b')\n and strings.icontains(sender.display_name, 'sign')\n )\n or (\n subject.subject is not null\n and regex.icontains(subject.subject, '\\bdocu\\b')\n and strings.icontains(subject.subject, 'sign')\n )\n or (\n (\n regex.icontains(body.html.raw,\n 'Powered by.{0,6}(?:\\s*<\\/?[^\\>]+\\>\\s*)+<img[^\\>]+(?:src=\"https:\\/\\/docucdn-a\\.akamaihd\\.net\\/[^\\\"]+email-logo.png\"|alt=\"DocuSign\")'\n )\n or regex.icontains(body.current_thread.text, 'Powered by\\s*DocuSign')\n )\n // limit it to where the powered by is within the current thread\n and strings.icontains(body.current_thread.text, 'Powered by')\n )\n // footer disclaimers\n or strings.icontains(body.current_thread.text,\n 'using the Docusign Electronic Signature Service'\n )\n or strings.icontains(body.current_thread.text,\n 'who uses the DocuSign Electronic Signature Service'\n )\n or strings.icontains(body.current_thread.text,\n 'Thank you for choosing DocuSign'\n )\n or (\n (\n strings.icontains(body.current_thread.text,\n 'Alternate Signing Method'\n )\n or strings.icontains(body.current_thread.text, 'Alternative Access')\n )\n and regex.icontains(body.current_thread.text,\n '(?:Click|Select) ''Access Documents'', and enter '\n )\n )\n or (\n strings.icontains(body.current_thread.text,\n 'Please do not share this email, link, or access code with others'\n )\n and not sender.email.domain.root_domain in (\n \"insuresign.com\",\n \"clixsign.com\",\n \"esignlive.com\",\n \"clickcontracts.com\"\n )\n )\n or (\n strings.icontains(body.current_thread.text, 'Docusign provides a ')\n and strings.icontains(body.current_thread.text,\n 'solution for Digital Transaction Management'\n )\n )\n or strings.icontains(body.current_thread.text,\n 'a secure link to DocuSign'\n )\n\n // footer links\n or (\n length(filter(body.links,\n (\n .href_url.domain.domain == \"support.docusign.com\"\n and strings.contains(.href_url.path, '/articles/')\n )\n or .href_url.domain.domain == \"community.docusign.com\"\n or .href_url.domain.domain == \"protect.docusign.com\"\n or .href_url.domain.domain == \"app.esign.docusign.com\"\n )\n ) >= 2\n // and the display_text for these links are within the current thread\n and (\n strings.icontains(body.current_thread.text, 'Declining to sign')\n or strings.icontains(body.current_thread.text,\n 'Managing notifications'\n )\n or strings.icontains(body.current_thread.text,\n 'How to Sign a Document'\n )\n or strings.icontains(body.current_thread.text,\n 'Docusign Support Center'\n )\n or strings.icontains(body.current_thread.text, 'Report this email')\n or strings.icontains(body.current_thread.text, 'Docusign Community')\n or strings.icontains(body.current_thread.text,\n 'Connect with our support team'\n )\n or strings.icontains(body.current_thread.text, 'Unsubscribe')\n or strings.icontains(body.current_thread.text, 'Manage Preferences')\n )\n )\n )\n )\n or (\n (\n regex.icontains(body.html.raw,\n '<font size=\"?[0-9]\"?[^\\>]*>DocuSign</font>'\n )\n or regex.icontains(body.html.raw, '\\nDocu(?:<[^\\>]+>\\s*)+Sign<')\n or regex.icontains(body.html.raw,\n '<span[^>]*style=\"[^\"]*\">Docu.?Sign<\\/span>'\n )\n\n // \n // This rule makes use of a beta feature and is subject to change without notice\n // using the beta feature in custom rules is not suggested until it has been formally released\n // \n or any(beta.html_xpath(body.html, '//h1').nodes,\n regex.icontains(.display_text, 'Docu.?Sign')\n )\n or regex.icontains(body.html.raw,\n '<span[^>]*style=\"[^\"]*\">(Docu|D(?:ocu?)?)<\\/span>(?:<[^\\>]+\\>){0,2}<span[^>]*style=\"[^\"]*\">(Sign|S(?:ign?)?)<\\/span>'\n )\n // any bold text contains docusign\n or any(beta.html_xpath(body.html, '//strong').nodes,\n regex.imatch(.display_text, 'Docu.?Sign')\n )\n // title starts with Docusign\n or any(beta.html_xpath(body.html, '//title').nodes,\n regex.icontains(.display_text, '^docu.?sign')\n )\n // a div with a class of logo contains the display text of docusign\n or any(beta.html_xpath(body.html, '//div[@class=\"logo\"]').nodes,\n strings.icontains(.display_text, 'Docusign')\n )\n // image contains an alt text of docusign\n or any(beta.html_xpath(body.html, '//img/@alt').nodes, .raw =~ \"docusign\")\n\n // Basic variations with HTML encoding\n // use of regex extract allows \n or any(regex.iextract(body.html.raw,\n '(?:D|&#68;|&#x44;)(?:&#?[0-9a-fA-F]{2,6};|\\s|o|о|&#1086;|&#x43e;)(?:&#?[0-9a-fA-F]{2,6};|\\s|c|с|&#1089;|&#x441;)u(?:&#?[0-9a-fA-F]{2,6};|\\s)?S(?:&#?[0-9a-fA-F]{2,6};|\\s|i|і|&#1110;|&#x456;)(?:&#?[0-9a-fA-F]{2,6};|\\s|g|ɡ|&#609;|&#x261;)(?:n|&#110;|&#x6e;)'\n ),\n .full_match !~ \"docusign\"\n )\n // Common homograph patterns\n or any(regex.iextract(body.html.raw,\n '(?:[DⅮᎠᗞᗡ\U0001D403\U0001D437\U0001D46B\U0001D49F\U0001D4D3\U0001D507\U0001D53B\U0001D56F\U0001D5A3])\\s*(?:[oοоօ0Ооʘ◯])\\s*(?:[cсçҫ¢ϲС])\\s*u\\s*(?:[sѕЅ5$])\\s*(?:[iіІ1l!|])\\s*(?:[gǵġģ9ɡ])\\s*(?:[nոռℼη\U0001D427\U0001D45B\U0001D48F\U0001D4C3\U0001D4F7\U0001D52B\U0001D55F\U0001D593])'\n ),\n .full_match !~ \"docusign\"\n )\n\n // Look for HTML entities for each letter in sequence\n or any(regex.iextract(body.html.raw,\n '(?:D|&#68;|&#x44;)(?:o|о|&#111;|&#x6f;|&#1086;|&#x43e;|&#959;|&#x3bf;)(?:c|с|&#99;|&#x63;|&#1089;|&#x441;|&#1010;|&#231;|&#x67;|&#265;|&#x109;)(?:u|&#117;|&#x75;|&#1091;|&#x443;|&#965;|&#x3c5;)(?:s|&#115;|&#x73;|&#1109;|&#x455;)(?:i|і|&#105;|&#x69;|&#1110;|&#x456;|&#305;|&#x131;)(?:g|&#103;|&#x67;|&#609;|&#x261;|&#287;|&#x11f;)(?:n|&#110;|&#x6e;|&#1085;|&#x43d;|&#951;|&#x3b7;)'\n ),\n .full_match !~ \"docusign\"\n )\n\n // Handle repeated HTML entities and variation selectors (using Unicode class) \n or any(regex.iextract(body.html.raw,\n 'D(?:&#[0-9]{1,7};)*\\p{Mn}*o(?:&#[0-9]{1,7};)*\\p{Mn}*c(?:&#[0-9]{1,7};)*\\p{Mn}*u(?:&#[0-9]{1,7};)*\\p{Mn}*[Ss](?:&#[0-9]{1,7};)*\\p{Mn}*i(?:&#[0-9]{1,7};)*\\p{Mn}*g(?:&#[0-9]{1,7};)*\\p{Mn}*n'\n ),\n .full_match !~ \"docusign\"\n )\n )\n and (\n regex.icontains(body.html.raw,\n 'b(?:ackground(?:-color)?|g?color):\\s*rgb\\(30,\\s*76,\\s*161\\)',\n 'b(?:ackground(?:-color)?|g?color):\\s*rgb\\(61,\\s*170,\\s*73\\)'\n )\n or regex.icontains(body.html.raw,\n '<(?:div|td|table)[^>]*b(?:ackground(?:-color)?|g?color)(?::|=)\\s*\\\"?#1e4ca1[^>]*>',\n )\n or regex.icontains(body.html.raw,\n 'b(?:ackground(?:-color)?|g?color)(?::|=)\\s*\\\"?#(?:214e9f|3260a7|0056b3|1e4ca1|214395|325bb8|3c60ad)'\n )\n )\n )\n)\n\n// identifies the main CTA in the email, eg \"Review now\" or \"Review document\"\n// this should always be a known docusign domain,\n// even with branded docusign subdomains\nand (\n any(body.links,\n // we've observed invisible characters in the display name\n // such as U+034f(look carefully): \"Revi͏ew Now\"\n (\n strings.ilevenshtein(.display_text, \"Review Now\") <= 3\n or strings.ilevenshtein(.display_text, \"Review and Sign\") <= 3\n or (\n strings.icontains(.display_text, \"Review\")\n and not strings.icontains(.display_text, \"Review Us\")\n )\n or strings.icontains(.display_text, \"document\")\n or strings.icontains(.display_text, \"docusign\")\n or strings.icontains(.display_text, \"Review on Docusign\")\n or (strings.icontains(.display_text, \"Sign\") and strings.icontains(.display_text, \"Now\"))\n\n )\n and not .href_url.domain.root_domain in (\"docusign.com\", \"docusign.net\")\n and not (\n .href_url.domain.root_domain == \"mimecastprotect.com\"\n and (\n .href_url.query_params is not null\n and (\n regex.icontains(.href_url.query_params,\n 'domain=(?:\\w+.)?docusign.net'\n )\n or regex.icontains(.href_url.query_params,\n 'domain=(?:\\w+.)?docusign.com'\n )\n )\n )\n )\n )\n // Suspicious attachment\n or any(attachments,\n (\n .file_extension in~ (\"html\", \"htm\", \"shtml\", \"dhtml\")\n or .file_extension in~ $file_extensions_common_archives\n or .file_type == \"html\"\n or .content_type == \"text/html\"\n )\n and 1 of (\n (\n regex.icontains(file.parse_html(.).raw, '\\s{0,}<script.*')\n and regex.icontains(file.parse_html(.).raw, \"</script>\")\n ),\n strings.ilike(file.parse_html(.).raw,\n \"*createElement*\",\n \"*appendChild*\",\n \"*createObjectURL*\"\n ),\n strings.icount(file.parse_html(.).raw, \"/*\") > 10,\n any($free_subdomain_hosts, strings.icontains(..file_name, .))\n )\n )\n)\n\n// negate highly trusted sender domains unless they fail DMARC authentication\nand (\n coalesce(sender.email.domain.root_domain in $high_trust_sender_root_domains\n and not headers.auth_summary.dmarc.pass,\n false\n )\n or sender.email.domain.root_domain not in $high_trust_sender_root_domains\n)\n// negation for messages traversing docusign.net\n// happens with custom sender domains\nand not (\n any(headers.domains, .root_domain == \"docusign.net\")\n and headers.auth_summary.spf.pass\n and headers.auth_summary.dmarc.pass\n)\n\n// adding negation for messages originating from docusigns api\n// and the sender.display.name contains \"via\"\nand not (\n any(headers.hops,\n any(.fields,\n .name == \"X-Api-Host\" and strings.ends_with(.value, \"docusign.net\")\n )\n )\n and strings.contains(sender.display_name, \"via\")\n)\n"
9+
attack_types:
10+
- "Credential Phishing"
11+
tactics_and_techniques:
12+
- "Impersonation: Brand"
13+
- "Lookalike domain"
14+
- "Social engineering"
15+
- "Spoofing"
16+
detection_methods:
17+
- "Header analysis"
18+
- "Sender analysis"
19+
- "URL analysis"
20+
id: "2bfa31c0-8c83-5eb5-8da0-5ffbec7f29dd"
21+
og_id: "4d29235c-08b9-5f9b-950e-60b05c4691fb"
22+
testing_pr: 2689
23+
testing_sha: 68063a7d3eebd3b3b4115a2645f8ede85377b5e8

0 commit comments

Comments
 (0)