Detection rules › Sublime MQL

Attachment: QR code with recipient targeting and special characters

Severity
high
Type
rule
Source
github.com/sublime-security/sublime-rules

Detects messages with QR code in attachments containing special characters in the path that include the recipient's email address in either the URL path or fragment, potentially encoded in base64. The URLs have a simple path structure and may end with suspicious patterns.

Threat classification

Sublime's own taxonomy (not MITRE ATT&CK).

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesQR code, Social engineering, Evasion

Event coverage

Rule body MQL

type.inbound
and length(recipients.to) == 1
and recipients.to[0].email.domain.valid
and any(attachments,
        (
          // Office documents
          .file_extension in $file_extensions_macros
          and any(file.explode(.),
                  .scan.qr.type == "url"
                  // QR code URL contains recipient's email (targeting indicator)
                  and any(recipients.to,
                          .email.domain.valid
                          and (
                            // Plaintext email address in URL
                            strings.icontains(..scan.qr.url.url, .email.email)
                            // OR base64 encoded email address
                            or any(strings.scan_base64(..scan.qr.url.url,
                                                       format="url",
                                                       ignore_padding=true
                                   ),
                                   strings.icontains(., ..email.email)
                            )
                          )
                  )
                  // a single path
                  and strings.count(.scan.qr.url.path, '/') == 2
                  and (
                    (
                      (
                        strings.contains(.scan.qr.url.path, '/$')
                        or strings.contains(.scan.qr.url.path, '/*')
                        or strings.contains(.scan.qr.url.path, '/#')
                      )
                      // subdomain should contain num{3}alpha or alphanum{3}
                      and regex.icontains(.scan.qr.url.domain.subdomain,
                                          '^(?:[a-z]+[0-9]{3}|[0-9]{3}[a-z]+)(?:$|\.)'
                      )
                      // url path should contain num{3}alpha or alphanum{3}
                      and regex.icontains(.scan.qr.url.path,
                                          '\/(?:[a-z]+[0-9]{3}|[0-9]{3}[a-z]+)\/'
                      )
                    )
                    or (
                      // special char in the path
                      (
                        strings.contains(.scan.qr.url.path, '!')
                        or strings.contains(.scan.qr.url.path, '@')
                      )
                      and (
                        strings.contains(.scan.qr.url.path, '/$')
                        or strings.contains(.scan.qr.url.path, '/*')
                        or strings.contains(.scan.qr.url.path, '/#')
                        // hex dollar sign
                        or strings.icontains(.scan.qr.url.path, '%24')
                        // hex star
                        or strings.icontains(.scan.qr.url.path, '%2A')
                        // hex pound
                        or strings.icontains(.scan.qr.url.path, '%23')
                      )
                      // ensure expected ordering
                      and regex.icontains(.scan.qr.url.url,
                                          '[!@].*(?:[$*]|%2[A43])'
                      )
                    )
                  )
          )
        )
        or (
          // pdf or images
          (
            .file_type == "pdf" or .file_type in $file_types_images
          )
          //
          // This rule makes use of a beta feature and is subject to change without notice
          // using the beta feature in custom rules is not suggested until it has been formally released
          //
          and any(beta.scan_qr(.).items,
                  .type is not null
                  // a single path
                  and strings.count(.url.path, '/') == 2
                  and (
                    (
                      (
                        strings.contains(.url.path, '/$')
                        or strings.contains(.url.path, '/*')
                        or strings.contains(.url.path, '/#')
                      )
                      // subdomain should contain num{3}alpha or alphanum{3}
                      and regex.icontains(.url.domain.subdomain,
                                          '^(?:[a-z]+[0-9]{3}|[0-9]{3}[a-z]+)(?:$|\.)'
                      )
                      // url path should contain num{3}alpha or alphanum{3}
                      and regex.icontains(.url.path,
                                          '\/(?:[a-z]+[0-9]{3}|[0-9]{3}[a-z]+)\/'
                      )
                    )
                    or (
                      // special char in the path
                      (
                        strings.contains(.url.path, '!')
                        or strings.contains(.url.path, '@')
                      )
                      and (
                        strings.contains(.url.path, '/$')
                        or strings.contains(.url.path, '/*')
                        or strings.contains(.url.path, '/#')
                        // hex dollar sign
                        or strings.icontains(.url.path, '%24')
                        // hex star
                        or strings.icontains(.url.path, '%2A')
                        // hex pound
                        or strings.icontains(.url.path, '%23')
                      )
                      // ensure expected ordering
                      and regex.icontains(.url.url, '[!@].*(?:[$*]|%2[A43])')
                    )
                  )
          )
        )
)

