Skip to content

CVE-2026-58377: Verified Repro With Script Download

CVE-2026-58377: JeecgBoot broken access control privilege escalation

CVE-2026-58377 is verified against jeecgboot/JeecgBoot · github. Affected versions: JeecgBoot through 3.9.2 (all versions up to and including 3.9.2). Vulnerability class: Privilege Escalation. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00250.

REPRO-2026-00250 jeecgboot/JeecgBoot · github Privilege Escalation Variant found Jul 6, 2026 CVE entry ↗ .txt
Severity
HIGH
CVSS
8.1
Confidence
HIGH
Reproduced in
31m 46s
Tool calls
271
Spend
$4.34
01 · Overview

What Is CVE-2026-58377?

CVE-2026-58377 is a high-severity broken access control vulnerability in JeecgBoot that lets an authenticated low-privileged user escalate privileges. Pruva reproduced it (reproduction REPRO-2026-00250).

02 · Severity & CVSS

CVE-2026-58377 Severity & CVSS Score

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

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

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

Affected jeecgboot/JeecgBoot Versions

jeecgboot/JeecgBoot · github versions JeecgBoot through 3.9.2 (all versions up to and including 3.9.2) are affected.

How to Reproduce CVE-2026-58377

$ pruva-verify REPRO-2026-00250
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00250/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-58377

Authorization bypass — 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

authenticated low-privileged user JWT token (ceshi/test role, password 123456)

