Detection rules › Sublime MQL

Xero infrastructure abuse

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

Identifies messages that resemble credential theft, originating from Xero. Xero infrastrcture abuse has been observed recently to send phishing attacks.

Threat classification

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

CategoryValues
Attack typesCredential Phishing
Tactics and techniquesEvasion, Social engineering

Event coverage

Rule body MQL

type.inbound
and sender.email.email == "messaging-service@post.xero.com"
and 
// there are external links (not org or xero domains)
length(filter(body.links,
              .href_url.domain.domain not in $org_domains
              and .href_url.domain.root_domain not in ("xero.com", )
       )
) > 0
and (
  any(ml.nlu_classifier(body.current_thread.text).intents,
      .name == "cred_theft" and .confidence == "high"
  )
  // subject match when cred_theft doesn't match
  // high confidence observed subject intros in the format of "Urgent Thing: ..."
  or regex.icontains(subject.subject,
                     '^(?:(?:Final|Last)?\s*Warning|(?:Final|Last|Legal|Critical|Content Violation)?\s*(?:Alert|Noti(?:ce|fication))|Appeal Required|Time.Sensitive|Critical.Alert|Important|Copyright Issue)\s*:\s*'
  )
  or any(ml.logo_detect(file.message_screenshot()).brands,
         .name in ("Facebook", "Meta", "Instagram")
         and .confidence in ("medium", "high")
  )
  // any of the links are for newly registered domains
  or any(filter(body.links,
                .href_url.domain.domain not in $org_domains
                and .href_url.domain.root_domain not in ("xero.com")
         ),
         network.whois(.href_url.domain).days_old < 30
  )
  or (
    any(ml.nlu_classifier(body.current_thread.text).topics,
        .name in ("B2B Cold Outreach", "Professional and Career Development")
        and .confidence != "low"
    )
  )
  // sender display name or subject contains confusables
  or (
    sender.display_name != strings.replace_confusables(sender.display_name)
    or subject.subject != strings.replace_confusables(subject.subject)
  )
  // IP pool appears to be tagged by Xero via Mailgun
  // https://help.mailgun.com/hc/en-us/articles/360052184214-IP-Pools
  or any(headers.hops,
         any(.fields,
             .name == "X-Mailgun-Sending-Ip-Pool-Name"
             and .value == "High Risk Pool"
         )
  )
)
and (
  ( // sender domain matches no body domains
    length(body.links) > 0
    and all(body.links,
            .href_url.domain.root_domain not in ("xero.com", )
            or .href_url.domain.root_domain is null
    )
  )
  // link contains email address
  or any(recipients.to,
         .email.domain.valid
         and any(body.links,
                 strings.icontains(.href_url.url, ..email.email)
                 or any(beta.scan_base64(.href_url.url,
                                         format="url",
                                         ignore_padding=true
                        ),
                        strings.icontains(., ...email.email)
                 )
                 or any(beta.scan_base64(.href_url.fragment,
                                         ignore_padding=true
                        ),
                        strings.icontains(., ...email.email)
                 )
                 // cloudflare turnstile or phishing warning page
                 or strings.icontains(ml.link_analysis(., mode="aggressive").final_dom.display_text,
                                      "cloudflare"
                 )
         )
  )
  or regex.icontains(subject.subject, "termination.*notice")
  or any(ml.nlu_classifier(body.current_thread.text).entities,
         .name in ("sender", "org")
         and regex.icontains(.text, 'Recruitment|staffing|\bhr\b')
  )
)

Detection logic

Scope: inbound message.

