Java代码审计系列课程
前置知识:了解Mybatis框架映射关系
mapper定位java代码
id定位方法
List<User> findByUserNameVuln02(String username);
定位到UserMapper.xml中的id
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like '%${_parameter}%'
</select>
Mybatis中两种数据库拼接方法
${}是直接拼接
#{}预处理后拼接
mybatis有两种写法,一种是使用@Param 注解:
public interface CategoryMapper {
("select * from category_ where name= '${name}' ")
public CategoryM getByName( ("name") String name);
}
1、${}直接拼接,类似jdbc中的直接拼接审计方法
http://localhost:8080/sqli/mybatis/vuln01?username=joychou
/ * http://localhost:8080/sqli/mybatis/vuln01?username=joychou' or '1'='1
*/
("/mybatis/vuln01")
public List<User> mybatisVuln01( ("username") String username) {
return userMapper.findByUserNameVuln01(username);
}
这里使用的是mybatis来进行SQL查询,获取参数username后使用userMapper.findByUserNameVuln01(username)来进行查询。
来看一下findByUserNameVuln01。MyBatis支持两种参数符号,一种是#,另一种是$。这里的参数获取使用的是${username},而不是#{username},而${username}是直接将参数拼接到了SQL查询语句中,就会造成SQL注入。
("select * from users where username = '${username}'")
List<User> findByUserNameVuln01( ("username") String username);
注入语句:
http://localhost:8080/sqli/mybatis/vuln01?username=joychou' or '1'='1
${}修复方法
采用#{},也就是预处理的形式
http://localhost:8080/sqli/mybatis/sec01?username=joychou
"select * from users where username = #{username}")
User findByUserName( ("username") String username);
(
2、Mybatis中like审计方法
访问路径:
http://localhost:8080/sqli/mybatis/vuln02?username=admin
这里使用的是findByUserNameVuln02(String username);来进行查询,来看一下UserMapper.xml,也就是他的XML映射文件。
List<User> findByUserNameVuln02(String username);
UserMapper.xml:
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like '%${_parameter}%'
</select>
_parameter是Mybatis的内置参数,代表整个参数
这里传入未过滤的username后,插入到SQL语句中,就成了:select * from users where username like '%username%',${}直接拼接字符串,而且这里在like的后面不能使用#{}预编译,不然就会产生报错。
注入语句:
http://localhost:8080/sqli/mybatis/vuln02?username=admin' or '1'='1' %23
修复建议:
可以使用like concat('%',#{username}, '%')就可以避免注入了。
http://localhost:8080/sqli/mybatis/vsec02?username=joyc
java代码:
List<User> findByUserNameVsec02(String username);
Mybatis配置:
<select id="findByUserNameVsec02" parameterType="String" resultMap="User">
select * from users where username like concat('%',#{_parameter}, '%')
</select>
验证:
http://localhost:8080/sqli/mybatis/vsec02?username=joychou' or '1'='1' %23
同理不同数据库的修复代码快:
Mysql数据库:
SELECT * FROM user WHERE name like CONCAT('%',#{name},'%')
Oracle数据库:
SELECT * FROM user WHERE name like CONCAT('%',#{name},'%')
或
SELECT * FROM user WHERE name like '%'||#{name}||'%'
Sqlserver数据库:
SELECT * FROM user WHERE name like '%'+#{name}+'%'
DB2数据库:
SELECT * FROM user WHERE name like '%'+#{name}+'%'
或
SELECT * FROM user WHERE name like '%'||#{name}||'%'
3、Mybatis中order by审计方法
访问链接:
http://localhost:8080/sqli/mybatis/orderby/vuln03?sort=1
这里使用的是findByUserNameVuln03(@Param("order") String order)来进行查询,同样也是去看UserMapper.xml。
<select id="findByUserNameVuln03" parameterType="String" resultMap="User">
select * from users
<if test="order != null">
order by ${order} asc
</if>
</select>
可以看到我们输入的参数在order by之后,也是${order}直接拼接起来了,与like相同,在这也是无法使用预编译,只能使用${}。由于没有过滤就直接拼接,很显然存在注入。
注入语句:
http://localhost:8080/sqli/mybatis/orderby/vuln03?sort=1 and (updatexml(1,concat(0x7e,(select version()),0x7e),1))
修复代码:
1、当order排序能不让用户输入就不让用户输入,后台直接写死,不传递参数到后端:
http://localhost:8080/sqli/mybatis/sec03
Java代码块:
User OrderByUsername();
Mysql配置:
<select id="OrderByUsername" resultMap="User">
select * from users order by id asc limit 1
</select>
2、当order必须要从外界获取参数到后端代码进行拼接时候,那就先对参数进行过滤,过滤后再使用:
http://localhost:8080/sqli/mybatis/orderby/sec04?sort=1
Java代码块:
("/mybatis/orderby/sec04")
public List<User> mybatisOrderBySec04( ("sort") String sort) {
String filter_order = SecurityUtil.sqlFilter(sort);
return userMapper.findByUserNameVuln03(filter_order);
}
//sqlFilter
private static final Pattern FILTER_PATTERN = Pattern.compile("^[a-zA-Z0-9_/\\.-]+$");
public static String sqlFilter(String sql) {
if (!FILTER_PATTERN.matcher(sql).matches()) { //严格限制用户输入只能包含a-zA-Z0-9_-.
return null;
}
return sql;
}
Mysql配置:
<select id="findByUserNameVuln03" parameterType="String" resultMap="User">
select * from users
<if test="order != null">
order by ${order} asc
</if>
</select>