Attack chain
  1. POST /jeecg-boot/sys/login (ceshi)
  2. GET/POST/DELETE /jeecg-boot/openapi/auth/* with X-Access-Token header
Variants tested

Alternate trigger of CVE-2026-58377's broken access control. The same root cause (missing Shiro @RequiresRoles annotations + Shiro filter chain mapping /** to the auth-only jwt filter) is reachable via two additional OpenAPI controllers the parent reproduction never tested: OpenApiLogController (/openapi/record/*) — f…

How the agent worked 629 events · 271 tool calls · 32 min
32 minDuration
271Tool calls
172Reasoning steps
629Events
1Dead-ends
Agent activity over 32 min
Support
22
Repro
390
Judge
28
Variant
185
0:0031:46

Root Cause and Exploit Chain for CVE-2026-58377

Versions: JeecgBoot through 3.9.2 (commit 7df07a823fd558be857d0208ccae96342539fbc1, tag v3.9.2)

JeecgBoot v3.9.2 contains a broken access control vulnerability in the OpenAPI credential management module. The OpenApiAuthController (/openapi/auth/*) and OpenApiPermissionController (/openapi/permission/*) expose full CRUD endpoints for managing OpenAPI AK/SK credential pairs without any Shiro authorization annotations (@RequiresRoles or @RequiresPermissions). In contrast, the sibling OpenApiController (/openapi/*) correctly annotates its mutating endpoints with @RequiresRoles({"admin"}). Because the Shiro filter chain maps /** to the jwt filter (authentication-only, no authorization), any authenticated user — including low-privileged users with only a "test" role — can list, create, edit, and delete all OpenAPI credential pairs, with the list endpoint returning secret keys (SK) in plaintext. This enables credential theft and unauthorized invocation of the OpenAPI surface, including endpoints owned by administrator accounts.

  • Package/component affected: jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiAuthController.java and OpenApiPermissionController.java
  • Affected versions: JeecgBoot through 3.9.2 (commit 7df07a823fd558be857d0208ccae96342539fbc1, tag v3.9.2)
  • Risk level: HIGH (CVSS 4.0: 8.6)
  • Consequences:
    • Any authenticated low-privilege user can read all OpenAPI AK/SK credential pairs in plaintext, including those belonging to administrators.
    • Low-privilege users can create, modify, and delete credential records without administrative authorization.
    • Stolen credentials can be used to invoke the OpenAPI surface (/openapi/call/**, which is mapped to anon and only checks the appkey header), impersonating the credential owner — including administrators.

Impact Parity

  • Disclosed/claimed maximum impact: Privilege escalation — authenticated low-privilege users perform full CRUD on OpenAPI credentials, read admin secret keys in plaintext, and can invoke privileged API routes as the credential owner.
  • Reproduced impact from this run: A low-privileged user (ceshi, role=test) successfully:
    1. Read admin's AK/SK credentials in plaintext via GET /openapi/auth/list (ak=ak-pFjyNHWRsJEFWlu6, sk=4hV5dBrZtmGAtPdbA5yseaeKRYNpzGsS, owner=admin)
    2. Created a new OpenAPI credential via POST /openapi/auth/add (persisted to database)
    3. Deleted an OpenAPI credential via DELETE /openapi/auth/delete
    4. Generated a new AK/SK pair via GET /openapi/auth/genAKSK
    5. Was correctly rejected by the protected POST /openapi/add endpoint (has @RequiresRoles({"admin"})), confirming the authorization gap is specific to OpenApiAuthController
  • Parity: full — all claimed CRUD operations on OpenAPI credentials were demonstrated, including plaintext secret key exposure and the negative control showing the sibling controller is properly protected.
  • Not demonstrated: Actual invocation of /openapi/call/** with stolen credentials to impersonate the admin (the second root cause described in the advisory). The primary broken access control on credential management endpoints is fully proven.

Root Cause

The Shiro security configuration (ShiroConfig.java) maps all URL paths (/**) to the jwt filter, which performs authentication only (validates JWT token existence and signature). Authorization (role/permission checks) is enforced exclusively through method-level annotations such as @RequiresRoles and @RequiresPermissions.

The OpenApiAuthController at /openapi/auth/* exposes seven endpoints — GET /list, POST /add, PUT /edit, DELETE /delete, DELETE /deleteBatch, GET /queryById, and GET /genAKSK — none of which carry any Shiro authorization annotation. This means the Shiro authorization layer is never triggered for these endpoints; any user who passes JWT authentication can access them.

In contrast, the sibling OpenApiController at /openapi/* correctly annotates its mutating endpoints (/add, /edit, /delete, /deleteBatch) with @RequiresRoles({"admin"}), which triggers Shiro's AuthorizationAttributeSourceAdvisor and enforces that only users with the "admin" role can perform those operations.

The OpenApiPermissionController at /openapi/permission/* has the same absence of authorization annotations on its POST /add and GET /getOpenApi endpoints.

Additionally, the /openapi/call/** path is mapped to anon (no authentication required at all), and the OpenApiController.call() method only reads the appkey header to look up the auth record without verifying the secret key signature, enabling unauthorized API invocation with stolen credentials.

Fix commit: Not yet publicly available in the main branch as of this analysis. The fix would involve adding @RequiresRoles({"admin"}) annotations to all mutating endpoints in OpenApiAuthController and OpenApiPermissionController, and implementing SK signature verification in the call() method.

Reproduction Steps

  1. Reference: bundle/repro/reproduction_steps.sh
  2. What the script does:
    • Resolves the JeecgBoot v3.9.2 repository from the prepared project cache
    • Installs JDK 17, Maven, Redis, and MariaDB if not present
    • Starts MariaDB and Redis, creates the jeecg-boot database, and imports the seed SQL (which includes an admin-owned OpenAPI credential)
    • Uses the pre-built jeecg-system-start-3.9.2.jar (or builds it with Maven if not present)
    • Starts the JeecgBoot Spring Boot application with login captcha disabled (--jeecg.firewall.enableLoginCaptcha=false)
    • Logs in as the low-privileged user ceshi (role: test, password: 123456) and obtains a JWT token
    • Calls GET /openapi/auth/list with the low-priv JWT to read all AK/SK credentials in plaintext
    • Calls POST /openapi/auth/add to create a credential as the low-priv user (verified in database)
    • Calls DELETE /openapi/auth/delete to delete a credential as the low-priv user
    • Calls GET /openapi/auth/genAKSK to generate an AK/SK pair as the low-priv user
    • Negative control: Calls POST /openapi/add (which has @RequiresRoles({"admin"})) and confirms it rejects the low-priv user with "Subject does not have role [admin]"
    • Admin positive control: Logs in as admin and confirms admin can access POST /openapi/add
    • Writes the runtime manifest with all proof artifacts
  3. Expected evidence of reproduction:
    • artifacts/http/openapi_auth_list_response.json — shows success: true with admin's AK/SK in plaintext
    • artifacts/http/openapi_auth_add_response.json — shows success: true (low-priv user created credential)
    • artifacts/http/openapi_auth_delete_response.json — shows success: true (low-priv user deleted credential)
    • artifacts/http/openapi_auth_genAKSK_response.json — shows success: true with generated AK/SK pair
    • artifacts/http/openapi_add_negcontrol_response.json — shows success: false with "Subject does not have role [admin]"
    • artifacts/http/openapi_add_admin_response.json — shows success: true (admin can access protected endpoint)

Evidence

  • Log file locations:

    • bundle/logs/reproduction_steps.log — full script execution log
    • bundle/logs/jeecgboot_app.log — JeecgBoot application startup and runtime log
    • bundle/artifacts/http/*.json — HTTP request/response captures for each proof
  • Key excerpts proving reproduction:

    PROOF 1 — Low-priv user reads admin's AK/SK in plaintext (openapi_auth_list_response.json):

    {
      "success": true,
      "code": 200,
      "result": {
        "records": [{
          "id": "1922164194775056386",
          "name": "scott",
          "ak": "ak-pFjyNHWRsJEFWlu6",
          "sk": "4hV5dBrZtmGAtPdbA5yseaeKRYNpzGsS",
          "systemUserId": "e9ca23d68d884d4ebb19d07889727dae",
          "systemUserId_dictText": "admin"
        }]
      }
    }
    

    NEGATIVE CONTROL — Protected endpoint rejects low-priv user (openapi_add_negcontrol_response.json):

    {
      "success": false,
      "message": "Subject does not have role [admin]",
      "code": 500
    }
    
  • Environment details:

    • JeecgBoot v3.9.2 (Spring Boot 3.5.5, Java 17.0.19)
    • MariaDB 11.8.6, Redis 8.0.5
    • Low-priv user: ceshi (role: test, tenant: 1001)
    • Admin user: admin (role: admin, tenant: 1000)
    • Application context path: /jeecg-boot, port 8080

Recommendations / Next Steps

  • Fix approach: Add @RequiresRoles({"admin"}) annotations to all mutating endpoints in OpenApiAuthController (/add, /edit, /delete, /deleteBatch) and OpenApiPermissionController (/add). Consider whether the /list, /queryById, and /genAKSK endpoints should also be admin-only or restricted to credentials owned by the requesting user.
  • Additional fix: Implement secret key (SK) signature verification in OpenApiController.call() to prevent unauthorized API invocation with stolen AK-only credentials.
  • Upgrade guidance: Upgrade to a version that includes the fix once available. In the interim, restrict access to /openapi/auth/** and /openapi/permission/** via reverse proxy rules (e.g., nginx) to admin-only users.
  • Testing recommendations: Add integration tests that verify non-admin users receive 403/510 responses when accessing OpenAPI credential management endpoints. Add tests that verify SK signature validation in the call endpoint.

Additional Notes

  • Idempotency confirmation: The script was run twice consecutively, both runs succeeded with exit code 0 and all 4 proofs confirmed. The script drops and recreates the database on each run, ensuring a clean state.
  • Captcha handling: Login captcha was disabled via --jeecg.firewall.enableLoginCaptcha=false Spring Boot argument to enable automated login. This does not affect the vulnerability — the captcha is an authentication-layer control, while the vulnerability is in the authorization layer.
  • Port conflict: Apache2 was found occupying port 8080 in the test environment; the script kills it before starting JeecgBoot.
  • Seed data: The seed SQL includes one admin-owned OpenAPI credential (name: scott, ak: ak-pFjyNHWRsJEFWlu6, sk: 4hV5dBrZtmGAtPdbA5yseaeKRYNpzGsS, systemUserId: admin), which serves as the target credential for the privilege escalation proof.

Variant Analysis & Alternative Triggers for CVE-2026-58377

Versions: versions (as tested): JeecgBoot v3.9.2

The parent CVE-2026-58377 reproduction validated broken access control only on OpenApiAuthController (/openapi/auth/*). This variant stage confirmed that the same root cause — missing Shiro @RequiresRoles/@RequiresPermissions annotations combined with a Shiro filter chain that maps /** to the authentication-only jwt filter — is reachable through two additional, materially distinct entry points that the parent reproduction never tested:

  • VARIANT A (primary, NEW surface not mentioned anywhere in the CVE description): OpenApiLogController (/openapi/record/*) exposes full CRUD (/list, /add, /edit, /delete, /deleteBatch, /queryById) over the OpenAPI call/audit log table. A low-privileged authenticated user (role test) can read every audit log, create log rows attributed to another user's/admin's auth record, and delete log rows — i.e. audit-log disclosure, forgery, and tampering/destruction.
  • VARIANT B (corroborating; CVE-claimed surface that the repro stage did NOT actually exercise): OpenApiPermissionController (/openapi/permission/*) exposes POST /add (grant OpenAPI permissions on any auth record, including the admin's) and GET /getOpenApi (retrieve the OpenAPI definitions mapped to any auth record).

Because no official patch exists (CVE status Deferred; the latest release tag is v3.9.2, which is the vulnerable version itself; and master HEAD 32a8dfe94f014f7a9d472bce83e08bb8cf9c10c3 is byte-identical to v3.9.2 for all four OpenAPI controllers), this is an alternate trigger confirmed on the latest available code, not merely an old-version issue. The "fixed-version" check reduces to testing the latest release / default branch, which is identical to the vulnerable code.

Fix Coverage / Assumptions

There is no fix/patch for CVE-2026-58377 as of this analysis:

  • NVD/cvefeed.io lists the CVE status as Deferred.
  • SecAlerts lists "No Patch" for CVE-2026-58377.
  • The highest release tag is v3.9.2 (commit 7df07a823fd558be857d0208ccae96342539fbc1), which is the vulnerable version itself.
  • master HEAD (32a8dfe94f014f7a9d472bce83e08bb8cf9c10c3) was fetched and the four OpenAPI controller source files are byte-identical to the v3.9.2 versions (verified by diff against the project-cache checkout).

The hypothetical fix the parent RCA recommends is: add @RequiresRoles({"admin"}) to all mutating endpoints in OpenApiAuthController and OpenApiPermissionController, and verify the SK signature in OpenApiController.call(). The invariant that fix would rely on is: "every sensitive OpenAPI endpoint carries a Shiro authorization annotation, so the AuthorizationAttributeSourceAdvisor enforces role checks before the method runs."

What that hypothetical fix would NOT cover (the gap this variant exposes):

  • OpenApiLogController (/openapi/record/*) — not mentioned in the CVE description at all, and not covered by the parent RCA's recommended annotations. It would remain fully unprotected, leaving audit-log CRUD exploitable by any authenticated low-priv user.
  • The read endpoints GET /openapi/list and GET /openapi/queryById on OpenApiController also carry no @RequiresRoles, allowing any authenticated user to enumerate/read all OpenAPI definitions (including originUrl, headersJson, paramsJson).

Variant / Alternate Trigger

Variant A — OpenApiLogController (/openapi/record/*) [NEW surface]
  • File: jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiLogController.java
  • Entry points: GET /openapi/record/list, POST /openapi/record/add, PUT /openapi/record/edit, DELETE /openapi/record/delete, DELETE /openapi/record/deleteBatch, GET /openapi/record/queryById
  • Code path: Every method delegates directly to the MyBatis-Plus ServiceImpl (service.save/service.updateById/service.removeById/service.page) with no @RequiresRoles/@RequiresPermissions annotation. Shiro's JwtFilter authenticates the caller (validates the JWT) but performs no authorization; the AuthorizationAttributeSourceAdvisor is never triggered because there is no annotation to advise on.
Variant B — OpenApiPermissionController (/openapi/permission/*) [CVE-claimed, unreproduced]
  • File: jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiPermissionController.java
  • Entry points: POST /openapi/permission/add, GET /openapi/permission/getOpenApi
  • Code path: POST /add calls service.add(...), which deletes all existing OpenApiPermission rows for the supplied apiAuthId then inserts new rows for each comma-separated apiId — with no authorization check and no validation that the caller owns apiAuthId. A low-priv user can therefore grant OpenAPI permissions against the admin's auth record. GET /getOpenApi returns the full list of OpenAPI definitions (with per-API ifCheckBox flags) for any apiAuthId.
Negative control (confirms the gap is annotation-specific)
  • POST /openapi/add (OpenApiController) carries @RequiresRoles({"admin"}) and correctly rejects the low-priv user with Subject does not have role [admin]. This proves the Shiro authorization layer is functional and that the vulnerability is specifically the absence of annotations on the controllers above.

  • Package/component affected:

    • org.jeecg.modules.openapi.controller.OpenApiLogController (Variant A — primary)
    • org.jeecg.modules.openapi.controller.OpenApiPermissionController (Variant B)
    • Same jeecg-system-biz module as the parent CVE.
  • Affected versions (as tested): JeecgBoot v3.9.2 (commit 7df07a823fd558be857d0208ccae96342539fbc1). The latest master HEAD (32a8dfe) is byte-identical for these controllers, so the latest available code is also affected.

  • Risk level: HIGH. Variant A adds a distinct impact class to the CVE: audit-log tampering/destruction and audit-log disclosure by any authenticated low-priv user, in addition to the credential CRUD the parent CVE describes. Variant B enables a low-priv user to grant themselves (or alter) OpenAPI permissions on any auth record and enumerate the OpenAPI surface.

  • Consequences:

    • A low-priv user can read every OpenAPI call log (GET /openapi/record/list, total=27 observed) regardless of ownership.
    • A low-priv user can create forged audit-log rows attributed to the admin's auth record (POST /openapi/record/add with callAuthId = admin's auth id; persisted and verified in the DB).
    • A low-priv user can delete audit-log rows (DELETE /openapi/record/delete; verified the row count drops from 1 → 0 in the DB), enabling cover-up of API call activity.
    • A low-priv user can grant OpenAPI permissions on the admin's auth record (POST /openapi/permission/add; 2 rows persisted against the admin auth id) and enumerate the OpenAPI definitions mapped to it (GET /openapi/permission/getOpenApi).

Impact Parity

  • Disclosed/claimed maximum impact (parent CVE): Privilege escalation — low-priv users perform full CRUD on OpenAPI AK/SK credentials and can invoke the OpenAPI surface as the credential owner.
  • Reproduced impact from this variant run:
    • Variant A: low-priv user performed full CRUD on the OpenAPI audit-log table (read 27 logs, create a forged log attributed to admin's auth id, delete a log) — a new impact class (audit-log disclosure/forgery/tampering) not claimed by the CVE.
    • Variant B: low-priv user granted OpenAPI permissions on the admin's auth record and enumerated the OpenAPI definitions — confirming the CVE's claimed OpenApiPermissionController surface that the repro stage had only described, not exercised.
    • Negative control: POST /openapi/add (admin-protected) correctly rejected the low-priv user.
  • Parity: full — the broken-access-control primitive (authenticated low-priv user performing admin-only operations on a protected resource) is fully demonstrated on two additional controllers, and a new impact class (audit-log tampering) is shown.
  • Not demonstrated: Chaining stolen credentials through /openapi/call/** to impersonate the admin end-to-end (same limitation as the parent repro; the /openapi/call/** SK-signature issue is a separate adjacent root cause and was not the focus of this variant).

Root Cause

The Shiro security configuration (jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java) maps the catch-all /** to the jwt filter:

filterChainDefinitionMap.put("/openapi/call/**", "anon"); // no auth at all
...
filterMap.put("jwt", new JwtFilter(cloudServer==null));
filterChainDefinitionMap.put("/**", "jwt");               // auth-only, no authz

JwtFilter performs authentication only (it validates that a JWT exists and is signed). Authorization is enforced exclusively via method-level annotations (@RequiresRoles / @RequiresPermissions), which Shiro's AuthorizationAttributeSourceAdvisor intercepts.

OpenApiLogController and OpenApiPermissionController declare no such annotations on any method, so the authorization layer is never invoked for them. Any caller who passes JWT authentication — including a user whose only role is test — can execute every method. The sibling OpenApiController annotates its mutating endpoints with @RequiresRoles({"admin"}), which is why its POST /add correctly rejects the low-priv user (the negative control).

The same underlying bug (missing Shiro authorization annotations + auth-only /** filter) is therefore reachable from multiple distinct controllers/endpoints — the parent CVE exercised OpenApiAuthController; this variant exercises OpenApiLogController and OpenApiPermissionController.

Fix commit: None exists. The parent RCA's recommended fix (add @RequiresRoles({"admin"}) to mutating endpoints in OpenApiAuthController and OpenApiPermissionController) would still leave OpenApiLogController unprotected.

Reproduction Steps

  1. Reference: bundle/vuln_variant/reproduction_steps.sh
  2. What the script does:
    • Resolves the JeecgBoot v3.9.2 repo from the prepared project cache and records the exact tested commit (git rev-parse HEAD = 7df07a8…).
    • Ensures MariaDB + Redis are running and the jeecg-boot database is seeded (reusing the seeded DB if present; importing the seed SQL otherwise).
    • Ensures the jeecg-system-start-3.9.2.jar is built (reuses the pre-built jar).
    • Starts the JeecgBoot Spring Boot application (or reuses an already-running instance) with login captcha disabled, then polls /sys/randomImage/** until it is healthy.
    • Logs in as the low-priv user ceshi (role test, password 123456) and as admin.
    • Variant A: ceshi calls GET /openapi/record/list (reads all 27 audit logs), POST /openapi/record/add (creates a forged log attributed to the admin auth id), DELETE /openapi/record/delete?id=<created> (deletes it; verified in DB), and GET /openapi/record/queryById.
    • Variant B: ceshi calls POST /openapi/permission/add (grants permissions on the admin's auth id; 2 rows persisted) and GET /openapi/permission/getOpenApi.
    • Negative control: ceshi calls POST /openapi/add and confirms it is rejected with Subject does not have role [admin].
    • Cleans up the rows it inserted so the run is idempotent.
    • Exit 0 = variant reproduced on the latest available code; Exit 1 = not reproduced.
  3. Expected evidence of reproduction:
    • http/record_list_ceshi.jsonsuccess:true, result.total=27.
    • http/record_add_ceshi.jsonsuccess:true ("添加成功!"); persisted id verified in DB.
    • http/record_delete_ceshi.jsonsuccess:true ("删除成功!"); DB row count 1→0.
    • http/record_queryById_ceshi.jsonsuccess:true.
    • http/permission_add_ceshi.jsonsuccess:true ("保存成功"); 2 permission rows persisted against admin auth id.
    • http/permission_getOpenApi_ceshi.jsonsuccess:true.
    • http/openapi_add_negcontrol.jsonsuccess:false, Subject does not have role [admin].
    • logs/vuln_variant/reproduction_steps.log — full execution log.
    • logs/vuln_variant/jeecgboot_app.log — application log.
    • logs/vuln_variant/latest_version.txt / fixed_version.txt — source-identity resolution.

Evidence

  • Log file locations:

    • bundle/logs/vuln_variant/reproduction_steps.log
    • bundle/logs/vuln_variant/jeecgboot_app.log
    • bundle/logs/vuln_variant/latest_version.txt
    • bundle/logs/vuln_variant/fixed_version.txt
    • bundle/vuln_variant/http/*.json — HTTP request/response captures for each proof.
  • Key excerpts proving the alternate trigger:

    VARIANT A — low-priv user reads all audit logs (http/record_list_ceshi.json):

    { "success": true, "code": 200,
      "result": { "records": [ { "id": "1922175238557913090",
        "apiId": "1922132683346649090", "callAuthId": "1922164194775056386", ... } ],
        "total": 27 } }
    

    VARIANT A — low-priv user creates then deletes an audit log:

    // POST /openapi/record/add  (callAuthId = admin's auth id)
    { "success": true, "message": "添加成功!", "code": 200 }
    // DELETE /openapi/record/delete?id=2073800946032979970
    { "success": true, "message": "删除成功!", "code": 200 }
    // DB verification: row count for that id went 1 -> 0
    

    VARIANT B — low-priv user grants permissions on admin's auth record:

    // POST /openapi/permission/add (apiAuthId = admin's auth id)
    { "success": true, "message": "保存成功", "code": 200 }
    // DB: 2 rows persisted in open_api_permission with api_auth_id=1922164194775056386
    

    NEGATIVE CONTROL — admin-protected endpoint correctly rejects low-priv user:

    // POST /openapi/add  (has @RequiresRoles({"admin"}))
    { "success": false, "message": "Subject does not have role [admin]", "code": 500 }
    
  • Environment details: MariaDB (jeecg-boot DB seeded), Redis, JeecgBoot v3.9.2 Spring Boot 3 jar, JDK 17, low-priv user ceshi/role test, admin user admin/role admin.

Recommendations / Next Steps

The (future) fix must cover all OpenAPI controllers that perform privileged operations, not just OpenApiAuthController:

  1. Add @RequiresRoles({"admin"}) (or appropriate @RequiresPermissions) to every mutating endpoint in OpenApiLogController (/openapi/record/add, /edit, /delete, /deleteBatch) — this controller is entirely absent from the CVE description and the parent RCA's recommended fix.
  2. Add @RequiresRoles({"admin"}) to OpenApiPermissionController.add() and getOpenApi() (or restrict getOpenApi to the caller's own apiAuthId).
  3. Reconsider the read endpoints GET /openapi/list and GET /openapi/queryById on OpenApiController, which also lack annotations and let any authenticated user enumerate all OpenAPI definitions (including originUrl/headers).
  4. Defense-in-depth: do not rely solely on method annotations. Add an authorization rule in the Shiro filter chain for /openapi/** (e.g. require an admin role at the filter level for mutating paths), and enforce ownership checks (a caller may only mutate logs/permissions/credentials whose systemUserId/apiAuthId belongs to them).
  5. Fix the adjacent /openapi/call/** issue: verify the SK signature, not just the appkey header, so stolen AK/SK pairs cannot be used without the secret.

Additional Notes

  • Idempotency: The script was run three times consecutively; all three completed with exit 0 and the variant reproduced each time. The script cleans up the rows it inserts (variant-test-api log row, variant-perm-api-* permission rows) so repeated runs do not accumulate state. It reuses an already-running JeecgBoot instance and an already-seeded database when present.
  • Source identity: Tested commit 7df07a823fd558be857d0208ccae96342539fbc1 (tag v3.9.2). master HEAD 32a8dfe94f014f7a9d472bce83e08bb8cf9c10c3 was byte-diffed and is identical for all four OpenAPI controllers — i.e. the variant is confirmed on the latest available code, not just an old tag.
  • Why this is a variant, not the same trigger relabeled: Variant A targets a different controller (OpenApiLogController), a different URL prefix (/openapi/record/*), a different protected resource (the audit-log table), and produces a different impact class (audit-log tampering/destruction) — none of which appear in the CVE description. Variant B targets OpenApiPermissionController, which the CVE description mentions but the parent repro never exercised at runtime.
  • No SECURITY.md / threat model exists in the JeecgBoot repository that scopes these endpoints as out-of-scope or "by design"; these are network-reachable authenticated admin-management endpoints, so the broken-access-control finding is within scope.

CVE-2026-58377 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:000:50
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-58377 · REPRO-20
0:02
0:04
web search
0:05
web search
0:07
0:17
web search
0:19
web search
0:21
0:24
0:26
web search
0:28
0:30
web search
0:31
web search
0:43
0:43
extract_facts
no facts extracted
0:44
0:44
0:44
supportrepro
0:45
0:45
0:45
0:45
0:46
0:46
0:46
0:49
0:49
0:49
0:50
0:50
08 · How to Fix

How to Fix CVE-2026-58377

Coming soon

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

10 · FAQ

FAQ: CVE-2026-58377

How does the JeecgBoot privilege escalation work?

Because the Shiro filter chain maps /** to the authentication-only jwt filter, any authenticated user - including a low-privileged user with only a "test" role - can list, create, edit, and delete all OpenAPI credential pairs, and the list endpoint returns the plaintext secret keys (SK), enabling credential theft and unauthorized invocation of the OpenAPI surface owned by administrator accounts.

Which JeecgBoot versions are affected by CVE-2026-58377?

JeecgBoot through 3.9.2 (all versions up to and including 3.9.2) are affected, verified at commit 7df07a823fd558be857d0208ccae96342539fbc1 / tag v3.9.2.

How severe is CVE-2026-58377?

It is rated high severity (CVSS 4.0: 8.6 per the root-cause analysis) - any authenticated low-privilege user can read all OpenAPI AK/SK credential pairs in plaintext, including administrators', and reuse them.

How can I reproduce CVE-2026-58377?

Download the verified script from this page and run it in an isolated environment against JeecgBoot <=3.9.2; create a low-privileged user and send requests to the unguarded OpenApiAuthController/OpenApiPermissionController endpoints to list and manage admin-owned OpenAPI credentials.
11 · References

References for CVE-2026-58377

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