Identifies messages that resemble credential theft, originating from Xero. Xero infrastrcture abuse has been observed recently to send phishing attacks.

  1. inbound message
  2. sender.email.email is 'messaging-service@post.xero.com'
  3. length(filter(body.links, .href_url.domain.domain not in $org_domains and .href_url.domain.root_domain not in ('xero.com'))) > 0
  4. any of:
    • any of ml.nlu_classifier(body.current_thread.text).intents where all hold:
      • .name is 'cred_theft'
      • .confidence is 'high'
    • subject.subject matches '^(?:(?:Final|Last)?\\s*Warning|(?:Final|Last|Legal|Critical|Content Violation)?\\s*(?:Alert|Noti(?:ce|fication))|Appeal Required|Time.Sensitive|Critical.Alert|Important|Copyright Issue)\\s*:\\s*'
    • any of ml.logo_detect(file.message_screenshot()).brands where all hold:
      • .name in ('Facebook', 'Meta', 'Instagram')
      • .confidence in ('medium', 'high')
    • any of filter(body.links) where:
      • network.whois(.href_url.domain).days_old < 30
    • any of ml.nlu_classifier(body.current_thread.text).topics where all hold:
      • .name in ('B2B Cold Outreach', 'Professional and Career Development')
      • .confidence is not 'low'
    • any of:
      • sender.display_name is not strings.replace_confusables(sender.display_name)
      • subject.subject is not strings.replace_confusables(subject.subject)
    • any of headers.hops where:
      • any of .fields where all hold:
        • .name is 'X-Mailgun-Sending-Ip-Pool-Name'
        • .value is 'High Risk Pool'
  5. any of:
    • all of:
      • length(body.links) > 0
      • all of body.links where any holds:
        • .href_url.domain.root_domain not in ('xero.com')
        • .href_url.domain.root_domain is missing
    • any of recipients.to where all hold:
      • .email.domain.valid
      • any of body.links where any holds:
        • strings.icontains(.href_url.url)
        • any of beta.scan_base64(.href_url.url) where:
          • strings.icontains(.)
        • any of beta.scan_base64(.href_url.fragment) where:
          • strings.icontains(.)
        • ml.link_analysis(., mode='aggressive').final_dom.display_text contains 'cloudflare'
    • subject.subject matches 'termination.*notice'
    • any of ml.nlu_classifier(body.current_thread.text).entities where all hold:
      • .name in ('sender', 'org')
      • .text matches 'Recruitment|staffing|\\bhr\\b'

Inspects: body.current_thread.text, body.links, body.links[].href_url.domain.domain, body.links[].href_url.domain.root_domain, body.links[].href_url.fragment, body.links[].href_url.url, headers.hops, headers.hops[].fields, headers.hops[].fields[].name, headers.hops[].fields[].value, recipients.to, recipients.to[].email.domain.valid, recipients.to[].email.email, sender.display_name, sender.email.email, subject.subject, type.inbound. Sensors: beta.scan_base64, file.message_screenshot, ml.link_analysis, ml.logo_detect, ml.nlu_classifier, network.whois, regex.icontains, strings.icontains, strings.replace_confusables. Reference lists: $org_domains.

Indicators matched (19)

FieldMatchValue
sender.email.emailequalsmessaging-service@post.xero.com
body.links[].href_url.domain.root_domainmemberxero.com
ml.nlu_classifier(body.current_thread.text).intents[].nameequalscred_theft
ml.nlu_classifier(body.current_thread.text).intents[].confidenceequalshigh
regex.icontainsregex^(?:(?:Final|Last)?\s*Warning|(?:Final|Last|Legal|Critical|Content Violation)?\s*(?:Alert|Noti(?:ce|fication))|Appeal Required|Time.Sensitive|Critical.Alert|Important|Copyright Issue)\s*:\s*
ml.logo_detect(file.message_screenshot()).brands[].namememberFacebook
ml.logo_detect(file.message_screenshot()).brands[].namememberMeta
ml.logo_detect(file.message_screenshot()).brands[].namememberInstagram
ml.logo_detect(file.message_screenshot()).brands[].confidencemembermedium
ml.logo_detect(file.message_screenshot()).brands[].confidencememberhigh
ml.nlu_classifier(body.current_thread.text).topics[].namememberB2B Cold Outreach
ml.nlu_classifier(body.current_thread.text).topics[].namememberProfessional and Career Development
7 more
headers.hops[].fields[].nameequalsX-Mailgun-Sending-Ip-Pool-Name
headers.hops[].fields[].valueequalsHigh Risk Pool
strings.icontainssubstringcloudflare
regex.icontainsregextermination.*notice
ml.nlu_classifier(body.current_thread.text).entities[].namemembersender
ml.nlu_classifier(body.current_thread.text).entities[].namememberorg
regex.icontainsregexRecruitment|staffing|\bhr\b