# REPRO-2026-00314: OpenCTI authentication bypass via user impersonation ## Summary Status: published Severity: critical CVSS: Unknown CWE: CWE-287 Improper Authentication (Improper Authentication) Type: security Confidence: high ## Identifiers REPRO ID: REPRO-2026-00314 CVE: CVE-2026-27960 ## Package Name: opencti Ecosystem: docker Affected: >= 6.6.0, < 6.9.13 (i.e. 6.6.0 through 6.9.12) Fixed: 6.9.13 ## Root Cause # CVE-2026-27960 — OpenCTI Unauthenticated Authentication Bypass via User Impersonation ## Summary OpenCTI versions 6.6.0 through 6.9.12 contain an improper-authentication flaw (CWE-287) in the GraphQL API bearer-token resolution path. The function `authenticateUserByTokenOrUserId()` in `opencti-platform/opencti-graphql/src/domain/user.js` resolves the HTTP `Authorization: Bearer ` credential against the platform user cache map, which is keyed not only by each user's secret `api_token`, but also by every non-secret identifier of the user: `internal_id`, `standard_id`, and STIX ids (`buildStoreEntityMap()` in `opencti-platform/opencti-graphql/src/database/cache.ts` explicitly pushes `entity.api_token` into the same id list as `internal_id`/`standard_id`). As a result, an unauthenticated remote attacker can present **any known or guessable user identifier** — in particular the hard-coded default-admin `internal_id` `OPENCTI_ADMIN_UUID = 88ec0c6a-13ce-5e39-b486-354fe4a7084f` (`opencti-platform/opencti-graphql/src/schema/general.js`) — as the bearer token and is authenticated as that user without ever proving knowledge of the secret API token, password, or any credential. ## Impact - Package/component affected: `opencti/platform` (OpenCTI GraphQL API, `opencti-graphql`), all deployment modes that expose the HTTP/GraphQL endpoint. - Affected versions: >= 6.6.0, < 6.9.13 (fixed in 6.9.13). - Risk level: critical (CVSS 9.8 per public advisories). An unauthenticated network attacker can query and mutate the GraphQL API as any existing user, including the default admin: full read access to threat-intelligence data and full administrative control (user management, settings, data destruction). ## Impact Parity - Disclosed/claimed maximum impact: unauthenticated remote authentication bypass / authorization bypass allowing API access as any existing user, including the default admin (impact class `authz_bypass`). - Reproduced impact from this run: unauthenticated GraphQL request carrying only the public, hard-coded default-admin `internal_id` as bearer token was accepted by OpenCTI 6.9.12 and executed both `me` (returning the admin identity) and the admin-only `users` listing query. The identical request was rejected on the fixed 6.9.13 build, while the real secret `api_token` remained accepted on both builds. - Parity: `full` (unauthenticated admin impersonation through the production GraphQL boundary demonstrated end-to-end). ## Root Cause `authenticateUserFromRequest()` extracts the bearer value and calls `authenticateUserByTokenOrUserId(context, req, tokenUUID)`. That function only tests `platformUsers.has(tokenOrId)` on the user cache map. `getEntitiesMapFromCache()` builds this map via `buildStoreEntityMap()`, which indexes each user under `internal_id`, `standard_id`, `x_opencti_stix_ids` **and** `api_token`. The code therefore conflates *public identifiers* with *secret credentials*: possession of a user's internal UUID (for the default admin a constant compiled into the shipped source, `OPENCTI_ADMIN_UUID`) is treated as proof of identity. Fix (6.9.13, diff of `src/domain/user.js` between tags 6.9.12 and 6.9.13): the function was split into `authenticateUserByToken()` — which additionally verifies `crypto.timingSafeEqual(Buffer.from(user.api_token), Buffer.from(token))` — and `authenticateUserByUserId()`, which is only reachable after a successfully authenticated header-provider login (`HEADERS_AUTHENTICATORS`), restoring the invariant that a bearer value must be the secret token. - Vendor advisory: https://github.com/OpenCTI-Platform/opencti/security/advisories/GHSA-6vvv-vmfr-xhrx - Fix: `opencti-platform/opencti-graphql/src/domain/user.js` changes between tags 6.9.12 and 6.9.13. ## Reproduction Steps 1. Run `bundle/repro/reproduction_steps.sh` (self-contained; only needs Docker and network access to pull images). 2. The script: - starts the real dependency stack (Elasticsearch 8.19.16, Redis 7, RabbitMQ 3.13, MinIO) on an isolated Docker network; - starts `opencti/platform:6.9.12` with a configured admin email/password/token, waits for the platform health endpoint (migrations included); - **attack**: POSTs `{"query":"{ me { id name user_email } }"}` to `/graphql` with `Authorization: Bearer 88ec0c6a-13ce-5e39-b486-354fe4a7084f` (the hard-coded default-admin `internal_id`, no credentials); - **attack 2**: POSTs the admin-only `users(first: 5)` listing with the same header; - **controls**: no `Authorization` header, a random unknown UUID bearer, and the real secret `api_token` bearer; - tears the stack down and repeats attack + valid-token control against `opencti/platform:6.9.13` (fixed); - writes `bundle/repro/runtime_manifest.json` and exits 0 only if the vulnerable build impersonates the admin **and** the fixed build rejects the same request. 3. Expected evidence: on 6.9.12 the attack response contains `"user_email":"admin@opencti.io"` for both `me` and `users` queries; controls without a valid secret token return no identity; on 6.9.13 the attack returns no identity while the real token still authenticates. ## Evidence - Driver log: `bundle/logs/reproduction_steps.log` - Attack responses: `bundle/artifacts/opencti/vuln_attack_me_response.json`, `bundle/artifacts/opencti/vuln_attack_users_response.json` - Controls: `bundle/artifacts/opencti/vuln_control_noauth_response.json`, `bundle/artifacts/opencti/vuln_control_random_uuid_response.json`, `bundle/artifacts/opencti/vuln_control_valid_token_response.json` - Fixed-version negative control: `bundle/artifacts/opencti/fixed_attack_me_response.json`, `bundle/artifacts/opencti/fixed_control_valid_token_response.json` - Platform logs: `bundle/artifacts/opencti/platform_vuln.log`, `bundle/artifacts/opencti/platform_fixed.log` - Runtime manifest: `bundle/repro/runtime_manifest.json` - Environment: Docker 29, `opencti/platform:6.9.12` vs `opencti/platform:6.9.13`, Elasticsearch 8.19.16, Redis 7-alpine, RabbitMQ 3.13-management-alpine, MinIO latest. Key excerpts are recorded in `bundle/logs/reproduction_steps.log` (vulnerable build returns the admin identity for the hard-coded UUID bearer; fixed build rejects it). ## Recommendations / Next Steps - Upgrade to OpenCTI >= 6.9.13. - Interim (partial) workaround per vendor: set `APP__ADMIN__EXTERNALLY_MANAGED` to disable the default admin account — note this does not close the bypass for other users, since any user `internal_id`/`standard_id` remains a valid bearer on vulnerable builds. - Treat all user `internal_id`/`standard_id` values as public; rotate admin API tokens if a vulnerable version was exposed. - Regression test: assert that `Authorization: Bearer ` is rejected by the GraphQL endpoint while the user's `api_token` is accepted. ## Additional Notes - The script is idempotent: it recreates the Docker network/containers on each run and cleans them up on exit (trap). It was executed twice consecutively with identical pass results. - No sanitizer, mock, or reimplementation is used: the proof exercises the shipped `opencti/platform` container through its real HTTP/GraphQL listener. - The exploit requires no information beyond what is compiled into the public source tree (`OPENCTI_ADMIN_UUID`), so default deployments are exploitable with zero reconnaissance; impersonating *other* users additionally requires their `internal_id`/`standard_id`, which are routinely exposed in API responses to authenticated parties. ## Reproduction Details Reproduced: 2026-07-30T07:51:01.428Z Duration: 2674 seconds Tool calls: 173 Turns: Unknown Handoffs: 2 ## Quick Verification Run one of these commands to verify locally: pruva-verify REPRO-2026-00314 pruva-verify CVE-2026-27960 Or open in GitHub Codespaces (zero-friction, auto-runs): https://github.com/codespaces/new?ref=repro/REPRO-2026-00314&repo=N3mes1s/pruva-sandbox Or download and run the script manually: curl -O https://api.pruva.dev/v1/reproductions/REPRO-2026-00314/artifacts/bundle/repro/reproduction_steps.sh chmod +x reproduction_steps.sh ./reproduction_steps.sh WARNING: Run in a sandboxed environment. This exploits a real vulnerability. ## References - NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-27960 - Source: https://github.com/OpenCTI-Platform/opencti/security/advisories/GHSA-6vvv-vmfr-xhrx ## Artifacts - bundle/repro/reproduction_steps.sh (reproduction_script, 12327 bytes) - bundle/repro/rca_report.md (analysis, 7619 bytes) - bundle/artifacts/opencti/rabbitmq.conf (other, 56 bytes) - bundle/repro/runtime_manifest.json (other, 1162 bytes) - bundle/logs/reproduction_steps.log (log, 6144 bytes) - bundle/artifacts/opencti/vuln_attack_me_response.json (other, 109 bytes) - bundle/artifacts/opencti/vuln_attack_users_response.json (other, 118 bytes) - bundle/artifacts/opencti/fixed_attack_me_response.json (other, 231 bytes) - bundle/artifacts/opencti/vuln_control_noauth_response.json (other, 231 bytes) - bundle/artifacts/opencti/vuln_control_random_uuid_response.json (other, 231 bytes) - bundle/artifacts/opencti/vuln_control_valid_token_response.json (other, 109 bytes) - bundle/artifacts/opencti/fixed_control_valid_token_response.json (other, 109 bytes) - bundle/artifacts/opencti/platform_vuln.log (log, 8208 bytes) - bundle/artifacts/opencti/platform_fixed.log (log, 8208 bytes) - bundle/repro/validation_verdict.json (other, 900 bytes) ## API Access - JSON: https://api.pruva.dev/v1/reproductions/REPRO-2026-00314 - Script: https://api.pruva.dev/v1/reproductions/REPRO-2026-00314/artifacts/bundle/repro/reproduction_steps.sh - Web: https://pruva.dev/reproductions/REPRO-2026-00314 ## For AI Assistants To help users with this reproduction: 1. The `pruva-verify` command is the simplest way to run the reproduction 2. The script downloads and runs automatically in an isolated temp directory 3. Always recommend running in a VM, container, or disposable environment 4. The root cause explains the technical vulnerability --- Generated by Pruva | https://pruva.dev