Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

admin 2024年2月18日23:32:31评论25 views字数 3287阅读10分57秒阅读模式
前言

若依系统版本小于等于4.6.1的时候存在SQL注入漏洞,这个SQL注入漏洞挺有意思的。

初学者在学习SQL注入的时候往往加个单引号闭合一下,如果没有报错就测试下一个点了。然而这种方式最终将会错过许多好玩的漏洞。

一位前辈曾和我说过,SQL注入漏洞最终落脚点可不是单引号这么简单,而是“闭合”。“闭合”两个字后面一直印在我脑海里,跟着我去看待每个SQL注入漏洞。而本文要讲的IN注入,更是将闭合思想发挥的淋漓尽致。

除此之外,我们初学者入门SQL注入的时候,往往面对的都是SELECT开头的查询语句的注入,比较少面对譬如UPDATE、INSERT、DELETE这种动作词的注入。本文研究的漏洞恰好是UPDATE这种动作词的注入,非常值得一看。

若依系统使用的是SpringBoot架构,初学者往往会在这种架构的各种文件中迷失。本文会简单阐述如何从SQL沿着架构找到参数的输入点。当然你最好对MVC的这种架构设计思想有一点了解。

知识点

Mybatis框架SQL注入的查找方式

UPDATE注入

IF注入

SpringBoot架构查找参数输入点

01

找到SQL注入点

在Mybatis中,参数输入有两种方式,分别为#{param}和${param}。这里面#{param}会使用占位符号(会进行预编译),而${param}会和原生的SQL语句进行拼接,因此使用${param}得到参数的方法存在SQL注入。

在若依项目里面使用ctrl+shift+f进行查找,注意查找的范围选择XML文件,因为mybatis的SQL映射都写在XML文件下。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

可以看到这边存在很多个${param}的SQL语句,我们第一个漏洞点就存在ancestors这个参数里面。点过去跟进查询详细语句。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)
#这段XML转化为SQL语句应该是这样的:update sys_dept update_time = sysdate() where dept_id in (${ancestors})update sys_dept update_time = sysdate(),status = #{status} where dept_id in (${ancestors})(如果status不为空的话)update sys_dept update_time = sysdate(),update_by = #{updateBy}where dept_id in (${ancestors})(如果updateBy不为空的话)

但是不管怎么样,ancestors都是可控的,因此是存在SQL注入的。

02

SpringBoot调用SQL语句的方式

在springboot架构中,有以下几种方式执行SQL操作:

1、业务层调用DAO层

2、controller调用service层间接调用DAO层

3、controller直接调用DAO层

DAO层

DAO层用来存放mapper接口,mapper作用为访问数据库,向数据库发送sql语句,完成数据的增删改查功能,通常将其实现为接口,内部声明的方法将会于mapper层中的对应数据库函数关联 。简而言之就是就是执行SQL语句相关的。

比如下列文件就是DAO层文件:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)
controller层

控制请求和响应,负责前后端交互。controller层主要调用Service层里面的接口控制具体的业务流程,不会在其中编写大量逻辑代码,同时也会接受并处理一些HTTP参数,例如session。控制层一大特征就是前面会加@controller。控制层我们可以看到接收了什么参数,什么参数是可控的。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)
service层

业务逻辑层,完成功能的设计。和DAO层一样都是先设计接口,再创建要实现的类,然后在配置文件中进行配置其实现的关联。service的impl是把mapper和service进行整合的文件 封装Service层的业务逻辑有利于业务逻辑的独立性和重复利用性。在service的实现类上要加注解@Service,否则会出现无法扫描识别 。

跟进SERVICE的方法便能够到达SERVICE层,比如下面这个方法:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

具体实现业务在接口下:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

这个就是具体实现业务

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

在这个类的最上面写着@Service,说明这个就是SERVICE层。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

所以我们可以看见,若依采用的是第2种方式调用SQL语句的,即controller调用Service层间接调用DAO层。

03

寻找参数的输入点

SQL注入要求输入参数可控,因此下面得寻找参数的输入点。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

以这个SQL语句的id作为检索条件搜索(注意要改成搜索.java文件结尾的方式),如下图:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)
DAO层

通过上诉搜索.java结尾的文件直接到达DAO层了。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)
Service层

点击“1 usage”跟进第二个调用这个SQL的语句:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

这个时候我们已经来到了Service层,可以看见输入的参数是dept,那么dept是什么,它可控吗?继续向上跟进。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)
CONTROLLER层

这个时候来到了controller层:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

URL路径

我们具体看一下这个controller层的这个方法前面的标签:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

第一个是用于日志文件的,第二个用于权限控制的,第三个则是我们比较关心的,就是url入口。不过这里的/edit是相对url,要看完整url,还有往上面翻一下:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

因此完整的url路径应该将两个拼接起来:/system/dept/edit

输入参数

要看输入什么参数得看这个。输入的数据类型为SysDept,前面的@Validated是检测数据是否有效的。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

我们跟进SysDept,可以发现,有ancestors这个参数,而且还是String的,且没有任何过滤。那么这个SQL注入就能够利用了。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

04

漏洞利用
构造payload

回归最开始的SQL语句

update sys_dept update_time = sysdate() where dept_id in (${ancestors})

这里涉及到两个知识点:

1.SQL动词注入:SQL语句为UPDATE、INSERT、DELETE等动词的时候,一般使用报错注入。

2.IN注入:可控参数在in后面可以使用括号进行闭合,而不是单引号。

结合上面两点思路,我们可以构造payload如下:

update sys_dept update_time = sysdate() where dept_id in (0)or(extractvalue(1,concat((select+user()))))

ancestors=0)or(extractvalue(1,concat((select+user())))
验证payload

那么接下来就验证这个payload的。我们一般我直接去漏洞的url,而是去对应的功能点。从上面函数可以看出这个功能点是修改部门的,因此我们定位到如下功能点:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)
Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

使用Burp Suite抓取报文:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

没有ancestors这个参数,我们手动输入。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

发现利用失败,最后排查发现是Content-Length的原因,这个字段得和上传参数一样。

直接使用python:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)
Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

运行后依旧不行。

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)
改正

结合其它博主的payload,我发现parentId不能使用系统已经有的,而要使用没有的。

从比较抽象的层面来看,该SQL语句的目的是为了更新父部门的状态。而parentId说明现在更新的部门的父部门id,ancestors这个参数有祖先的意思。因此两个参数可能会互相影响。说明这两个参数是否冲突调试起来比较麻烦,就不做进一步调试了。

但是我们可以得到一点经验:降低输入参数的关联性,避免冲突。

最后将parentId改为不存在的1

Content-Length改为 261,然后发送报文:

Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

成功!

05

总结

①使用${查找Mybatis的SQL注入点。

②了解SpringBoot的架构,找到可控的SQL注入输入点。

③UPDATE使用报错注入。

④IN注入使用括号闭合。

⑤尽量降低参数之间的关联性,避免影响结果。

⑥记得修改Content-Length,确保和参数长度一致。

// 作者:IntSheep

原文始发于微信公众号(赛博游民营):Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月18日23:32:31
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Java代码审计:SQL注入中的Update、In注入(若依CMS|Mybatis|Spring|新手向)http://cn-sec.com/archives/2499238.html

发表评论

匿名网友 填写信息