Detection rules › Sublime MQL

Sublime MQL rules: cc

Constant Contact link infrastructure abuse

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

Email contains a Constant Contact (mass mailing platform) tracking link but does not originate from Constant Contact sending infrastructure. The rs6.net domain has been abused by threat actors to attempt credential phishing.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesFree email provider, Open redirect, Social engineering

Telemetry coverage

PlatformRecord / event type
SublimeInbound email message

Message attributes

  • attachments
  • body
  • body.links
  • headers
  • headers.auth_summary
  • headers.hops
  • sender.email
  • subject
  • type

Rule body

type.inbound
and (
  (
    length(body.links) < 15
    and any(body.links,
            .href_url.domain.root_domain in (
              "rs6.net",
              "constantcontactpages.com"
            )
    )
  )
  or (
    length(attachments) <= 3
    and (
      any(attachments,
          (.file_type in $file_types_images or .file_type == "pdf")
          //
          // 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
                  and .url.domain.root_domain in (
                    "rs6.net",
                    "constantcontactpages.com"
                  )
          )
          //
          // 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
          //
          // exclude images taken with mobile cameras and screenshots from android
          and not any(beta.parse_exif(.).fields,
                      .key == "Model"
                      or (
                        .key == "Software"
                        and strings.starts_with(.value, "Android")
                      )
          )
          // exclude images taken with mobile cameras and screenshots from Apple
          and not any(beta.parse_exif(.).fields,
                      .key == "DeviceManufacturer"
                      and .value == "Apple Computer Inc."
          )
      )
      or (
        length(attachments) == 0
        and (
          //
          // 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
          //
          beta.parse_exif(file.message_screenshot()).image_height < 2000
          and beta.parse_exif(file.message_screenshot()).image_width < 2000
          and beta.scan_qr(file.message_screenshot()).found
          and any(beta.scan_qr(file.message_screenshot()).items,
                  .type is not null and .url.domain.root_domain == "rs6.net"
          )
        )
      )
    )
  )
)
and not (
  any(headers.hops,
      strings.icontains(.authentication_results.spf_details.designator,
                        "constantcontact.com"
      )
  )
  or any(headers.hops,
         strings.icontains(.received_spf.designator, "constantcontact.com")
  )
  or (
    (
      any(headers.hops,
          any(.authentication_results.dkim_details,
              .domain == "auth.ccsend.com"
          )
      )
    )
    and headers.auth_summary.dmarc.pass
  )
  or any(headers.references, strings.iends_with(., "ccsend.com"))
)
// negating legit replies
and not (
  (
    strings.istarts_with(subject.subject, "RE:")
    or strings.istarts_with(subject.subject, "FW:")
    or strings.istarts_with(subject.subject, "FWD:")
    or strings.istarts_with(subject.subject, "Automatic reply:")
    or regex.imatch(subject.subject,
                    '(\[[^\]]+\]\s?){0,3}(re|fwd?|automat.*)\s?:.*'
    )
  )
  and (length(headers.references) > 0 or headers.in_reply_to is not null)
)
// negate highly trusted sender domains unless they fail DMARC authentication
and (
  (
    sender.email.domain.root_domain in $high_trust_sender_root_domains
    and not headers.auth_summary.dmarc.pass
  )
  or sender.email.domain.root_domain not in $high_trust_sender_root_domains
)
and profile.by_sender().prevalence in ("new", "outlier", "rare")

Detection logic

Scope: inbound message.

Email contains a Constant Contact (mass mailing platform) tracking link but does not originate from Constant Contact sending infrastructure. The rs6.net domain has been abused by threat actors to attempt credential phishing.

  1. inbound message
  2. any of:
    • all of:
      • length(body.links) < 15
      • any of body.links where:
        • .href_url.domain.root_domain in ('rs6.net', 'constantcontactpages.com')
    • all of:
      • length(attachments) ≤ 3
      • any of:
        • any of attachments where all hold:
          • any of:
            • .file_type in $file_types_images
            • .file_type is 'pdf'
          • any of beta.scan_qr(.).items where all hold:
            • .type is set
            • .url.domain.root_domain in ('rs6.net', 'constantcontactpages.com')
          • not:
            • any of beta.parse_exif(.).fields where any holds:
              • .key is 'Model'
              • all of:
                • .key is 'Software'
                • .value starts with 'Android'
          • not:
            • any of beta.parse_exif(.).fields where all hold:
              • .key is 'DeviceManufacturer'
              • .value is 'Apple Computer Inc.'
        • all of:
          • length(attachments) is 0
          • all of:
            • beta.parse_exif(file.message_screenshot()).image_height < 2000
            • beta.parse_exif(file.message_screenshot()).image_width < 2000
            • beta.scan_qr(file.message_screenshot()).found
            • any of beta.scan_qr(file.message_screenshot()).items where all hold:
              • .type is set
              • .url.domain.root_domain is 'rs6.net'
  3. none of:
    • any of headers.hops where:
      • .authentication_results.spf_details.designator contains 'constantcontact.com'
    • any of headers.hops where:
      • .received_spf.designator contains 'constantcontact.com'
    • all of:
      • any of headers.hops where:
        • any of .authentication_results.dkim_details where:
          • .domain is 'auth.ccsend.com'
      • headers.auth_summary.dmarc.pass
    • any of headers.references where:
      • . ends with 'ccsend.com'
  4. not:
    • all of:
      • any of:
        • subject.subject starts with 'RE:'
        • subject.subject starts with 'FW:'
        • subject.subject starts with 'FWD:'
        • subject.subject starts with 'Automatic reply:'
        • subject.subject matches '(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?|automat.*)\\s?:.*'
      • any of:
        • length(headers.references) > 0
        • headers.in_reply_to is set
  5. any of:
    • all of:
      • sender.email.domain.root_domain in $high_trust_sender_root_domains
      • not:
        • headers.auth_summary.dmarc.pass
    • sender.email.domain.root_domain not in $high_trust_sender_root_domains
  6. profile.by_sender().prevalence in ('new', 'outlier', 'rare')

