Ref#
Vulnerable#
SQL_INJECTION#
注入点 1#
ms-base/net/mingsoft/base/dao/IBaseDao.xml#sqlWhere
<sql id="sqlWhere" databaseId="mysql">
<if test="sqlWhereList != null">
<foreach collection="sqlWhereList" item="item" index="index"
open="and( " separator=" " close=" )">
<if test="item.el == 'eq'">
<choose>
<when test="item.multiple != null and item.multiple == true">
FIND_IN_SET(#{item.value}, ${item.field})>0
</when>
<otherwise>
${item.field} = #{item.value}
</otherwise>
</choose>
</if>
<!-- 其他逻辑省略,大量使用了 ${item.field} -->
<if test="item.el == 'like'">
${item.field} like CONCAT(CONCAT('%',#{item.value}),'%')
</if>
</foreach>
</if>
</sql>反向寻找接口
net.mingsoft.base.entity.BaseEntity#getSqlWhereListnet.mingsoft.cms.action.ContentAction#list
@PostMapping("/list")
@ResponseBody
public ResultData list(@ModelAttribute @ApiIgnore ContentBean content, HttpServletResponse response, HttpServletRequest request, @ApiIgnore ModelMap model, BindingResult result) {
BasicUtil.startPage();
List contentList = contentBiz.query(content);
return ResultData.build().success(new EUListBean(contentList,(int) BasicUtil.endPage(contentList).getTotal()));
}接口毫无校验,用户可通过请求参数直接提交如 sqlWhere=[{"field":"1=1 or sleep(3)","el":"eq","value":"1"}] 的 JSON 数据段,直接造成 SQL 语句拼接注入。(涵盖 net.mingsoft.cms.action.web.ContentAction 等多处 list 注入)
注入点 2#
ms-base/net/mingsoft/base/dao/IBaseDao.xml#queryBySQL
<!-- mysql根据sql动态查询开始 -->
<select id="queryBySQL" resultType="Map" databaseId="mysql">
select *
from ${table}
<where>
1=1
<foreach item="item" index="key" collection="wheres" open="AND"
separator="AND" close=""> ${key} = #{item}
</foreach>
<include refid="net.mingsoft.base.dao.IBaseDao.sqlWhere"></include>
</where>
<!-- ...... -->
</select>反向寻找接口
net.mingsoft.base.biz.impl.BaseBizImpl#queryBySQLnet.mingsoft.basic.action.BaseAction#validatednet.mingsoft.mdiy.action.PageAction#verify
@Api(tags={"自定义页面接口"})
@RequestMapping("/${ms.manager.path}/mdiy/page")
public class PageAction extends BaseAction {
//... 略去其他代码
@GetMapping("verify")
@ResponseBody
public ResultData verify(String fieldName, String fieldValue, String id, String idName){
boolean verify = false;
// 漏洞参数直接传递至父类验证逻辑中
if(StringUtils.isBlank(id)){
verify = super.validated("mdiy_page",fieldName,fieldValue);
}else{
verify = super.validated("mdiy_page",fieldName,fieldValue,id,idName);
}
if(verify){
return ResultData.build().success(false);
}else {
return ResultData.build().success(true);
}
}
}// 父类 BaseAction 中封装了通用字段验证功能:
protected boolean validated(String tableName,String fieldName, String fieldValue) {
Map where = new HashMap<>(1);
// fieldName 未过滤作为主键直接赋给查询条件 wheres
where.put(fieldName, fieldValue);
List list = appBiz.queryBySQL(tableName, null, where);
if (ObjectUtil.isNotNull(list) && !list.isEmpty()) {
return true;
}
return false;
}接口对于用户传入的 fieldName 毫无校验过滤。由外界在 verify 方法传参 fieldName=xx ,接着进入底层由 <foreach> 取为其 map 的 key 然后使用 ${key} 原样拼接,导致在动态查询验证时发生 SQL 注入。