oak_techniques: [OAK-T15.004]
spec_id: oak-detection-T15.004
version: 0.1.0
maturity: emerging
maintainer: "@iZonex"
license: Apache-2.0

scope: |
  Detect operator-side credential / session compromise on non-signing
  identity surfaces — SSO / IDP, cloud accounts (AWS / GCP / Azure /
  Cloudflare / Vercel), domain-registrar accounts, DNS providers,
  package-registry publisher credentials, OAuth tokens. Compromise is
  at a credential surface whose downstream effect is to manipulate
  what the operator's audience sees / signs / trusts. Excludes:
  T15.003 (developer / signer / employee endpoint state — composes);
  T15.005 (operator communication-channel takeover — different
  surface, distinguished by what-the-access-was-used-for); T15.002
  (shared upstream package-registry surface — composes for credential-
  mediated publication).

data_sources: [registrar_audit_log, dns_change_feed,
               cloud_iam_audit_log, ssp_oauth_log,
               package_publisher_audit, certificate_transparency_log,
               session_anomaly_feed]

detection_logic:
  description: |
    Four orthogonal paths matching the four sub-shapes. PATH A
    (registrar / DNS): unscheduled DNS / nameserver / SSL-certificate
    change at operator domain; registrar account-activity anomaly.
    PATH B (cloud / SaaS): unscheduled IAM policy change, root-admin
    login from unfamiliar source, OAuth-app authorisation, API-key
    creation. PATH C (package-publisher credential): unscheduled
    publication outside normal cadence + no corresponding developer-
    workstation activity (distinguishes from T15.003 → T15.002 chain).
    PATH D (SSO / IDP / session anomaly): impossible-travel login,
    MFA-fatigue / push-bombing, session-token-replay.
  pseudocode: |
    # PATH A — registrar / DNS / certificate change
    for each operator domain D in monitored_operator_domains:
      changes ← {dns_record_changes(D),
                 nameserver_changes(D),
                 ssl_certificate_issuances(D, ct_log)}
      if any(c.scheduled == False for c in flatten(changes)):
        emit(PATH_A, domain=D, change_kinds=classify(changes),
             severity=critical)

    # PATH B — cloud / SaaS account anomaly
    for each cloud account A in monitored_cloud_accounts:
      events ← cloud_iam_audit_log(A, window = audit_window)
      anomalies ← [e for e in events
                   if e.kind ∈ {iam_policy_change, root_login_unfamiliar_source,
                                 oauth_app_authorised, api_key_created}
                   and e.scheduled == False]
      if anomalies ≠ ∅:
        emit(PATH_B, account=A, anomaly_events=anomalies, severity=high)

    # PATH C — package-publisher credential without dev activity
    for each package publication P:
      publisher ← P.publisher_account
      developer_activity ← workstation_activity(publisher,
                                                  window = publish_window)
      if developer_activity == None and P.scheduled == False:
        emit(PATH_C, package=P.name, version=P.version,
             publisher, severity=critical)

    # PATH D — SSO / IDP session anomaly
    for each session event S in ssp_oauth_log:
      patterns ← [impossible_travel(S),
                  mfa_fatigue_bombing(S),
                  session_token_replay(S)]
      if any(p ≠ None for p in patterns):
        emit(PATH_D, session=S, anomaly_patterns=patterns,
             severity=high)

parameters:
  monitored_operator_domains:    { type: list,    default: [] }
  monitored_cloud_accounts:      { type: list,    default: [] }
  audit_window:                  { type: duration, default: 7d }
  publish_window:                { type: duration, default: 24h }

output_alert: [oak_technique, detection_path, severity,
               domain, account, package, version, publisher,
               session, change_kinds, anomaly_patterns, evidence]

test_fixtures:
  positive:
    - 2022-08-curve-dns-hijack       # canonical registrar / DNS sub-shape
    - 2023-12-galxe-dns              # second canonical DNS hijack
    - 2024-06-coinstats-snap         # operator-cloud / SaaS sub-shape
  negative:
    - "Scheduled DNS change with documented out-of-band confirmation"
    - "Cloud IAM update via documented change-management process"
    - "Package publication from publisher with concurrent developer-workstation activity"

false_positive_modes:
  - scheduled DNS / cloud / IDP changes with documented change-management approval (require scheduled == False)
  - PATH C false positives where developer workstation telemetry is incomplete (combine with publish-cadence anomaly)
  - PATH D false positives from VPN / split-tunnel users producing impossible-travel signature (allowlist VPN exit IPs)
  - certificate transparency noise for legitimate SSL renewal (filter by issuer matching expected reference)

mitigations: [OAK-M22, OAK-M30, OAK-M40]

reference_implementations:
  - { target: cloudflare-radar,        chain: cross-chain, url: "" }
  - { target: certificate-transparency-monitor, chain: cross-chain, url: "" }
  - { target: aws-cloudtrail-siem,     chain: cross-chain, url: "" }
  - { target: okta-system-log,         chain: cross-chain, url: "" }
  - { target: github-audit-log,        chain: cross-chain, url: "" }