Inspects: attachments[].file_type, body.links, body.links[].href_url.domain.root_domain, headers.auth_summary.dmarc.pass, headers.hops, headers.hops[].authentication_results.dkim_details, headers.hops[].authentication_results.dkim_details[].domain, headers.hops[].authentication_results.spf_details.designator, headers.hops[].received_spf.designator, headers.in_reply_to, headers.references, sender.email.domain.root_domain, subject.subject, type.inbound. Sensors: beta.parse_exif, beta.scan_qr, file.message_screenshot, profile.by_sender, regex.imatch, strings.icontains, strings.iends_with, strings.istarts_with, strings.starts_with. Reference lists: $file_types_images, $high_trust_sender_root_domains.

Indicators matched (6)

FieldMatchValue
body.links[].href_url.domain.root_domainmemberrs6.net
body.links[].href_url.domain.root_domainmemberconstantcontactpages.com
attachments[].file_typeequalspdf
beta.scan_qr(attachments[]).items[].url.domain.root_domainmemberrs6.net
beta.scan_qr(attachments[]).items[].url.domain.root_domainmemberconstantcontactpages.com
beta.scan_qr(file.message_screenshot()).items[].url.domain.root_domainequalsrs6.net

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        any(attachments)
          and
            not
              any(beta.parse_exif(attachments).fields)
                or
                  and
                    beta.parse_exif(attachments[]).fields[].key eq "Software"
                    beta.parse_exif(attachments[]).fields[].value starts_with "Android"
                  beta.parse_exif(attachments[]).fields[].key eq "Model"
            not
              any(beta.parse_exif(attachments).fields)
                and
                  beta.parse_exif(attachments[]).fields[].key eq "DeviceManufacturer"
                  beta.parse_exif(attachments[]).fields[].value eq "Apple Computer Inc."
            any(beta.scan_qr(attachments).items)
              and
                beta.scan_qr(attachments[]).items[].type is_not_null
                beta.scan_qr(attachments[]).items[].url.domain.root_domain in ["constantcontactpages.com", "rs6.net"]
            or
              attachments.file_type eq "pdf"
               macro "attachments[].file_type in file_types_images"
        and
          any(beta.scan_qr(file.message_screenshot()).items)
            and
              beta.scan_qr(file.message_screenshot()).items.type is_not_null
              beta.scan_qr(file.message_screenshot()).items.url.domain.root_domain eq "rs6.net"
          attachments length_compare "0"
          beta.parse_exif func_call "beta.parse_exif(file.message_screenshot()).image_height < 2000"
          beta.parse_exif func_call "beta.parse_exif(file.message_screenshot()).image_width < 2000"
          beta.scan_qr func_call "beta.scan_qr(file.message_screenshot()).found"
      attachments length_compare "3"
    and
      any(body.links)
        body.links.href_url.domain.root_domain in ["constantcontactpages.com", "rs6.net"]
      body.links length_compare "15"
  not
    or
      and
        any(headers.hops)
          any(headers.hops.authentication_results.dkim_details)
            headers.hops.authentication_results.dkim_details.domain eq "auth.ccsend.com"
        headers.auth_summary.dmarc.pass eq "true"
      any(headers.hops)
        headers.hops.authentication_results.spf_details.designator contains "constantcontact.com"
      any(headers.hops)
        headers.hops.received_spf.designator contains "constantcontact.com"
      any(headers.references)
        headers.references ends_with "ccsend.com"
  or
    and
      not
        headers.auth_summary.dmarc.pass eq "true"
       macro "sender.email.domain.root_domain in high_trust_sender_root_domains"
     macro "sender.email.domain.root_domain not in high_trust_sender_root_domains"
  not
    and
      or
        headers.in_reply_to is_not_null
        headers.references length_compare "0"
      or
        subject.subject regex_match "(\\[[^\\]]+\\]\\s?){0,3}(re|fwd?|automat.*)\\s?:.*"
        subject.subject starts_with "Automatic reply:"
        subject.subject starts_with "FW:"
        subject.subject starts_with "FWD:"
        subject.subject starts_with "RE:"
  profile.by_sender func_call "profile.by_sender().prevalence in (new, outlier, rare)"
  type.inbound eq "true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
headers.hopsarray_any(no value, null check)excludes:headers.hops
headers.auth_summary.dmarc.passeqtrueexcludes:headers.auth_summary.dmarc.pass field:"headers.auth_summary.dmarc.pass" value:"true"
headers.referencesarray_any(no value, null check)excludes:headers.references
headers.in_reply_tois_not_null(no value, null check)excludes:headers.in_reply_to
headers.referenceslength_compare0excludes:headers.references field:"headers.references" value:"0"
subject.subjectregex_match([[^]]+]\s?){0,3}(re|fwd?|automat.*)\s?:.*excludes:subject.subject field:"subject.subject" value:"([[^]]+]\s?){0,3}(re|fwd?|automat.*)\s?:.*"
subject.subjectstarts_withAutomatic reply:excludes:subject.subject field:"subject.subject" value:"Automatic reply:"
subject.subjectstarts_withFW:excludes:subject.subject field:"subject.subject" value:"FW:"
subject.subjectstarts_withFWD:excludes:subject.subject field:"subject.subject" value:"FWD:"
subject.subjectstarts_withRE:excludes:subject.subject field:"subject.subject" value:"RE:"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
type.inboundeq
  • true transforms: boolean
field:"type.inbound" kind:eq value:"true"