Detection rules › Sigma

Account takeover via account linking using a delegated user token

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

Detects potential account takeover through abuse of Auth0 account linking. In the "Auth0 defaults" attack chain (e.g. an XSS or implicit-grant foothold in a SPA), an attacker obtains a current_user-delegated Management API token and adds an identity they control to another user's account via POST /api/v2/users/{id}/identities ("Link a User Account"). Once linked, authenticating with the attacker-controlled identity resolves to the victim's primary account, i.e. full account takeover. Benign self-service linking uses the exact same API call, so this rule keys on the delegated-token shape of the event and then on the tell-tale result of the attack: an account that holds more identities than distinct connections, i.e. at least two identities merged into one account that share a single connection, which normal cross-connection linking does not produce.

Known false positives

  • Legitimate self-service account linking, especially bulk or migration flows executed through a delegated token. This rule is designed for threat hunting and to be a starting point for further investigation.
  • A user legitimately merging two of their own accounts on the same connection (self-service de-duplication) produces the same event shape and will trigger this rule.
  • Sigma-only conversions apply only the coarse gate (the delegated-token shape) because Sigma field matching cannot count identities or detect duplicate connections. Expect substantially more matches from a converted rule than from the Splunk query, which enforces the same-connection check. Deploy the Splunk logic for production.

MITRE ATT&CK coverage

TacticTechniques
Persistence
Privilege Escalation

Rule body

title: Account takeover via account linking using a delegated user token
id: 8801b462-0020-44c2-a656-b168a1473181
status: experimental
description: |
    Detects potential account takeover through abuse of Auth0 account linking.
    In the "Auth0 defaults" attack chain (e.g. an XSS or implicit-grant foothold in a SPA), an attacker obtains a
    current_user-delegated Management API token and adds an identity they control to another user's account via
    POST /api/v2/users/{id}/identities ("Link a User Account"). Once linked, authenticating with the
    attacker-controlled identity resolves to the victim's primary account, i.e. full account takeover.
    Benign self-service linking uses the exact same API call, so this rule keys on the delegated-token shape of the
    event and then on the tell-tale result of the attack: an account that holds more identities than distinct
    connections, i.e. at least two identities merged into one account that share a single connection, which normal
    cross-connection linking does not produce.
author: Okta
date: 2026-08-04
modified: 2026-08-04
logsource:
    product: auth0
detection:
    selection:
        data.type: sapi
        data.description:
            - Link a User Account
        data.details.response.statusCode: 201
    delegated_token:
        # channel=api plus a current_user write scope in the presented credentials indicates a user-delegated
        # Management API token, not an admin/dashboard action or an M2M integration.
        data.details.request.channel: api
        data.details.request.auth.credentials.scopes{}:
            - 'update:current_user_identities'
    # NOTE: the same-connection check (more identities than distinct connections) cannot be expressed in Sigma
    # field matching because it requires counting array elements and detecting duplicate values. It lives in the
    # Splunk query below. This Sigma condition is therefore the coarse delegated-token gate only, and a converted
    # rule will match more broadly than the Splunk detection.
    condition: selection and delegated_token
