JeecgBoot Tenant Privilege Escalation: GET /sys/sysDepartRole/getDeptRoleByUserId User Department Role Query Without Tenant Validation
Contributors: huangweigang
1. Impact Scope
2. Vulnerable Endpoint
- GET
/sys/sysDepartRole/getDeptRoleByUserId?userId=...&departId=... (Get User's Assigned Department Roles 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:
@RequestMapping(value = "/getDeptRoleByUserId", method = RequestMethod.GET)
public Result<List<SysDepartRoleUser>> getDeptRoleByUserId(@RequestParam(value = "userId") String userId,@RequestParam(value = "departId") String departId)
- Key code (lines 217–231):
Result<List<SysDepartRoleUser>> result = new Result<>();
//Query department roles
List<SysDepartRole> roleList = sysDepartRoleService.list(new QueryWrapper<SysDepartRole>().eq("depart_id",departId));
List<String> roleIds = roleList.stream().map(SysDepartRole::getId).collect(Collectors.toList());
//Query authorized roles based on role id and user id
List<SysDepartRoleUser> roleUserList = null;
if(roleIds!=null && roleIds.size()>0){
roleUserList = departRoleUserService.list(new QueryWrapper<SysDepartRoleUser>().eq("user_id",userId).in("drole_id",roleIds));
}
result.setSuccess(true);
result.setResult(roleUserList);
return result;
- Problem points:
- The endpoint directly uses
departId to query department roles without verifying the department's tenant ownership
- Directly uses
userId to query user role authorizations without verifying whether the user belongs to the current tenant
- Attackers can query the user department role authorization status of any tenant
- May leak cross-tenant user permission configuration information
4. Reproduction
-- Prerequisites
- Attacker has a valid login session
- Attacker knows or can enumerate the target tenant's user ID and department ID
- System has multi-tenancy mode enabled
-- Steps (Cross-tenant User Permission Information Disclosure)
- Using attacker account (Tenant A):
curl -X GET -H "Authorization: Bearer <attacker_token>" "http://<host>/jeecgboot/sys/sysDepartRole/getDeptRoleByUserId?userId=<victim_user_id>&departId=<victim_dept_id>"
- Observation: API returns 200 OK, returns the target user's role authorization list in the specified department
- Verification:
- Use Tenant B's administrator account to query the same user's roles and confirm data consistency
- Database query sys_depart_role_user table to confirm the returned authorization relationship belongs to another tenant
5. Impact
- Cross-tenant user permission information disclosure
- Attackers can obtain other tenant users' department role authorization information
- Understand the target user's permission level and functional scope within the organization
- Permission system analysis
- By querying different users' role authorizations in bulk, complete organizational permission architecture diagrams can be drawn
- Personnel information disclosure
- Confirm specific users' department affiliations and role responsibilities within the organization
- Provides intelligence for privilege escalation attacks
- Identify high-privilege users, providing targets for subsequent targeted attacks
6. Remediation
- Department tenant ownership validation
- Verify whether the department corresponding to
departId belongs to the current tenant:
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");
}
- User tenant ownership validation
- Verify whether the user corresponding to
userId belongs to the current tenant:
SysUser user = sysUserService.getById(userId);
if(user == null || !sysUserTenantService.userBelongsToTenant(userId, currentUser.getTenantId())) {
return Result.error("Unauthorized to query this user information");
}
- Force tenant filtering
- Add tenant ID filtering conditions in all database queries
- Least privilege principle
- Restrict ordinary users to only query their own role authorizations, only administrators can query other users
- Audit logging
- Record all user role query operations, especially cross-tenant access attempts
JeecgBoot Tenant Privilege Escalation: GET /sys/sysDepartRole/getDeptRoleByUserId User Department Role Query Without Tenant Validation
Contributors: huangweigang
1. Impact Scope
2. Vulnerable Endpoint
/sys/sysDepartRole/getDeptRoleByUserId?userId=...&departId=...(Get User's Assigned Department Roles API)3. Code Analysis
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysDepartRoleController.java@RequestMapping(value = "/getDeptRoleByUserId", method = RequestMethod.GET)public Result<List<SysDepartRoleUser>> getDeptRoleByUserId(@RequestParam(value = "userId") String userId,@RequestParam(value = "departId") String departId)Result<List<SysDepartRoleUser>> result = new Result<>();//Query department rolesList<SysDepartRole> roleList = sysDepartRoleService.list(new QueryWrapper<SysDepartRole>().eq("depart_id",departId));List<String> roleIds = roleList.stream().map(SysDepartRole::getId).collect(Collectors.toList());//Query authorized roles based on role id and user idList<SysDepartRoleUser> roleUserList = null;if(roleIds!=null && roleIds.size()>0){roleUserList = departRoleUserService.list(new QueryWrapper<SysDepartRoleUser>().eq("user_id",userId).in("drole_id",roleIds));}result.setSuccess(true);result.setResult(roleUserList);return result;departIdto query department roles without verifying the department's tenant ownershipuserIdto query user role authorizations without verifying whether the user belongs to the current tenant4. Reproduction
-- Prerequisites
-- Steps (Cross-tenant User Permission Information Disclosure)
curl -X GET -H "Authorization: Bearer <attacker_token>" "http://<host>/jeecgboot/sys/sysDepartRole/getDeptRoleByUserId?userId=<victim_user_id>&departId=<victim_dept_id>"5. Impact
6. Remediation
departIdbelongs to the current tenant: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");}userIdbelongs to the current tenant:SysUser user = sysUserService.getById(userId);if(user == null || !sysUserTenantService.userBelongsToTenant(userId, currentUser.getTenantId())) {return Result.error("Unauthorized to query this user information");}