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.
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).
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 — serious impact or readily exploitable. Prioritize remediation.
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 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 Proof of Reproduction for CVE-2026-58377
- 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
authenticated low-privileged user JWT token (ceshi/test role, password 123456)
- POST /jeecg-boot/sys/login (ceshi)
- GET/POST/DELETE /jeecg-boot/openapi/auth/* with X-Access-Token header
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
Root Cause and Exploit Chain for CVE-2026-58377
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.javaandOpenApiPermissionController.java - Affected versions: JeecgBoot through 3.9.2 (commit
7df07a823fd558be857d0208ccae96342539fbc1, tagv3.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 toanonand only checks theappkeyheader), 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:
- Read admin's AK/SK credentials in plaintext via
GET /openapi/auth/list(ak=ak-pFjyNHWRsJEFWlu6, sk=4hV5dBrZtmGAtPdbA5yseaeKRYNpzGsS, owner=admin) - Created a new OpenAPI credential via
POST /openapi/auth/add(persisted to database) - Deleted an OpenAPI credential via
DELETE /openapi/auth/delete - Generated a new AK/SK pair via
GET /openapi/auth/genAKSK - Was correctly rejected by the protected
POST /openapi/addendpoint (has@RequiresRoles({"admin"})), confirming the authorization gap is specific toOpenApiAuthController
- Read admin's AK/SK credentials in plaintext via
- 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
- Reference:
bundle/repro/reproduction_steps.sh - 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-bootdatabase, 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/listwith the low-priv JWT to read all AK/SK credentials in plaintext - Calls
POST /openapi/auth/addto create a credential as the low-priv user (verified in database) - Calls
DELETE /openapi/auth/deleteto delete a credential as the low-priv user - Calls
GET /openapi/auth/genAKSKto 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
- Expected evidence of reproduction:
artifacts/http/openapi_auth_list_response.json— showssuccess: truewith admin's AK/SK in plaintextartifacts/http/openapi_auth_add_response.json— showssuccess: true(low-priv user created credential)artifacts/http/openapi_auth_delete_response.json— showssuccess: true(low-priv user deleted credential)artifacts/http/openapi_auth_genAKSK_response.json— showssuccess: truewith generated AK/SK pairartifacts/http/openapi_add_negcontrol_response.json— showssuccess: falsewith "Subject does not have role [admin]"artifacts/http/openapi_add_admin_response.json— showssuccess: true(admin can access protected endpoint)
Evidence
Log file locations:
bundle/logs/reproduction_steps.log— full script execution logbundle/logs/jeecgboot_app.log— JeecgBoot application startup and runtime logbundle/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 inOpenApiAuthController(/add,/edit,/delete,/deleteBatch) andOpenApiPermissionController(/add). Consider whether the/list,/queryById, and/genAKSKendpoints 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=falseSpring 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
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 (roletest) 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/*) exposesPOST /add(grant OpenAPI permissions on any auth record, including the admin's) andGET /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(commit7df07a823fd558be857d0208ccae96342539fbc1), which is the vulnerable version itself. masterHEAD (32a8dfe94f014f7a9d472bce83e08bb8cf9c10c3) was fetched and the four OpenAPI controller source files are byte-identical to thev3.9.2versions (verified bydiffagainst 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/listandGET /openapi/queryByIdonOpenApiControlleralso carry no@RequiresRoles, allowing any authenticated user to enumerate/read all OpenAPI definitions (includingoriginUrl,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/@RequiresPermissionsannotation. Shiro'sJwtFilterauthenticates the caller (validates the JWT) but performs no authorization; theAuthorizationAttributeSourceAdvisoris 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 /addcallsservice.add(...), which deletes all existingOpenApiPermissionrows for the suppliedapiAuthIdthen inserts new rows for each comma-separatedapiId— with no authorization check and no validation that the caller ownsapiAuthId. A low-priv user can therefore grant OpenAPI permissions against the admin's auth record.GET /getOpenApireturns the full list of OpenAPI definitions (with per-APIifCheckBoxflags) for anyapiAuthId.
Negative control (confirms the gap is annotation-specific)
POST /openapi/add(OpenApiController) carries@RequiresRoles({"admin"})and correctly rejects the low-priv user withSubject 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-bizmodule as the parent CVE.
Affected versions (as tested): JeecgBoot
v3.9.2(commit7df07a823fd558be857d0208ccae96342539fbc1). The latestmasterHEAD (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=27observed) regardless of ownership. - A low-priv user can create forged audit-log rows attributed to the admin's auth
record (
POST /openapi/record/addwithcallAuthId= 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).
- A low-priv user can read every OpenAPI call log (
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
OpenApiPermissionControllersurface 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
- Reference:
bundle/vuln_variant/reproduction_steps.sh - What the script does:
- Resolves the JeecgBoot
v3.9.2repo from the prepared project cache and records the exact tested commit (git rev-parse HEAD=7df07a8…). - Ensures MariaDB + Redis are running and the
jeecg-bootdatabase is seeded (reusing the seeded DB if present; importing the seed SQL otherwise). - Ensures the
jeecg-system-start-3.9.2.jaris 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(roletest, password123456) and asadmin. - Variant A:
ceshicallsGET /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), andGET /openapi/record/queryById. - Variant B:
ceshicallsPOST /openapi/permission/add(grants permissions on the admin's auth id; 2 rows persisted) andGET /openapi/permission/getOpenApi. - Negative control:
ceshicallsPOST /openapi/addand confirms it is rejected withSubject 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.
- Resolves the JeecgBoot
- Expected evidence of reproduction:
http/record_list_ceshi.json—success:true,result.total=27.http/record_add_ceshi.json—success:true("添加成功!"); persisted id verified in DB.http/record_delete_ceshi.json—success:true("删除成功!"); DB row count 1→0.http/record_queryById_ceshi.json—success:true.http/permission_add_ceshi.json—success:true("保存成功"); 2 permission rows persisted against admin auth id.http/permission_getOpenApi_ceshi.json—success:true.http/openapi_add_negcontrol.json—success: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.logbundle/logs/vuln_variant/jeecgboot_app.logbundle/logs/vuln_variant/latest_version.txtbundle/logs/vuln_variant/fixed_version.txtbundle/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 -> 0VARIANT 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=1922164194775056386NEGATIVE 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/roletest, admin useradmin/roleadmin.
Recommendations / Next Steps
The (future) fix must cover all OpenAPI controllers that perform privileged
operations, not just OpenApiAuthController:
- Add
@RequiresRoles({"admin"})(or appropriate@RequiresPermissions) to every mutating endpoint inOpenApiLogController(/openapi/record/add,/edit,/delete,/deleteBatch) — this controller is entirely absent from the CVE description and the parent RCA's recommended fix. - Add
@RequiresRoles({"admin"})toOpenApiPermissionController.add()andgetOpenApi()(or restrictgetOpenApito the caller's ownapiAuthId). - Reconsider the read endpoints
GET /openapi/listandGET /openapi/queryByIdonOpenApiController, which also lack annotations and let any authenticated user enumerate all OpenAPI definitions (includingoriginUrl/headers). - 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 whosesystemUserId/apiAuthIdbelongs to them). - Fix the adjacent
/openapi/call/**issue: verify the SK signature, not just theappkeyheader, 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(tagv3.9.2).masterHEAD32a8dfe94f014f7a9d472bce83e08bb8cf9c10c3was 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 targetsOpenApiPermissionController, 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.
Artifacts and Evidence for CVE-2026-58377
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-58377
FAQ: CVE-2026-58377
How does the JeecgBoot privilege escalation work?
Which JeecgBoot versions are affected by CVE-2026-58377?
How severe is CVE-2026-58377?
How can I reproduce CVE-2026-58377?
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.