Attachment: Encrypted PDF with credential theft body

Attached PDF is encrypted, and email body contains credential theft language. Seen in-the-wild impersonating e-fax services.

Sublime rule (View on GitHub)

  1name: "Attachment: Encrypted PDF with credential theft body"
  2description: "Attached PDF is encrypted, and email body contains credential theft language. Seen in-the-wild impersonating e-fax services."
  3type: "rule"
  4severity: "medium"
  5source: |
  6  type.inbound
  7  and any(filter(attachments, .file_type == "pdf"),
  8          any(file.explode(.),
  9              any(.scan.exiftool.fields, .key == "Encryption")
 10              or (
 11                .scan.entropy.entropy > 7
 12                and any(.scan.strings.strings, strings.icontains(., "/Encrypt"))
 13              )
 14          )
 15          and (
 16            // Encrypted PDFs do not have child nodes with any data
 17            all(filter(file.explode(.), .depth > 0), .size == 0)
 18            // if we cracked the password, it will have child objects
 19            or (
 20              any(file.explode(.), 'pdf_encryption_dict' in .flavors.yara)
 21              and any(file.explode(.),
 22                      .scan.encrypted_pdf.cracked_password is not null
 23              )
 24            )
 25          )
 26  )
 27  and (
 28    any(ml.nlu_classifier(body.current_thread.text).intents,
 29        .name == "cred_theft" and .confidence in ("medium", "high")
 30    )
 31    or any(ml.nlu_classifier(beta.ocr(file.message_screenshot()).text).intents,
 32           .name == "cred_theft" and .confidence in ("medium", "high")
 33    )
 34    or regex.icontains(strings.replace_confusables(body.current_thread.text),
 35                       'PDF\s*(?:Access|Preview|Unlock|Decrypt|pass(?:code|word))',
 36                       '(Access|Preview|Unlock|Decrypt|Pass)\s*(?:word|code)\s*(?:\S+\s+){0,3}PDF\s*is?\s*:',
 37                       'Th(?:is|e)\s+(?:file|document|pdf)\s+is\s+(?:pin|password)[-\s]?\s*protected[[:punct:]]\s*The\s+p(?:in|assword)\s+is\s*:?',
 38                       '(?:Access|Preview|Unlock|Decrypt|open)\s+(?:\S+\s+){0,3}(?:PDF|statement)\s*(?:\S+\s+){0,3}(?:pass(?:word|code)|code:|\s*with\s+\S+)',
 39                       'The\s+(?:file|document|pdf|attachment)\s+(?:pass(?:code|word)|access\s*code)\s+is\s*',
 40                       '(?:pass(?:code|word)|pin|access\s*code)\s+to\s+(?:access|open|view)\s+(?:\S+\s+){0,3}is\s+\S*[0-9]\b'
 41    )
 42    or (
 43      (
 44        length(body.current_thread.text) <= 10
 45        or (body.current_thread.text is null)
 46      )
 47      and any(body.previous_threads,
 48              regex.icontains(strings.replace_confusables(.text),
 49                              'PDF\s*(?:Access|Preview|Unlock|Decrypt|pass(?:code|word))',
 50                              '(Access|Preview|Unlock|Decrypt|Pass)\s*(?:word|code)\s*(?:\S+\s+){0,3}PDF\s*is?\s*:',
 51                              'Th(?:is|e)\s+(?:file|document|pdf)\s+is\s+(?:pin|password)[-\s]?\s*protected[[:punct:]]\s*The\s+p(?:in|assword)\s+is\s*:?',
 52                              '(?:Access|Preview|Unlock|Decrypt|open)\s+(?:\S+\s+){0,3}(?:PDF|statement)\s*(?:\S+\s+){0,3}(?:pass(?:word|code)|code:|\s*with\s+\S+)',
 53                              'The\s+(?:file|document|pdf|attachment)\s+(?:pass(?:code|word)|access\s*code)\s+is\s*',
 54                              '(?:pass(?:code|word)|pin|access\s*code)\s+to\s+(?:access|open|view)\s+(?:\S+\s+){0,3}is\s+\S*[0-9]\b'
 55              )
 56      )
 57    )
 58  )
 59  // not forwards/replies
 60  and not (
 61    (length(headers.references) > 0 or headers.in_reply_to is not null)
 62    and (subject.is_forward or subject.is_reply)
 63    and length(body.previous_threads) >= 1
 64  )
 65  and (
 66    (
 67      profile.by_sender_email().prevalence in ("new", "outlier")
 68      and not profile.by_sender_email().solicited
 69    )
 70    or (
 71      profile.by_sender_email().any_messages_malicious_or_spam
 72      and not profile.by_sender_email().any_messages_benign
 73    )
 74    or (
 75      length(recipients.to) == 0
 76      or all(recipients.to,
 77             strings.ilike(.display_name, "undisclosed?recipients")
 78      )
 79    )
 80    or (
 81      length(recipients.to) == 1
 82      and any(recipients.to, .email.email == sender.email.email)
 83    )
 84  )
 85  // negate highly trusted sender domains unless they fail DMARC authentication
 86  and not (
 87    sender.email.domain.root_domain in $high_trust_sender_root_domains
 88    and coalesce(headers.auth_summary.dmarc.pass, false)
 89  )  
 90attack_types:
 91  - "Credential Phishing"
 92tactics_and_techniques:
 93  - "Encryption"
 94  - "Evasion"
 95  - "PDF"
 96  - "Social engineering"
 97detection_methods:
 98  - "Content analysis"
 99  - "Exif analysis"
100  - "File analysis"
101  - "Natural Language Understanding"
102  - "Sender analysis"
103id: "c9596c9a-0465-5364-8523-542e6d25a8f7"
to-top