Detection rules › Sublime MQL

Brand impersonation: Paperless Post

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

Detects messages containing multiple images hosted on ppassets.com (Paperless Post's asset domain) but with fewer than 3 legitimate Paperless Post links, while excluding authentic forwards/replies and messages from verified Paperless Post domains with valid DMARC authentication.

Threat classification

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

CategoryValues
Attack typesCredential Phishing, Malware/Ransomware
Tactics and techniquesImpersonation: Brand

Event coverage

Rule body MQL

type.inbound
and strings.contains(body.html.raw, 'ppassets.com')
and length(filter(html.xpath(body.html, '//img/@src').nodes,
                  // calling parse_url allows url decoding to help us
                  strings.parse_url(.raw).domain.root_domain == 'ppassets.com'
           )
) >= 2
and (
  length(filter(body.links,
                .href_url.domain.domain == "links.paperlesspost.com"
                or (
                  .href_url.domain.root_domain == "paperlesspost.com"
                  and strings.istarts_with(.href_url.path, '/go/')
                )
         )
  ) < 2
  or any(body.links,
         regex.icontains(.display_text, '(?:view the card|view and reply)')
         and .href_url.domain.root_domain != "paperlesspost.com"
  )
)
and not (
  (subject.is_forward or subject.is_reply)
  and (
    (length(headers.references) != 0 or headers.in_reply_to is not null)
    or length(body.previous_threads) > 0
  )
)
and not (
  sender.email.domain.root_domain == "paperlesspost.com"
  and headers.auth_summary.dmarc.pass
)

Detection logic

Scope: inbound message.

Detects messages containing multiple images hosted on ppassets.com (Paperless Post's asset domain) but with fewer than 3 legitimate Paperless Post links, while excluding authentic forwards/replies and messages from verified Paperless Post domains with valid DMARC authentication.

  1. inbound message
  2. body.html.raw contains 'ppassets.com'
  3. length(filter(html.xpath(body.html, '//img/@src').nodes, strings.parse_url(.raw).domain.root_domain == 'ppassets.com')) ≥ 2
  4. any of:
    • length(filter(body.links, .href_url.domain.domain == 'links.paperlesspost.com' or .href_url.domain.root_domain == 'paperlesspost.com' and strings.istarts_with(.href_url.path, '/go/'))) < 2
    • any of body.links where all hold:
      • .display_text matches '(?:view the card|view and reply)'
      • .href_url.domain.root_domain is not 'paperlesspost.com'
  5. not:
    • all of:
      • any of:
        • subject.is_forward
        • subject.is_reply
      • any of:
        • any of:
          • length(headers.references) is not 0
          • headers.in_reply_to is set
        • length(body.previous_threads) > 0
  6. not:
    • all of:
      • sender.email.domain.root_domain is 'paperlesspost.com'
      • headers.auth_summary.dmarc.pass

Inspects: body.html, body.html.raw, body.links, body.links[].display_text, body.links[].href_url.domain.domain, body.links[].href_url.domain.root_domain, body.links[].href_url.path, body.previous_threads, headers.auth_summary.dmarc.pass, headers.in_reply_to, headers.references, sender.email.domain.root_domain, subject.is_forward, subject.is_reply, type.inbound. Sensors: html.xpath, regex.icontains, strings.contains, strings.istarts_with, strings.parse_url.

Indicators matched (6)

FieldMatchValue
strings.containssubstringppassets.com
body.links[].href_url.domain.domainequalslinks.paperlesspost.com
body.links[].href_url.domain.root_domainequalspaperlesspost.com
strings.istarts_withprefix/go/
regex.icontainsregex(?:view the card|view and reply)
sender.email.domain.root_domainequalspaperlesspost.com