Java_Sec_Code
SQL注入审计
项目地址
https://github.com/JoyChou93/java-sec-code
0x1基于JDBC的sql注入审计
Statement Sql注入
我们直接定位到这里的漏洞代码
我们会发现这里是用Statement
进行执行sql语句,它会直接把参数拼接进入sql语句,不经过任何处理,所以会导致sql注入,momo插件也直接就给我们爆红了,显示这里存在sql注入漏洞
我们debug一下,在web端传入admin
,也发现了sql语句没有经过任何处理,就直接执行了
于是我们注入的payload就是简单的闭合掉单引号就好了
?username=admin'or'1'='1
Statement Sql注入fix
知道了漏洞产生的原因,我们再看看修复漏洞的代码
可以发现,这里的语句将Statement
改成了PreparedStatement
,它会对sql语句进行预编译,并且将参数传入的位置采用问号占位符,预编译之后再设置
当然,这么说是不容易理解的,我们跟进去调试看一下参数究竟是怎么被设置进去查询的,我们将断点断在st.setString
方法上,跟进调试
一直跟进到if判断里面,有个needsQuoted
变量,开始被设置为false,然后将传入的参数(admin'or'1'='1
)长度取出来,设置一个buf变量
进入for循环之后,开始对传入的字符进行逐字判断,由于我们传入的参数里有'
,被switch捕捉到了
然后就在后面添加\
和\'
,再break出去。总的来说,这个switch就是对我们传入的字符串进行处理,但是它不是在原先的基础上替换,而是新建一个变量去逐个添加,并且会在首位添加一个'
,这样最后进行查询的参数就完全不是我们传入的那个了,而是一个新值buf。'admin\' or \'1\'=\'1'
这样就能防止sql注入,但是只有我们使用?
作为占位符才起作用,如果是直接拼接,仍然会产生sql注入,如下
JDBC产生注入情况
直接拼接
PreparedStatement在使用?
作为占位符才能防止sql注入,直接拼接仍会产生sql注入
比如
String sql = "select * from users where username = '" + username + "'";
PreparedStatement st = con.prepareStatement(sql);
logger.info(st.toString());
ResultSet rs = st.executeQuery();
使用in语句
一般出现在删除语句中,有时候无法确定删除对象的数量,直接拼接,如:
String sql = "delete from users where id in("+id+");"
应该将对象遍历传入,使用?
占位符,预编译
使用like语句,order by等关键字
模糊查询场景
String sql = "select * from user where username like '%" + name + "%'" //存在注入
0x2基于Mybatis的sql注入审计
Mybatis采用parameterType向sql语句传参,有#{Parameter}和${Parameter}两种方式
其中${Parameter}传参,是直接将参数拼接到sql语句中,对输入没有过滤的情况下,会导致sql注入
而 #{Parameter}传参,是采用了预编译的构造sql语句,防止sql注入
mybatis产生注入情况
like注入
在这种情况下使用 #{}
程序会报错,容易把 #号
改成了 $
,这就照样导致了拼接的问题了。
<select id="findByUserNameVuln02" parameterType="String" resultMap="User">
select * from users where username like '%${_parameter}%'
</select>
<!-- fix code-->
<select id="findByUserNamesec" parameterType="String" resultMap="User">
select * from users where username like concat('%',#{_parameter}, '%')
</select>
in语句产生注入
<select id="findByUserNameVuln04" parameterType="String" resultMap="User">
select * from users where id in (${id})
</select>
<!-- fix code-->
<select id="findByIdSec04" parameterType="String" resultMap="User">
SELECT
* from users WHERE id IN <foreach collection="id" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
order by语句
和JDBC同理,使用#{}方式传参会导致order by语句失效,所以使用order by语句的时候还是需要做好过滤
参考
https://xz.aliyun.com/t/10686#toc-5