Attachment: HTML attachment with login portal indicators

Recursively scans files and archives to detect indicators of login portals implemented in HTML files. This is a known credential theft technique used by threat actors.

Sublime rule (View on GitHub)

  1name: "Attachment: HTML attachment with login portal indicators"
  2description: |
  3    Recursively scans files and archives to detect indicators of login portals implemented in HTML files. This is a known credential theft technique used by threat actors.
  4references:
  5  - "https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/html-file-attachments-still-a-threat/"
  6  - "https://app.any.run/tasks/6bd34bda-91ef-4d13-847c-81d7787dc763/"
  7  - "https://playground.sublimesecurity.com?id=6cd813e4-085b-4229-ad15-d2194cdbb91b"
  8type: "rule"
  9severity: "medium"
 10authors:
 11  - twitter: "ajpc500"
 12source: |
 13  type.inbound
 14  and any(attachments,
 15          (
 16            .file_extension in~ ("html", "htm", "shtml", "dhtml")
 17            or .file_extension in~ $file_extensions_common_archives
 18            or .file_type == "html"
 19          )
 20          and any(file.explode(.),
 21                  // suspicious strings found in javascript
 22                  (
 23                    length(filter(.scan.javascript.strings,
 24                                  strings.ilike(., "*password*", )
 25                           )
 26                    ) >= 2
 27                    and 2 of (
 28                      any(.scan.javascript.strings,
 29                          strings.ilike(., "*incorrect*")
 30                      ),
 31                      any(.scan.javascript.strings, strings.ilike(., "*invalid*")),
 32                      any(.scan.javascript.strings, strings.ilike(., "*login*")),
 33                      any(.scan.javascript.strings, regex.icontains(., "sign.in")),
 34                    )
 35                  )
 36                  or (
 37                    // suspicious strings found outside of javascript, but binexplode'd file still of HTML type
 38                    length(filter(.scan.strings.strings,
 39                                  strings.ilike(., "*password*", )
 40                           )
 41                    ) >= 2
 42                    and 2 of (
 43                      any(.scan.strings.strings, strings.ilike(., "*incorrect*")),
 44                      any(.scan.strings.strings, strings.ilike(., "*invalid*")),
 45                      any(.scan.strings.strings, strings.ilike(., "*login*")),
 46                      any(.scan.strings.strings, strings.ilike(., "*<script>*")),
 47                      any(.scan.strings.strings, regex.icontains(., "sign.in")),
 48                      any(.scan.strings.strings,
 49                          regex.icontains(.,
 50                                          '<title>.[^<]+(Payment|Invoice|Statement|Login|Microsoft|Email|Excel)'
 51                          )
 52                      )
 53                    )
 54                  )
 55                  or 
 56                  // Known phishing obfuscation
 57                  2 of (
 58                    // Enter password
 59                    any(.scan.strings.strings,
 60                        strings.ilike(.,
 61                                      "*&#69;&#110;&#116;&#101;&#114;&#32;&#112;&#97;&#115;&#115;&#119;&#111;&#114;&#100*"
 62                        )
 63                    ),
 64  
 65                    // Forgotten my password
 66                    any(.scan.strings.strings,
 67                        strings.ilike(.,
 68                                      "*&#70;&#111;&#114;&#103;&#111;&#116;&#116;&#101;&#110;&#32;&#109;&#121;&#32;&#112;&#97;&#115;&#115;&#119;&#111;&#114;&#100*"
 69                        )
 70                    ),
 71  
 72                    // Sign in
 73                    any(.scan.strings.strings,
 74                        strings.ilike(.,
 75                                      "*&#83;&#105;&#103;&#110;&#32;&#105;&#110*"
 76                        )
 77                    )
 78                  )
 79          )
 80  )
 81  and (
 82    (
 83      // exclude internal mailers where there is no SPF configured.
 84      // if the sender's root domain is an org domain, we
 85      // ensure there's an SPF pass
 86      // we use root_domain because it's typically subdomains that are misconfigured
 87      sender.email.domain.root_domain in $org_domains
 88      and headers.auth_summary.spf.pass
 89    )
 90    or sender.email.domain.root_domain not in $org_domains
 91  )
 92  
 93  // negate highly trusted sender domains unless they fail DMARC authentication
 94  and (
 95    (
 96      sender.email.domain.root_domain in $high_trust_sender_root_domains
 97      and not headers.auth_summary.dmarc.pass
 98    )
 99    or sender.email.domain.root_domain not in $high_trust_sender_root_domains
100  )
101  and (
102    (
103      not profile.by_sender().solicited
104      and profile.by_sender().prevalence in ("new", "outlier")
105    )
106    or (
107      profile.by_sender().any_messages_malicious_or_spam
108      and not profile.by_sender().any_messages_benign
109    )
110  )  
111attack_types:
112  - "Credential Phishing"
113tactics_and_techniques:
114  - "HTML smuggling"
115  - "Scripting"
116detection_methods:
117  - "Archive analysis"
118  - "File analysis"
119  - "HTML analysis"
120  - "Javascript analysis"
121  - "Sender analysis"
122id: "3aabf4a7-fefa-5266-83fe-012002c9db4a"
to-top