当前位置: 首页 > news >正文

RuoYi issue5: Department Hierarchy Rebinding

Vulnerability call chain

1.1 Summary

RuoYi has a missing authorization vulnerability: Department Hierarchy Rebinding. 越权创建或移动部门,改变 DataScope 使用的部门层级授权路径。

  • Attack precondition: 拥有 system:dept:addsystem:dept:edit
  • Affected authorization property: ``sys_dept.parent_id, sys_dept.ancestors
  • Security impact: 越权创建或移动部门,改变 DataScope 使用的部门层级授权路径。

1.2 Exploit path

POST /system/dept/add/system/dept/edit,提交不可见父部门 parentId

1.3 Key code evidence

  1. ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java

Evidence location: https://github.com/yangzongzhuan/RuoYi/blob/master/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java#L77

   74      @PostMapping("/add")75      @ResponseBody76      public AjaxResult addSave(@Validated SysDept dept)77      {78          if (!deptService.checkDeptNameUnique(dept))79          {80              return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");81          }82          dept.setCreateBy(getLoginName());83          return toAjax(deptService.insertDept(dept));84      }85  86      /**87       * 修改部门
  1. ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java

Evidence location: https://github.com/yangzongzhuan/RuoYi/blob/master/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java#L193

  190       * @return 结果191       */192      @Override193      public int insertDept(SysDept dept)194      {195          SysDept info = deptMapper.selectDeptById(dept.getParentId());196          // 如果父节点不为"正常"状态,则不允许新增子节点197          if (!UserConstants.DEPT_NORMAL.equals(info.getStatus()))198          {199              throw new ServiceException("部门停用,不允许新增");200          }201          dept.setAncestors(info.getAncestors() + "," + dept.getParentId());202          return deptMapper.insertDept(dept);203      }204  205      /**206       * 修改保存部门信息207       * 208       * @param dept 部门信息209       * @return 结果210       */211      @Override212      @Transactional213      public int updateDept(SysDept dept)214      {215          SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId());216          SysDept oldDept = selectDeptById(dept.getDeptId());217          if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept))218          {219              String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId();220              String oldAncestors = oldDept.getAncestors();221              dept.setAncestors(newAncestors);222              updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors);223          }224          int result = deptMapper.updateDept(dept);225          if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors())226                  && !StringUtils.equals("0", dept.getAncestors()))227          {
  1. ruoyi-system/target/classes/mapper/system/SysDeptMapper.xml

Evidence location: https://github.com/yangzongzhuan/RuoYi/blob/master/ruoyi-system/target/classes/mapper/system/SysDeptMapper.xml#L89

   86  		select count(*) from sys_dept where status = 0 and del_flag = '0' and find_in_set(#{deptId}, ancestors)87  	</select>88  	89  	<insert id="insertDept" parameterType="SysDept">90   		insert into sys_dept(91   			<if test="deptId != null and deptId != 0">dept_id,</if>92   			<if test="parentId != null and parentId != 0">parent_id,</if>93   			<if test="deptName != null and deptName != ''">dept_name,</if>94   			<if test="ancestors != null and ancestors != ''">ancestors,</if>95   			<if test="orderNum != null">order_num,</if>96   			<if test="leader != null and leader != ''">leader,</if>97   			<if test="phone != null and phone != ''">phone,</if>98   			<if test="email != null and email != ''">email,</if>99   			<if test="status != null">status,</if>100   			<if test="createBy != null and createBy != ''">create_by,</if>101   			create_time102   		)values(103   			<if test="deptId != null and deptId != 0">#{deptId},</if>104   			<if test="parentId != null and parentId != 0">#{parentId},</if>105   			<if test="deptName != null and deptName != ''">#{deptName},</if>106   			<if test="ancestors != null and ancestors != ''">#{ancestors},</if>107   			<if test="orderNum != null">#{orderNum},</if>108   			<if test="leader != null and leader != ''">#{leader},</if>109   			<if test="phone != null and phone != ''">#{phone},</if>110   			<if test="email != null and email != ''">#{email},</if>111   			<if test="status != null">#{status},</if>112   			<if test="createBy != null and createBy != ''">#{createBy},</if>113   			sysdate()114   		)115  	</insert>116  	117  	<update id="updateDept" parameterType="SysDept">118   		update sys_dept119   		<set>120   			<if test="parentId != null and parentId != 0">parent_id = #{parentId},</if>121   			<if test="deptName != null and deptName != ''">dept_name = #{deptName},</if>122   			<if test="ancestors != null and ancestors != ''">ancestors = #{ancestors},</if>123   			<if test="orderNum != null">order_num = #{orderNum},</if>124   			<if test="leader != null">leader = #{leader},</if>125   			<if test="phone != null">phone = #{phone},</if>126   			<if test="email != null">email = #{email},</if>127   			<if test="status != null and status != ''">status = #{status},</if>128   			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>129   			update_time = sysdate()130   		</set>131   		where dept_id = #{deptId}132  	</update>133  	134  	<update id="updateDeptChildren" parameterType="java.util.List">
  1. ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java

Evidence location: https://github.com/yangzongzhuan/RuoYi/blob/master/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java#L100

   97                  scopeCustomIds.add(Convert.toStr(role.getRoleId()));98              }99          });100  101          for (SysRole role : user.getRoles())102          {103              String dataScope = role.getDataScope();104              if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE))105              {106                  continue;107              }108              if (StringUtils.isNotEmpty(permission) && !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))109              {110                  continue;111              }112              if (DATA_SCOPE_ALL.equals(dataScope))113              {114                  sqlString = new StringBuilder();115                  conditions.add(dataScope);
  1. deptService.c

Evidence location: https://github.com/yangzongzhuan/RuoYi/blob/master/deptService.c

3. Root Cause Analysis

Root Cause 1: Missing server-side authorization on the vulnerable operation.

The endpoint accepts user-controlled authorization-sensitive identifiers or fields, but the write/read path does not prove that the current caller may operate on the target object.

Root Cause 2: Missing object-scope or grant-bound validation.

The implementation relies on endpoint access, UI filtering, or object existence checks instead of enforcing target ownership, tenant boundary, role ceiling, or grantable-resource constraints at the service layer.

add/edit 保存前对 parentId 调用 deptService.checkDeptDataScope(parentId),并阻止跨授权范围移动部门。

5. Verification after fix

  • Unauthorized callers receive HTTP 403 or equivalent rejection.
  • Out-of-scope target identifiers are rejected before database writes or sensitive reads.
  • Role, permission, tenant, organization, ownership, or grant-bound ceilings are enforced server-side.
  • Direct HTTP requests are rejected even when front-end controls are hidden.
http://www.jsqmd.com/news/1035809/

相关文章:

  • 2026年6月知识产权商标注册公司推荐:TOP5实力榜专业评测市场份额案例 - 品牌推荐
  • hu
  • 2026黔南焊缝探伤检测权威机构排行 TOP 本地高频选择,无损检测 + UT+RT+PT 检测 附电话地址 - 中安检测集团
  • 2026随州焊缝探伤检测权威机构排行 TOP 本地高频选择,无损检测 + UT+RT+PT 检测 附电话地址 - 中安检测集团
  • 基于MOBI文件解析的Kindle封面元数据修复技术
  • Hermes Agent Skill Runtime 架构拆解:让 AI Agent 不再从零开始
  • 2026上海焊缝探伤检测权威机构排行 TOP 本地高频选择,无损检测 + UT+RT+PT 检测 附电话地址 - 中安检测集团
  • 解锁米哈游游戏字体:HoYo-Glyphs开源字体库创意应用全攻略
  • 拉萨市空调维修/中央空调维修|本地避坑指南,满分五星平台|欧米到家首选 - 欧米到家
  • 终极免费浏览器AI图像标注工具:make-sense.ai完全指南
  • 2026内蒙古焊缝探伤检测权威机构排行 TOP 本地高频选择,无损检测 + UT+RT+PT 检测 附电话地址 - 中安检测集团
  • 2026柳州焊缝探伤检测权威机构排行 TOP 本地高频选择,无损检测 + UT+RT+PT 检测 附电话地址 - 中安检测集团
  • Zotero Actions Tags:终极智能文献自动化管理指南
  • 2026上饶焊缝探伤检测权威机构排行 TOP 本地高频选择,无损检测 + UT+RT+PT 检测 附电话地址 - 中安检测集团
  • 授权委托书公证办理周期大概多久?授权委托书公证不用本人到场能操作吗?
  • 沈阳市于洪区桶装水哪家好 万家水业 17050428888 - GrowthUME
  • 2026年,企业级大文件传输如何实现极速飞跃?
  • 2026年国内AI搜索优化源头厂家深度评测:十大服务商选型避坑指南 - 品牌报告
  • 2026连云港焊缝探伤检测权威机构排行 TOP 本地高频选择,无损检测 + UT+RT+PT 检测 附电话地址 - 中安检测集团
  • TPU2协处理器:嵌入式实时系统定时任务的硬件化解决方案
  • 2026六安焊缝探伤检测权威机构排行 TOP 本地高频选择,无损检测 + UT+RT+PT 检测 附电话地址 - 中安检测集团
  • 专家观察:深圳全屋定制正在进入“证据型消费”阶段 - 爱格研究所
  • 数字员工是什么?熊猫智汇在AI销售工具中的作用与价值是什么?
  • 2026韶关焊缝探伤检测权威机构排行 TOP 本地高频选择,无损检测 + UT+RT+PT 检测 附电话地址 - 中安检测集团
  • 2026年 武汉GEO优化服务商推荐榜:精准推广/全域运营/流量优化与排名提升的深度解析 - 品牌发掘
  • Gemini原生多模态架构:从语言模型到世界解析器的范式跃迁
  • 功率MOSFET选型与驱动设计实战:以MCP87050为例解析低RDS(ON)与快速开关的平衡
  • 2026 徐州附近废品回收商家实测汇总|同城上门废金属、旧家电、工地废料正规回收指南 - 星际AI
  • 怒江市空调维修/中央空调维修|本地避坑指南,满分五星平台|欧米到家首选 - 欧米到家
  • 2026年国内激光砍树设备品牌实力排行盘点 - 起跑123