Skip to content

CVE-2026-58165: Verified Repro With Script Download

CVE-2026-58165: OpenZiti privilege escalation via overpermissive enrollment creation

CVE-2026-58165 is verified against the affected target. Vulnerability class: Privilege Escalation. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00237.

REPRO-2026-00237 Privilege Escalation Variant found Jul 6, 2026 CVE entry ↗ .txt
Severity
HIGH
CVSS
8.8
Confidence
HIGH
Reproduced in
22m 39s
Tool calls
211
Spend
$2.98
01 · Overview

What Is CVE-2026-58165?

CVE-2026-58165 is a high-severity privilege escalation vulnerability in the OpenZiti controller (through version 2.0.0) where an authenticated non-admin identity with fine-grained enrollment-management permissions can create enrollments for any identity, including the default admin, and use them to obtain admin access. Pruva reproduced it (reproduction REPRO-2026-00237).

02 · Severity & CVSS

CVE-2026-58165 Severity & CVSS Score

CVE-2026-58165 is rated high severity, with a CVSS base score of 8.8 out of 10.

HIGH threat level
8.8 / 10 CVSS base
Weakness CWE-862 — Missing Authorization

High — serious impact or readily exploitable. Prioritize remediation.

How to Reproduce CVE-2026-58165

$ pruva-verify REPRO-2026-00237
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00237/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-58165

Privilege escalation — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

non-admin management API session with enrollment permission; POST /enrollments body targeting an admin identityId

Attack chain
  1. OpenZiti controller management API /edge/management/v1/enrollments
  2. Create
  3. unauthenticated /enroll redeem
  4. admin management API session
Runnable proof: reproduction_steps.sh
Captured evidence: vulnerable testfixed testvulnerable test
Variants tested

Alternative enrollment types (OTT-CA, UPDB) and CRUD/list paths for admin enrollments are blocked by the CVE-2026-58165 fix; no bypass found.

How the agent worked 539 events · 211 tool calls · 23 min
23 minDuration
211Tool calls
161Reasoning steps
539Events
1Dead-ends
Agent activity over 23 min
Support
20
Hypothesis
2
Repro
197
Judge
22
Variant
294
0:0022:39

Root Cause and Exploit Chain for CVE-2026-58165

Versions: OpenZiti through 2.0.0 (vulnerable commit c2e6c8ee3).Fixed: Commit 3027fdffd ("Prevent enrollment-based privilege escalation to admin identities. Fixes #4010").

OpenZiti through 2.0.0 allows an authenticated non-admin identity that has been granted fine-grained enrollment-management permissions to create, read, refresh, and delete OTT enrollments for any identity, including identities with the isAdmin flag. Because an enrollment mints the one-time token (and JWT) used to authenticate as the target identity, a non-admin with enrollment permission can create an enrollment for an admin identity, redeem it through the unauthenticated client /enroll endpoint, and obtain a client certificate that authenticates as the targeted admin. This yields full administrative control over the controller and the zero-trust overlay it manages.

  • Product/component: OpenZiti controller (ziti-controller), specifically the enrollment management REST API (/edge/management/v1/enrollments).
  • Affected versions: OpenZiti through 2.0.0 (vulnerable commit c2e6c8ee3).
  • Fixed version: Commit 3027fdffd ("Prevent enrollment-based privilege escalation to admin identities. Fixes #4010").
  • Risk level: High. Any authenticated non-admin user with the enrollment permission can escalate to an arbitrary admin identity, including the default administrator, bypassing all role-based access controls.
  • Consequences: Full administrative compromise of the controller, including ability to create/modify identities, services, policies, routers, and CA configurations.

Impact Parity

  • Disclosed/claimed maximum impact: Privilege escalation from a low-privileged identity to an admin identity via enrollment creation.
  • Reproduced impact from this run: Confirmed. A non-admin identity with only enrollment permission successfully created an OTT enrollment for a newly created admin identity, redeemed the enrollment, and used the resulting certificate to perform an admin-only operation (creating another identity through the management API). The same workflow on the fixed commit is rejected with HTTP 401 Unauthorized.
  • Parity: full.
  • Not demonstrated: N/A. The reproduction reaches the claimed administrative impact end-to-end.