explanation: >
    The query collects successful "Link a User Account" Management API events (sapi, statusCode 201).
    It first isolates the attack backbone: a user-delegated token (request channel "api" and the
    update:current_user_identities scope present in the request credentials), which removes the bulk of benign
    linking performed by administrators via the dashboard or by M2M integrations.
    It then inspects the identities array returned in the response body and alerts when the account holds more
    identities than distinct connections, i.e. at least two identities share a single connection. Legitimate
    linking is cross-connection (unifying a user's different login methods), so a same-connection merge within the
    delegated population is the signature of attacker account farming and is the sole trigger used here; the
    identity count is reported alongside for triage.
splunk: |
    index=auth0 data.tenant_name="{your-tenant-name}"
    data.type=sapi data.description="Link a User Account"
    data.details.response.statusCode=201
    ``` Backbone: user-delegated Management API token, not an admin/dashboard or M2M action.
        channel=api together with a current_user write scope in the presented credentials. ```
    data.details.request.channel="api"
    data.details.request.auth.credentials.scopes{}="update:current_user_identities"
    | fields _time, data.ip, data.user_agent, data.details.request.path,
        data.details.response.body{}.user_id,
        data.details.response.body{}.connection,
        data.details.response.body{}.profileData.email
    | rename data.details.response.body{}.user_id as linked_user_ids
    | rename data.details.response.body{}.connection as linked_connections
    | rename data.details.response.body{}.profileData.email as linked_emails
    ``` Detection: the account now holds more identities than distinct connections, i.e. at least two identities
        share one connection. Normal linking is cross-connection (unifying a user's different login methods), so a
        same-connection merge within the delegated population isolated above is the account-farming signature seen
        in the attack PoC. identity_count is reported for triage (fan-in); it is not part of the trigger. ```
    | eval identity_count = mvcount(linked_user_ids)
    | eval distinct_connections = mvcount(mvdedup(linked_connections))
    | where identity_count > distinct_connections
    | table _time, data.ip, data.user_agent, data.details.request.path,
        identity_count, distinct_connections,
        linked_emails, linked_connections
comments:
    - The Splunk query must be tuned to reflect a valid tenant name.
    - The discriminating fields (data.details.request.channel, data.details.request.auth.*,
      data.details.response.body{}.*) are not exposed to Auth0 Dashboard log search (q=). Use the tenant_logs query
      only to locate candidate events, then analyse them in your SIEM.
    - The backbone (channel=api together with update:current_user_identities in the request credentials) removes
      admin/dashboard and M2M linking, which are the bulk of benign "Link a User Account" events. It does not by
      itself separate the attack from legitimate user self-service linking - that is the job of the same-connection
      check.
    - The link_with value in the request body is a signed JWT (id_token) whose claims are not directly queryable, so
      this rule uses the queryable response identities array instead. That array is the full resulting set of linked
      identities, which is exactly what the same-connection check operates on.
tenant_logs: |
    type:"sapi" AND description:"Link a User Account" AND details.request.auth.credentials.scopes:"update:current_user_identities"
prevention:
    - Disable the implicit grant and migrate single-page applications to Authorization Code with PKCE. This removes
      the token-theft foothold that makes delegated account linking possible and closes the associated logging gap.
    - Do not grant end-user-facing SPAs the update:current_user_identities scope. Perform account linking server-side
      with a scoped Management API token only after verifying both identities belong to the same user.
    - Require email verification (email_verified=true) on both the primary and the linked identity before allowing a
      link, and avoid linking multiple accounts of the same database connection.
    - Restrict Management API calls to trusted, static IP ranges using Auth0 Network ACLs scoped to "management".
falsepositives:
    - Legitimate self-service account linking, especially bulk or migration flows executed through a delegated
      token. This rule is designed for threat hunting and to be a starting point for further investigation.
    - A user legitimately merging two of their own accounts on the same connection (self-service de-duplication)
      produces the same event shape and will trigger this rule.
    - Sigma-only conversions apply only the coarse gate (the delegated-token shape) because Sigma field matching
      cannot count identities or detect duplicate connections. Expect substantially more matches from a converted
      rule than from the Splunk query, which enforces the same-connection check. Deploy the Splunk logic for
      production.
level: high
tags:
    - attack.persistence
    - attack.privilege-escalation
    - attack.t1098

Stages and Predicates

Stage 0: condition

selection and delegated_token

Stage 1: selection

selection:
    data.type: sapi
    data.description:
        - Link a User Account
    data.details.response.statusCode: 201

Stage 2: delegated_token

delegated_token:
    data.details.request.channel: api
    data.details.request.auth.credentials.scopes{}:
        - 'update:current_user_identities'

Indicators

These rows show field, operator, and value matches.