Detection rules › Sigma

Loaded LiquidJS error page template contains XSS vulnerabilities

Status
experimental
Log source
product auth0
Author
Okta
Source
github.com/auth0/auth0-customer-detections

This detection monitors if there are patterns that indicate potential Cross-Site Scripting (XSS) vulnerabilities in a LiquidJS error page template.

MITRE ATT&CK coverage

Rule body yaml

title: Loaded LiquidJS error page template contains XSS vulnerabilities
description: |
    This detection monitors if there are patterns that indicate potential Cross-Site Scripting (XSS)
     vulnerabilities in a LiquidJS error page template.
id: 8281291e-51b4-46a9-8012-955e20206efd
status: experimental
author: Okta
date: 2025-07-11
modified: 2025-08-04
logsource:
    product: auth0
detection:
    selection:
        data.type: sapi
        data.description: "Update tenant settings"
    vulnerable_variables:
        # This regex matches LiquidJS variables that are unfiltered
        data.details.response.body.error_page.html|re: '\{\{([^}]+?)(?:\s*\|\s*(?!escape|escape_once|h|html_escape)[^}]+)??\s*\}\}|\{\{([^}|]+)\}\}'
    condition: selection and vulnerable_variables
explanation: >
    The query searches for events where tenant settings have been modified.
    To examine an error page the 'data.details.response.body.error_page.html' path of the json object needs to be reviewed.
    The Splunk query looks for a XSS vulnerability introduced by unescaped variables - {{ variable }} - by applying a regex.
    It also outputs the whole error_page template, list of filters, list of vulnerable filters, and modifying IP.
splunk: |
    index=auth0 data.tenant_name="{your-tenant-name}"
    data.type=sapi data.description="Update tenant settings"
    | fields data.details.response.body.error_page.html, data.ip
    | spath path=data.details.response.body.error_page.html output=error_page_html
    | rex field=error_page_html "{{(?<variable_with_filter>[^}]+?)}}" max_match=0
    | eval safe_variables = mvfind(variable_with_filter, "\|\s*(escape|escape_once|h|html_escape)")
    | eval vulnerable_variables = mvfilter(NOT match(variable_with_filter, "\|\s*(escape|escape_once|h|html_escape)"))
    | where isnotnull(vulnerable_variables)
    | table _time, data.ip, error_page_html, variable_with_filter, vulnerable_variables
comments:
    - The Splunk query above shall be tuned to reflect a valid tenant name.
    - The Sigma query converted into the Splunk backend returns only those records where all variables are vulnerable.
    - Thus, it misses the cases where only some variables are vulnerable. The provided Splunk query addresses this gap.
tenant_logs: |
    type:"sapi" AND description: "Update tenant settings"
prevention:
    - Apply known linters, e.g. shopify (maintainer of liquidjs) has some linters we can reference
        https://github.com/Shopify/theme-tools/tree/main/packages/theme-check-common.
false_positive: |
     - Parameters that do not have escape filters but are not vulnerable to XSS,
        such as using the date filter '{{ "now" | date: "%Y-%m-%d %H:%M" }}'
tags:
    - attack.defense-evasion
    - attack.t1562
    - attack.t1562.007

Stages and Predicates

Stage 0: condition

selection and vulnerable_variables

Stage 1: selection

selection:
    data.type: sapi
    data.description: "Update tenant settings"

Stage 2: vulnerable_variables

vulnerable_variables:
    data.details.response.body.error_page.html|re: '\{\{([^}]+?)(?:\s*\|\s*(?!escape|escape_once|h|html_escape)[^}]+)??\s*\}\}|\{\{([^}|]+)\}\}'

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
data.descriptioneq
  • Update tenant settings
data.details.response.body.error_page.htmlregex_match
    • {{([^}]+?)(?:\s*|\s*(?!escape|escape_once|h|html_escape)[^}]+)??\s*}}
    • {{([^}|]+)}}
data.typeeq
  • sapi