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语句没有经过任何处理,就直接执行了

image-20230203154135309

于是我们注入的payload就是简单的闭合掉单引号就好了

?username=admin'or'1'='1

image-20230203154535036

Statement Sql注入fix

知道了漏洞产生的原因,我们再看看修复漏洞的代码

image-20230203163104296

可以发现,这里的语句将Statement改成了PreparedStatement,它会对sql语句进行预编译,并且将参数传入的位置采用问号占位符,预编译之后再设置

当然,这么说是不容易理解的,我们跟进去调试看一下参数究竟是怎么被设置进去查询的,我们将断点断在st.setString方法上,跟进调试

一直跟进到if判断里面,有个needsQuoted变量,开始被设置为false,然后将传入的参数(admin'or'1'='1)长度取出来,设置一个buf变量

image-20230203173408227

进入for循环之后,开始对传入的字符进行逐字判断,由于我们传入的参数里有',被switch捕捉到了

image-20230203174124323

然后就在后面添加\\',再break出去。总的来说,这个switch就是对我们传入的字符串进行处理,但是它不是在原先的基础上替换,而是新建一个变量去逐个添加,并且会在首位添加一个',这样最后进行查询的参数就完全不是我们传入的那个了,而是一个新值buf。'admin\' or \'1\'=\'1'

image-20230203171522214

这样就能防止sql注入,但是只有我们使用?作为占位符才起作用,如果是直接拼接,仍然会产生sql注入,如下

image-20230203175757614

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 + "%'" //存在注入

image-20230203185127235

image-20230216114749854

0x2基于Mybatis的sql注入审计

Mybatis采用parameterType向sql语句传参,有#{Parameter}和${Parameter}两种方式

其中${Parameter}传参,是直接将参数拼接到sql语句中,对输入没有过滤的情况下,会导致sql注入

image-20230203201841835

而 #{Parameter}传参,是采用了预编译的构造sql语句,防止sql注入

image-20230203202504444

image-20230203202441419

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

https://tttang.com/archive/1726/

https://drun1baby.github.io/2022/09/14/Java-OWASP-%E4%B8%AD%E7%9A%84-SQL-%E6%B3%A8%E5%85%A5%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/#toc-heading-16