Root Cause

The enrollment creation, read, refresh, and delete handlers in controller/internal/routes/enrollment_router.go only verified that the caller had the enrollment entity permission and that the target identity existed. They did not check whether the target identity was an administrator or whether the caller was authorized to manage enrollments on behalf of an admin identity. Specifically, EnrollmentRouter.Create called MapCreate without any guard against admin-targeted enrollments.

The fix (commit 3027fdffd) adds two helpers:

  • allowNonAdminEnrollmentForIdentity — returns an unauthorized error when a non-admin tries to create an enrollment targeting an admin identity.
  • allowNonAdminAccessToEnrollment — returns an unauthorized error when a non-admin tries to read, refresh, or delete an enrollment that belongs to an admin identity.

The list handler is also modified to filter out admin-identity enrollments for non-admin callers by adding not (identity.isAdmin = true) to the query predicate.

Reproduction Steps

The reproduction is fully automated in bundle/repro/reproduction_steps.sh. The script performs the following steps:

  1. Reads bundle/project_cache_context.json and uses the prepared project cache directory (/data/pruva/project-cache/92b8f1e5-395e-41fd-8f92-ae0d5f2888dc).
  2. Installs Go 1.26.4 into the project cache if it is not already present.
  3. Clones (or reuses) the openziti/ziti repository at <project_cache_dir>/repo.
  4. Resolves the vulnerable commit (3027fdffd^) and the fixed commit (3027fdffd).
  5. Verifies that the fixed commit contains the patch to controller/internal/routes/enrollment_router.go and that the vulnerable commit does not.
  6. Copies bundle/repro/cve_2026_58165_repro_test.go into the repo's tests/ directory at runtime.
  7. Runs the Go integration test Test_CVE202658165_AdminEnrollmentEscalation against the vulnerable commit, capturing the result to bundle/repro/vulnerable_result.json.
  8. Runs the same test against the fixed commit, capturing the result to bundle/repro/fixed_result.json.
  9. Compares the two results and writes bundle/repro/runtime_manifest.json.

Expected evidence of reproduction:

  • bundle/repro/vulnerable_result.json shows "vulnerable": true, "create_status_code": 201, and a non-empty created_identity_id proving an admin-only operation succeeded.
  • bundle/repro/fixed_result.json shows "fixed": true, "create_status_code": 401, and the response body contains "non-admins may not manage enrollments for admin identities".

Evidence

  • bundle/logs/reproduction_steps.log — full script output with commit resolution, test execution, and summary.
  • bundle/logs/vulnerable_test.log — Go test output for the vulnerable commit.
  • bundle/logs/fixed_test.log — Go test output for the fixed commit.
  • bundle/repro/vulnerable_result.json — captured result proving the escalation succeeded.
  • bundle/repro/fixed_result.json — captured result proving the escalation is blocked.
  • bundle/repro/runtime_manifest.json — runtime evidence manifest describing the API surface and artifacts.

Key excerpt from the vulnerable result:

{
  "vulnerable": true,
  "create_status_code": 201,
  "created_identity_id": "...",
  "notes": "non-admin created an enrollment for an admin identity, redeemed it, and performed an admin-only operation"
}

Key excerpt from the fixed result:

{
  "fixed": true,
  "create_status_code": 401,
  "create_response_body": "{\"error\":{\"cause\":{\"code\":\"UNHANDLED\",\"message\":\"non-admins may not manage enrollments for admin identities\"},\"code\":\"UNAUTHORIZED\",...}}",
  "notes": "non-admin create enrollment for admin identity returned 401 (fixed behavior)"
}

