Detection rules › Sublime MQL

Sublime MQL rules: phishing

Credential phishing: Blue button styled link with file-sharing template artifacts

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

Detects inbound messages containing styled blue button links commonly associated with generic file-sharing phishing templates, where the link does not point to legitimate Outlook domains.

Threat Classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesImpersonation: Brand, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • attachments
  • body
  • body.current_thread
  • headers
  • headers.auth_summary
  • sender.email
  • subject
  • type

Rule Body

type.inbound
and (
  // no previous threads
  length(body.previous_threads) == 0
  // or is a fake thread
  or (
    (length(headers.references) == 0 or headers.in_reply_to is null)
    and (
      subject.is_reply
      or subject.is_forward
      or length(body.previous_threads) > 0
    )
  )
)
and any(filter(html.xpath(body.html,
                          // require some styling anywhere in the anchor before running the
                          // (comparatively expensive) regex checks below
                          '//a[@href][@style or .//@style]'
               ).nodes,
               // blue button background, background-color and observed colors
               regex.icontains(.raw,
                               '(?:background(?:-color)?)\s*[:\s]\s*#(?:0078d4|3a78d1)'
               )
        ),
        (
          // it's styled as a button
          regex.icontains(.raw, 'padding')
        )
        // ignore links going to microsoft
        and not any(.links,
                    (
                      .href_url.domain.sld in (
                        "microsoft",
                        "azure",
                        "outlook.office365",
                        "office365"
                      )
                    )
                    or .href_url.domain.domain in $tenant_domains
                    or (
                      .href_url.domain.root_domain in (
                        "mimecast.com",
                        "mimecastprotect.com"
                      )
                      and any(.href_url.query_params_decoded['domain'],
                              strings.parse_domain(.).domain in (
                                "microsoft.com",
                                "azure.com",
                                "outlook.office365.com",
                                "office365.com"
                              )
                              or strings.parse_domain(.).domain in $tenant_domains
                      )
                    )
        )
)
and any(ml.nlu_classifier(body.current_thread.text).intents, .name != "benign")
// negate attachments that contain the known microsoft content type
and not any(attachments,
            strings.icontains(.content_type, 'x-microsoft-rpmsg-message')
)
// negate microsoft emails who pass auth
and not (
  sender.email.domain.root_domain == "microsoft.com"
  and headers.auth_summary.dmarc.pass
)

Detection Logic

Scope: inbound message.

