JeecgBoot Tenant Privilege Escalation: GET /sys/sysDepartRole/datarule/{permissionId}/{departId}/{roleId} Data Rule Query Without Tenant Validation
Contributors: huangweigang
1. Impact Scope
2. Vulnerable Endpoint
- GET
/sys/sysDepartRole/datarule/{permissionId}/{departId}/{roleId} (Query Data Rule Information API)
3. Code Analysis
- Controller:
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysDepartRoleController.java
- Route & method:
@GetMapping(value = "/datarule/{permissionId}/{departId}/{roleId}")
public Result<?> loadDatarule(@PathVariable("permissionId") String permissionId,@PathVariable("departId") String departId,@PathVariable("roleId") String roleId)
- Key code (lines 236–260):
//Query authorized department rules
List<SysPermissionDataRule> list = sysDepartPermissionService.getPermRuleListByDeptIdAndPermId(departId,permissionId);
if(list==null || list.size()==0) {
return Result.error("Permission configuration information not found");
}else {
Map<String,Object> map = new HashMap(5);
map.put("datarule", list);
LambdaQueryWrapper<SysDepartRolePermission> query = new LambdaQueryWrapper<SysDepartRolePermission>()
.eq(SysDepartRolePermission::getPermissionId, permissionId)
.eq(SysDepartRolePermission::getRoleId,roleId);
SysDepartRolePermission sysRolePermission = sysDepartRolePermissionService.getOne(query);
if(sysRolePermission==null) { ... }
else {
String drChecked = sysRolePermission.getDataRuleIds();
if(oConvertUtils.isNotEmpty(drChecked)) {
map.put("drChecked", drChecked.endsWith(",")?drChecked.substring(0, drChecked.length()-1):drChecked);
}
}
return Result.ok(map);
}
- Problem points:
- The endpoint directly uses path parameters
departId, roleId, permissionId to query data permission rules
- Does not verify whether the department and role belong to the current tenant
- Attackers can obtain other tenants' data permission rule configurations
- Data permission rules are sensitive permission control information, and leakage may be used to bypass access controls
4. Reproduction
-- Prerequisites
- Attacker has a valid login session
- Attacker knows or can enumerate the target tenant's department ID, role ID, permission ID
- System has data permission rules configured
-- Steps (Cross-tenant Data Permission Rule Disclosure)
- Using attacker account (Tenant A):
curl -X GET -H "Authorization: Bearer <attacker_token>" "http://<host>/jeecgboot/sys/sysDepartRole/datarule/<permission_id>/<victim_dept_id>/<victim_role_id>"
- Observation: API returns 200 OK, returns target tenant's data permission rule configuration, including:
datarule: List of department permission data rules
drChecked: Data rule IDs selected for this role
- Verification:
- Use Tenant B's administrator account to query the same data rule configuration and confirm data consistency
- Database query confirms the returned data rules belong to another tenant
5. Impact
- Data permission rule disclosure
- Attackers can obtain other tenants' data permission control rules
- Understand the target system's data access control policies (such as filtering rules by department, region, etc.)
- Complete permission system exposure
- Combined with other vulnerabilities, attackers can completely map the target tenant's permission architecture
- Provides intelligence for permission bypass attacks
- After understanding data permission rules, targeted bypass strategies can be constructed
- Business logic disclosure
- Data permission rules often reflect the business's organizational structure and data access policies
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 data rules");
}
- Role tenant ownership validation
- Verify whether the role belongs to the current tenant's department:
SysDepartRole role = sysDepartRoleService.getById(roleId);
if(role == null || !role.getDepartId().equals(departId)) {
return Result.error("Role does not match department");
}
- Force tenant filtering
- Add tenant ID filtering conditions in all queries
- Permission level control
- Restrict only department administrators or system administrators to view data permission rules
- Audit logging
- Record all data permission rule query operations for security auditing
JeecgBoot Tenant Privilege Escalation: GET /sys/sysDepartRole/datarule/{permissionId}/{departId}/{roleId} Data Rule Query Without Tenant Validation
Contributors: huangweigang
1. Impact Scope
2. Vulnerable Endpoint
/sys/sysDepartRole/datarule/{permissionId}/{departId}/{roleId}(Query Data Rule Information API)3. Code Analysis
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysDepartRoleController.java@GetMapping(value = "/datarule/{permissionId}/{departId}/{roleId}")public Result<?> loadDatarule(@PathVariable("permissionId") String permissionId,@PathVariable("departId") String departId,@PathVariable("roleId") String roleId)//Query authorized department rulesList<SysPermissionDataRule> list = sysDepartPermissionService.getPermRuleListByDeptIdAndPermId(departId,permissionId);if(list==null || list.size()==0) {return Result.error("Permission configuration information not found");}else {Map<String,Object> map = new HashMap(5);map.put("datarule", list);LambdaQueryWrapper<SysDepartRolePermission> query = new LambdaQueryWrapper<SysDepartRolePermission>().eq(SysDepartRolePermission::getPermissionId, permissionId).eq(SysDepartRolePermission::getRoleId,roleId);SysDepartRolePermission sysRolePermission = sysDepartRolePermissionService.getOne(query);if(sysRolePermission==null) { ... }else {String drChecked = sysRolePermission.getDataRuleIds();if(oConvertUtils.isNotEmpty(drChecked)) {map.put("drChecked", drChecked.endsWith(",")?drChecked.substring(0, drChecked.length()-1):drChecked);}}return Result.ok(map);}departId,roleId,permissionIdto query data permission rules4. Reproduction
-- Prerequisites
-- Steps (Cross-tenant Data Permission Rule Disclosure)
curl -X GET -H "Authorization: Bearer <attacker_token>" "http://<host>/jeecgboot/sys/sysDepartRole/datarule/<permission_id>/<victim_dept_id>/<victim_role_id>"datarule: List of department permission data rulesdrChecked: Data rule IDs selected for this role5. 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 data rules");}SysDepartRole role = sysDepartRoleService.getById(roleId);if(role == null || !role.getDepartId().equals(departId)) {return Result.error("Role does not match department");}