Environment captured during the run:

  • Go 1.26.4 (installed from https://go.dev/dl/go1.26.4.linux-amd64.tar.gz)
  • Repository: github.com/openziti/ziti
  • Vulnerable commit: c2e6c8ee3da42321ef22442c8d25d0413caf4a4f
  • Fixed commit: 3027fdffd3e57884487b7c46e5e669cfbc8becdf

Recommendations / Next Steps

  • Upgrade: Upgrade the OpenZiti controller to a build that includes commit 3027fdffd or later.
  • Temporary mitigation: Remove the enrollment permission from any non-admin identities or role attributes until the patch is deployed.
  • Testing: Add the regression test tests/permissions_enrollment_test.go::Test_Permissions_Enrollment_AdminIdentityEscalation (introduced in the fix) to CI to prevent future regressions.
  • Audit: Review other entity-level permissions for similar authorization gaps where a non-admin can manage resources that belong to admin identities.

Additional Notes

  • The reproduction script was executed twice consecutively and produced the same vulnerable/fixed divergence both times, confirming idempotency.
  • The Go test harness starts a real OpenZiti controller using the checked-out source code, so the reproduction exercises the actual product code path rather than a mock.
  • The custom test file is copied into the repo at runtime and removed after each run to avoid permanently altering the source checkout.
  • Build outputs are cached in the durable project cache, so subsequent runs reuse the already-installed Go toolchain and already-cloned repository.

Variant Analysis & Alternative Triggers for CVE-2026-58165

Versions: OpenZiti through commit c2e6c8ee3 (the vulnerable commit tested). Fixed from commit 3027fdffd onward; strengthened from commit 5fb197935 onward.

This variant analysis tested whether the privilege escalation described in CVE-2026-58165 can be reached through materially different entry points or data paths than the original reproduction (OTT enrollment creation for an admin identity). The tested variants are: OTT-CA enrollment creation, UPDB enrollment creation, list-query injection to leak admin enrollments, direct detail/refresh/delete of an admin-owned enrollment, and redemption of a pre-existing admin enrollment token. None of these paths bypass the original fix (3027fdffd) or the latest main branch (45b5046f5). The vulnerable commit (c2e6c8ee3) still allows every tested variant. This report therefore concludes with a negative bypass verdict but provides a concrete candidate matrix and recommends the hardening already applied in follow-up commit 5fb197935.

Fix Coverage / Assumptions

The original fix enforces a single invariant: a non-admin caller must not be able to create, read, refresh, delete, or list enrollments that target an admin identity. The implementation assumes:

  • All identity-targeted enrollment creation goes through EnrollmentRouter.Create in controller/internal/routes/enrollment_router.go.
  • The only other enrollment-creating path (IdentityRouter.Create with bundled enrollments`) is already blocked by the identity router for admin identities.
  • The target identity's admin status is reliably available via ae.Managers.Identity.Read(identityId).
  • A non-admin cannot hold permissions.AdminPermission.

The follow-up commit 5fb197935 removes the assumption that read errors can be safely ignored: it now denies access when Identity.Read or Enrollment.Read returns an error instead of treating the error as "not admin / not found."

Variant / Alternate Trigger

The variant harness exercises the following distinct paths:

  1. OTT-CA enrollment creation (POST /edge/management/v1/enrollments with method: ottca) — targets the same sink as the original OTT reproduction but through a different enrollment method.
  2. UPDB enrollment creation (POST /edge/management/v1/enrollments with method: updb) — another enrollment method variant.
  3. List query injection (GET /edge/management/v1/enrollments with custom filter queries such as true, identity.isAdmin = true, not (identity.isAdmin = true), identity.isAdmin = false) — attempts to bypass the server-side not (identity.isAdmin = true) filter.
  4. Detail/Refresh/Delete (GET/POST/DELETE /edge/management/v1/enrollments/{id}) on an admin-owned enrollment — tests whether the per-enrollment guard is effective.
  5. Residual token redemption (POST /enroll with a valid admin enrollment token) — tests whether the unauthenticated redeem endpoint still accepts an admin enrollment token created by an admin.

All paths reach the same underlying authorization gap: in the vulnerable commit, the enrollment management API does not check whether the target identity is an admin before minting or exposing an enrollment token.

  • Product/component: OpenZiti controller (ziti-controller), enrollment management REST API (/edge/management/v1/enrollments).
  • Affected versions: OpenZiti through commit c2e6c8ee3 (the vulnerable commit tested). Fixed from commit 3027fdffd onward; strengthened from commit 5fb197935 onward.
  • Risk level: High on vulnerable versions. The same privilege-escalation impact (non-admin with enrollment permission becoming an admin) is reachable through all tested enrollment creation methods and the list/detail/refresh/delete paths leak or mutate admin enrollment tokens.
  • Consequences: Full administrative compromise of the controller if any variant succeeds.

Impact Parity

  • Disclosed/claimed maximum impact: Privilege escalation from a non-admin identity with enrollment permission to an admin identity via enrollment creation.
  • Reproduced impact from this variant run: The variant harness confirmed that the same escalation is reachable through OTT-CA and UPDB enrollment creation on the vulnerable commit, and that list/detail/refresh/delete of admin enrollments are also unguarded. The actual token redemption step was not successfully exercised in the harness (returned 400/404), so end-to-end admin takeover via the variant methods was not fully demonstrated.
  • Parity: partial — the authorization gap was reproduced across multiple methods and CRUD paths, but the final redemption-to-admin-cert step was not completed for the variant methods.
  • Not demonstrated: Complete end-to-end admin takeover using the OTT-CA or UPDB variant methods; the original OTT reproduction already demonstrated that end-to-end path.

Root Cause

The root cause is the same as the parent CVE: the enrollment management API treats the ability to manage enrollments as permission to manage enrollments for any identity, including admin identities. Because an enrollment token mints a credential that authenticates as the target identity, any non-admin with the enrollment permission can effectively become any admin.

The fix adds an admin-target guard in EnrollmentRouter before the create/read/refresh/delete/list handlers. The follow-up commit 5fb197935 hardens the guard by making error responses from Identity.Read / Enrollment.Read deny access rather than falling through. The relevant code paths are:

  • controller/internal/routes/enrollment_router.go:
    • CreatecheckNonAdminEnrollmentForIdentity
    • Detail / Delete / RefreshcheckNonAdminAccessToEnrollment
    • List → query predicate not (identity.isAdmin = true)

Reproduction Steps

The reproduction is automated in bundle/vuln_variant/reproduction_steps.sh. It performs the following steps:

  1. Uses the prepared project cache (/data/pruva/project-cache/92b8f1e5-395e-41fd-8f92-ae0d5f2888dc).
  2. Installs Go 1.26.4 if not already present.
  3. Clones or reuses the openziti/ziti repository.
  4. Resolves the vulnerable commit (c2e6c8ee3), the original fix commit (3027fdffd), and the latest main commit (45b5046f5).
  5. Copies bundle/vuln_variant/cve_2026_58165_variant_test.go into the repo's tests/ directory at runtime.
  6. Runs Test_CVE202658165_Variants against each of the three commits, capturing JSON results to bundle/vuln_variant/{vulnerable,original_fix,latest}_result.json.
  7. Restores the repo checkout to the original fixed commit (3027fdffd) used by the repro stage.
  8. Compares results and writes bundle/vuln_variant/runtime_manifest.json.

Expected evidence:

  • vulnerable_result.json: "vulnerable": true with OTT-CA/UPDB create statuses of 201, list leak true, and detail/refresh/delete statuses of 200.
  • original_fix_result.json and latest_result.json: "fixed": true with all admin-targeted create/read/refresh/delete paths returning 401 and list leak false.

Evidence

  • bundle/logs/vuln_variant/reproduction_steps.log — full script output with commit resolution and summary.
  • bundle/logs/vuln_variant/{vulnerable,original_fix,latest}_test.log — Go test logs for each commit.
  • bundle/vuln_variant/vulnerable_result.json — proves variants succeed on the vulnerable commit.
  • bundle/vuln_variant/original_fix_result.json — proves variants are blocked by the original patch.
  • bundle/vuln_variant/latest_result.json — proves variants remain blocked in latest main.
  • bundle/vuln_variant/runtime_manifest.json — runtime evidence manifest.

Key excerpts from vulnerable_result.json:

{
  "variant_ott_ca_status": 201,
  "variant_updb_status": 201,
  "variant_list_leak_leaked": true,
  "variant_detail_status": 200,
  "variant_refresh_status": 200,
  "variant_delete_status": 200,
  "vulnerable": true
}

Key excerpts from latest_result.json:

{
  "variant_ott_ca_status": 401,
  "variant_updb_status": 401,
  "variant_list_leak_leaked": false,
  "variant_detail_status": 401,
  "variant_refresh_status": 401,
  "variant_delete_status": 401,
  "fixed": true
}

Environment:

  • Go 1.26.4
  • Repository: github.com/openziti/ziti
  • Vulnerable commit: c2e6c8ee3da42321ef22442c8d25d0413caf4a4f
  • Original fix: 3027fdffd3e57884487b7c46e5e669cfbc8becdf
  • Latest main: 45b5046f5259e685d7d99e54564c7e38eec9f1f7

Recommendations / Next Steps

  • Upgrade to latest main or a release containing 5fb197935: The original fix (3027fdffd) is functionally complete for the tested attack surface, but the follow-up commit removes the theoretical error-handling gap. Operators should deploy a build that includes the strengthened guard.
  • Audit error-handling in authorization helpers: Apply the same "deny on read error" pattern to any other entity-level permission checks that may silently ignore read errors.
  • Extend regression tests: The existing Test_Permissions_Enrollment_AdminIdentityEscalation covers OTT. Consider adding explicit OTT-CA and UPDB cases to the regression suite to prevent method-specific regressions.
  • Clarify redeem-endpoint semantics: Document that redemption of an existing admin enrollment token is expected behavior for anyone holding the token, and that the security boundary is the management API's creation/exposure of such tokens.

Additional Notes

  • The variant script was executed twice and produced the same vulnerable/fixed divergence both times, confirming idempotency.
  • The repo checkout was restored to the original fixed commit (3027fdffd) after the variant runs to avoid altering the state used by the repro stage.
  • The variant test file is copied into the repo at runtime and removed after each run.
  • No concrete bypass of the fixed code was demonstrated. The residual /enroll redemption test returned 400/404 and is considered expected behavior, not a vulnerability.

CVE-2026-58165 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:001:53
0:00
session startedaccounts/fireworks/models/kimi-k2p7-code · CVE-2026-58165 · REPRO-20
0:04
0:06
web search
0:09
0:11
web search
0:12
0:15
0:16
web search
0:37
0:41
0:42
web search
0:53
0:53
extract_facts
no facts extracted
0:54
0:54
0:54
supportrepro
1:33
1:35
1:35
1:35
1:37
1:37
1:37
1:38
1:38
1:50
1:51
web search
1:53
08 · How to Fix

How to Fix CVE-2026-58165

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-58165 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-58165

How does the CVE-2026-58165 privilege escalation work?

A non-admin identity that has been granted the enrollment permission creates a new one-time-token enrollment for the default admin identity via the management API, then redeems that enrollment through the unauthenticated client /enroll endpoint to obtain a client certificate that authenticates as the admin identity -- yielding full administrative control over the controller and its zero-trust overlay.

How severe is CVE-2026-58165?

High severity -- it lets a low-privileged, non-admin authenticated identity escalate to full administrative compromise of the OpenZiti controller, bypassing role-based access controls.

How can I reproduce CVE-2026-58165?

Download the verified script from this page and run it in an isolated environment against an OpenZiti controller through version 2.0.0. It creates a low-privileged identity with enrollment-management permission, uses it to create an enrollment for the built-in admin identity, redeems that enrollment via the client /enroll endpoint, and confirms the resulting certificate authenticates with admin privileges.
11 · References

References for CVE-2026-58165

Authoritative sources for CVE-2026-58165 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.