Detection logic

Scope: inbound message.

Detects messages with QR code in attachments containing special characters in the path that include the recipient's email address in either the URL path or fragment, potentially encoded in base64. The URLs have a simple path structure and may end with suspicious patterns.

  1. inbound message
  2. length(recipients.to) is 1
  3. recipients.to[0].email.domain.valid
  4. any of attachments where any holds:
    • all of:
      • .file_extension in $file_extensions_macros
      • any of file.explode(.) where all hold:
        • .scan.qr.type is 'url'
        • any of recipients.to where all hold:
          • .email.domain.valid
          • any of:
            • strings.icontains(.scan.qr.url.url)
            • any of strings.scan_base64(.scan.qr.url.url) where:
              • strings.icontains(.)
        • strings.count(.scan.qr.url.path, '/') is 2
        • any of:
          • all of:
            • any of:
              • .scan.qr.url.path contains '/$'
              • .scan.qr.url.path contains '/*'
              • .scan.qr.url.path contains '/#'
            • .scan.qr.url.domain.subdomain matches '^(?:[a-z]+[0-9]{3}|[0-9]{3}[a-z]+)(?:$|\\.)'
            • .scan.qr.url.path matches '\\/(?:[a-z]+[0-9]{3}|[0-9]{3}[a-z]+)\\/'
          • all of:
            • any of:
              • .scan.qr.url.path contains '!'
              • .scan.qr.url.path contains '@'
            • .scan.qr.url.path contains any of 6 patterns
              • /$
              • /*
              • /#
              • %24
              • %2A
              • %23
            • .scan.qr.url.url matches '[!@].*(?:[$*]|%2[A43])'
    • all of:
      • any of:
        • .file_type is 'pdf'
        • .file_type in $file_types_images
      • any of beta.scan_qr(.).items where all hold:
        • .type is set
        • strings.count(.url.path, '/') is 2
        • any of:
          • all of:
            • any of:
              • .url.path contains '/$'
              • .url.path contains '/*'
              • .url.path contains '/#'
            • .url.domain.subdomain matches '^(?:[a-z]+[0-9]{3}|[0-9]{3}[a-z]+)(?:$|\\.)'
            • .url.path matches '\\/(?:[a-z]+[0-9]{3}|[0-9]{3}[a-z]+)\\/'
          • all of:
            • any of:
              • .url.path contains '!'
              • .url.path contains '@'
            • .url.path contains any of 6 patterns
              • /$
              • /*
              • /#
              • %24
              • %2A
              • %23
            • .url.url matches '[!@].*(?:[$*]|%2[A43])'

Inspects: attachments[].file_extension, attachments[].file_type, recipients.to, recipients.to[0].email.domain.valid, recipients.to[].email.domain.valid, recipients.to[].email.email, type.inbound. Sensors: beta.scan_qr, file.explode, regex.icontains, strings.contains, strings.count, strings.icontains, strings.scan_base64. Reference lists: $file_extensions_macros, $file_types_images.

Indicators matched (13)

FieldMatchValue
file.explode(attachments[])[].scan.qr.typeequalsurl
strings.containssubstring/$
strings.containssubstring/*
strings.containssubstring/#
regex.icontainsregex^(?:[a-z]+[0-9]{3}|[0-9]{3}[a-z]+)(?:$|\.)
regex.icontainsregex\/(?:[a-z]+[0-9]{3}|[0-9]{3}[a-z]+)\/
strings.containssubstring!
strings.containssubstring@
strings.icontainssubstring%24
strings.icontainssubstring%2A
strings.icontainssubstring%23
regex.icontainsregex[!@].*(?:[$*]|%2[A43])
1 more
attachments[].file_typeequalspdf