JeecgBoot Tenant Privilege Escalation: GET /sys/permission/queryDepartPermission Department Authorization Menu Query Without Tenant Validation
Contributors: huangweigang
1. Impact Scope
2. Vulnerable Endpoint
- GET
/sys/permission/queryDepartPermission?departId=... (Query Department Authorization Menu API)
3. Code Analysis
- Controller:
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysPermissionController.java
- Route & method:
@RequestMapping(value = "/queryDepartPermission", method = RequestMethod.GET)
public Result<List<String>> queryDepartPermission(@RequestParam(name = "departId", required = true) String departId)
- Key code (lines 1000–1011):
Result<List<String>> result = new Result<>();
try {
List<SysDepartPermission> list = sysDepartPermissionService.list(new QueryWrapper<SysDepartPermission>().lambda().eq(SysDepartPermission::getDepartId, departId));
result.setResult(list.stream().map(sysDepartPermission -> String.valueOf(sysDepartPermission.getPermissionId())).collect(Collectors.toList()));
result.setSuccess(true);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return result;
- Problem points:
- The endpoint directly uses the
departId parameter to query the department's authorization menu list
- Does not verify whether the department belongs to the current tenant
- Returns a list of permission IDs, exposing the department's complete menu authorization configuration
- Attackers can obtain menu permission configurations of all tenants by enumerating department IDs
4. Reproduction
-- Prerequisites
- Attacker has a valid login session
- Attacker knows or can enumerate the target tenant's department ID
- System has department-level menu permissions configured
-- Steps (Cross-tenant Menu Permission Configuration Disclosure)
- Using attacker account (Tenant A):
curl -X GET -H "Authorization: Bearer <attacker_token>" "http://<host>/jeecgboot/sys/permission/queryDepartPermission?departId=<victim_dept_id>"
- Observation: API returns 200 OK, returns the list of authorized permission IDs for the target department, for example:
["1", "2", "3", "100", "200", ...]
- Verification:
- Use Tenant B's administrator account to query the same department's permission configuration
- Query sys_permission table based on permission IDs to confirm corresponding menu functions
- Confirm the returned permission configuration belongs to another tenant
5. Impact
- Menu permission configuration disclosure
- Attackers can obtain any tenant's department menu authorization status
- Understand which functional modules and menus different departments can access
- Functional module architecture exposure
- Permission ID lists can be reverse-engineered to infer the system's functional module divisions
- Combined with permission table, complete system functional architecture can be mapped
- Permission system analysis
- By querying multiple departments' permission configurations, complete organizational permission allocation diagrams can be drawn
- Provides intelligence for subsequent attacks
- Understand which departments have access to sensitive functions
- Identify high-privilege departments, select targets for privilege escalation attacks
6. Remediation
- Department tenant ownership validation
- Verify whether the department belongs to the current tenant before querying:
SysDepart depart = sysDepartService.getById(departId);
LoginUser currentUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
if(depart == null || !depart.getTenantId().equals(currentUser.getTenantId())) {
return Result.error("Unauthorized to access this department permission configuration");
}
- Force tenant filtering
- When querying department permissions, ensure the department belongs to the current tenant through JOIN or subquery
- Permission level control
- Restrict only department administrators or system administrators to query department permission configurations
- Ordinary users can only query permissions of their own departments
- Data desensitization
- Apply appropriate desensitization to sensitive permission configuration information based on the querier's role
- Audit logging
- Record all department permission query operations
- Pay special attention to cross-tenant access attempts and high-frequency query behaviors
JeecgBoot Tenant Privilege Escalation: GET /sys/permission/queryDepartPermission Department Authorization Menu Query Without Tenant Validation
Contributors: huangweigang
1. Impact Scope
2. Vulnerable Endpoint
/sys/permission/queryDepartPermission?departId=...(Query Department Authorization Menu API)3. Code Analysis
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysPermissionController.java@RequestMapping(value = "/queryDepartPermission", method = RequestMethod.GET)public Result<List<String>> queryDepartPermission(@RequestParam(name = "departId", required = true) String departId)Result<List<String>> result = new Result<>();try {List<SysDepartPermission> list = sysDepartPermissionService.list(new QueryWrapper<SysDepartPermission>().lambda().eq(SysDepartPermission::getDepartId, departId));result.setResult(list.stream().map(sysDepartPermission -> String.valueOf(sysDepartPermission.getPermissionId())).collect(Collectors.toList()));result.setSuccess(true);} catch (Exception e) {log.error(e.getMessage(), e);}return result;departIdparameter to query the department's authorization menu list4. Reproduction
-- Prerequisites
-- Steps (Cross-tenant Menu Permission Configuration Disclosure)
curl -X GET -H "Authorization: Bearer <attacker_token>" "http://<host>/jeecgboot/sys/permission/queryDepartPermission?departId=<victim_dept_id>"["1", "2", "3", "100", "200", ...]5. Impact
6. Remediation
SysDepart depart = sysDepartService.getById(departId);LoginUser currentUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();if(depart == null || !depart.getTenantId().equals(currentUser.getTenantId())) {return Result.error("Unauthorized to access this department permission configuration");}