Detects inbound messages containing styled blue button links commonly associated with generic file-sharing phishing templates, where the link does not point to legitimate Outlook domains.

  1. inbound message
  2. any of:
    • length(body.previous_threads) == 0
    • all of:
      • any of:
        • length(headers.references) == 0
        • headers.in_reply_to is missing
      • any of:
        • subject.is_reply
        • subject.is_forward
        • length(body.previous_threads) > 0
  3. any(filter(html.xpath(body.html, // require some styling anywhere in the anchor before running the // (comparatively expensive) regex checks below '//a[@href][@style or .//@style]' ).nodes, // blue button background, background-color and observed colors regex.icontains(.raw, '(?:background(?:-color)?)\s*[:\s]\s*#(?:0078d4|3a78d1)' ) ), ( // it's styled as a button regex.icontains(.raw, 'padding') ) // ignore links going to microsoft and not any(.links, ( .href_url.domain.sld in ( "microsoft", "azure", "outlook.office365", "office365" ) ) or .href_url.domain.domain in $tenant_domains or ( .href_url.domain.root_domain in ( "mimecast.com", "mimecastprotect.com" ) and any(.href_url.query_params_decoded['domain'], strings.parse_domain(.).domain in ( "microsoft.com", "azure.com", "outlook.office365.com", "office365.com" ) or strings.parse_domain(.).domain in $tenant_domains ) ) ) )
  4. any(ml.nlu_classifier(body.current_thread.text).intents, .name != "benign")
  5. not:
    • any of attachments where:
      • .content_type contains 'x-microsoft-rpmsg-message'
  6. not:
    • all of:
      • sender.email.domain.root_domain is 'microsoft.com'
      • headers.auth_summary.dmarc.pass

Inspects: attachments[].content_type, body.current_thread.text, body.html, 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, ml.nlu_classifier, regex.icontains, strings.icontains, strings.parse_domain. Reference lists: $tenant_domains.

Indicators Matched (2)

FieldMatchValue
regex.icontainsregex(?:background(?:-color)?)\s*[:\s]\s*#(?:0078d4|3a78d1)
regex.icontainsregexpadding

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      or
        body.previous_threads length_compare "0"
        subject.is_forward eq "true"
        subject.is_reply eq "true"
      or
        headers.in_reply_to is_null
        headers.references length_compare "0"
    body.previous_threads length_compare "0"
  not
    any(attachments)
      attachments.content_type contains "x-microsoft-rpmsg-message"
  not
    and
      headers.auth_summary.dmarc.pass eq "true"
      sender.email.domain.root_domain eq "microsoft.com"
  type.inbound eq "true"
   macro "any(filter(html.xpath(body.html,\n                          // require some styling anywhere in the anchor before running the\n                          // (comparatively expensive) regex checks below\n                          '//a[@href][@style or .//@style]'\n               ).nodes,\n               // blue button background, background-color and observed colors\n               regex.icontains(.raw,\n                               '(?:background(?:-color)?)\\s*[:\\s]\\s*#(?:0078d4|3a78d1)'\n               )\n        ),\n        (\n          // it's styled as a button\n          regex.icontains(.raw, 'padding')\n        )\n        // ignore links going to microsoft\n        and not any(.links,\n                    (\n                      .href_url.domain.sld in (\n                        \"microsoft\",\n                        \"azure\",\n                        \"outlook.office365\",\n                        \"office365\"\n                      )\n                    )\n                    or .href_url.domain.domain in $tenant_domains\n                    or (\n                      .href_url.domain.root_domain in (\n                        \"mimecast.com\",\n                        \"mimecastprotect.com\"\n                      )\n                      and any(.href_url.query_params_decoded['domain'],\n                              strings.parse_domain(.).domain in (\n                                \"microsoft.com\",\n                                \"azure.com\",\n                                \"outlook.office365.com\",\n                                \"office365.com\"\n                              )\n                              or strings.parse_domain(.).domain in $tenant_domains\n                      )\n                    )\n        )\n)"
   macro "any(ml.nlu_classifier(body.current_thread.text).intents, .name != \"benign\")"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
headers.in_reply_tois_null
  • (no value, null check)
field:"headers.in_reply_to" kind:is_null
subject.is_forwardeq
  • true transforms: boolean corpus 8 (mql 8)
field:"subject.is_forward" kind:eq value:"true"
subject.is_replyeq
  • true transforms: boolean corpus 11 (mql 11)
field:"subject.is_reply" kind:eq value:"true"
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
attachmentsarray_any(no value, null check)excludes:attachments
headers.auth_summary.dmarc.passeqtrueexcludes:headers.auth_summary.dmarc.pass field:"headers.auth_summary.dmarc.pass" value:"true"
sender.email.domain.root_domaineqmicrosoft.comexcludes:sender.email.domain.root_domain field:"sender.email.domain.root_domain" value:"microsoft.com"

Credential phishing: Personalized document signing request

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

Detects messages with a single recipient and personalized document signing requests. The rule identifies messages referencing DocuSign or document-related language, combined with domain-specific greeting patterns or known malicious HTML artifacts associated with fraudulent signing invitations from entities such as STAR Capital or Agito AS.

Threat Classification

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

CategoryValues
Attack typesCredential Phishing, BEC/Fraud
Tactics and techniquesImpersonation: Brand, Social engineering

Telemetry Coverage

PlatformRecord / event type
SublimeInbound email message

Message Attributes

  • body
  • body.current_thread
  • body.html
  • recipients
  • recipients.to
  • type

Rule Body

type.inbound
// personalized document form ensures recipients should always be 1
and length(recipients.to) == 1
and 2 of (
  // document/sign language
  strings.icontains(body.current_thread.text, 'docusign', 'document'),
  // greeting uses recipient's email local_part
  any(recipients.to,
      strings.icontains(body.current_thread.text,
                        strings.concat("You're receiving this on behalf of ",
                                       .email.domain.sld
                        )
      )
      or strings.icontains(body.current_thread.text,
                           strings.concat("Invitation to sign document for ",
                                          .email.domain.sld
                           )
      )
  ),
  // templated html artifact
  strings.contains(body.html.raw,
                   'STAR Capital invites you to exchange',
                   'Reminder: Invitation to sign document for Agito AS'
  )
)

Detection Logic

Scope: inbound message.

Detects messages with a single recipient and personalized document signing requests. The rule identifies messages referencing DocuSign or document-related language, combined with domain-specific greeting patterns or known malicious HTML artifacts associated with fraudulent signing invitations from entities such as STAR Capital or Agito AS.

  1. inbound message
  2. length(recipients.to) == 1
  3. at least 2 of:
    • body.current_thread.text contains any of 2 patterns
      • docusign
      • document
    • any of recipients.to where any holds:
      • strings.icontains(body.current_thread.text, strings.concat("You're receiving this on behalf of ", .email.domain.sld ) )
      • strings.icontains(body.current_thread.text, strings.concat("Invitation to sign document for ", .email.domain.sld ) )
    • body.html.raw contains any of 2 patterns
      • STAR Capital invites you to exchange
      • Reminder: Invitation to sign document for Agito AS

Inspects: body.current_thread.text, body.html.raw, recipients.to, recipients.to[].email.domain.sld, type.inbound. Sensors: strings.concat, strings.contains, strings.icontains.

Indicators Matched (4)

FieldMatchValue
strings.icontainssubstringdocusign
strings.icontainssubstringdocument
strings.containssubstringSTAR Capital invites you to exchange
strings.containssubstringReminder: Invitation to sign document for Agito AS

Stages and Predicates

Stage 1: mql_rule

and
  or
    and
      any(recipients.to)
        or
          strings.icontains func_call "strings.icontains(body.current_thread.text,\n                           strings.concat(\"Invitation to sign document for \",\n                                          .email.domain.sld\n                           )\n      )"
          strings.icontains func_call "strings.icontains(body.current_thread.text,\n                        strings.concat(\"You're receiving this on behalf of \",\n                                       .email.domain.sld\n                        )\n      )"
      or
        body.current_thread.text contains "document"
        body.current_thread.text contains "docusign"
    and
      any(recipients.to)
        or
          strings.icontains func_call "strings.icontains(body.current_thread.text,\n                           strings.concat(\"Invitation to sign document for \",\n                                          .email.domain.sld\n                           )\n      )"
          strings.icontains func_call "strings.icontains(body.current_thread.text,\n                        strings.concat(\"You're receiving this on behalf of \",\n                                       .email.domain.sld\n                        )\n      )"
      or
        body.html.raw contains "Reminder: Invitation to sign document for Agito AS"
        body.html.raw contains "STAR Capital invites you to exchange"
    and
      or
        body.current_thread.text contains "document"
        body.current_thread.text contains "docusign"
      or
        body.html.raw contains "Reminder: Invitation to sign document for Agito AS"
        body.html.raw contains "STAR Capital invites you to exchange"
  recipients.to length_compare "1"
  type.inbound eq "true"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
body.current_thread.textcontains
  • document corpus 2 (mql 2)
  • docusign corpus 2 (mql 2)
field:"body.current_thread.text" kind:contains
body.html.rawcontains
  • Reminder: Invitation to sign document for Agito AS
  • STAR Capital invites you to exchange
field:"body.html.raw" kind:contains
type.inboundeq
  • true transforms: boolean corpus 1212 (mql 1212)
field:"type.inbound" kind:eq value:"true"

Back to all Sublime MQL rules