-
-
[原创]SpringBoot-MyBatis-luckwheel-master开源代码审计
-
发表于: 2022-7-14 16:27 1090
-
比较简单的代码!
Github地址: GitHub - s6056826/luckwheel: 国产开源幸运大转盘管理系统,积分,倍率,奖品兑换
CSDN介绍地址: 开源大转盘抽奖源码,带后台管理,可管理奖品和奖品中奖概率,java语言实现飞吧菜鸟了的博客-CSDN博客转盘抽奖源码
下载之后用Idea打开,首先导入数据库
这个数据库里面只有表,不会自动创建库。所以需要手动创建一个数据库
看一眼spring的配置库名叫youyoudb
创建跟这个一样的名称,然后导入表即可
这里的codepay_order是我自己创建的,后边会说到为什么
然后idea maven会自动添加下载依赖,等待全部下好之后,启动即可
直接访问会提示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来访问
然后就一直会提示参数错误,看一眼JS
这里会判断uid的值,上面的变量可以看到uid来自另外一个函数,这里函数这个pnum是GET参数,跟过去看一眼
这里正则匹配我们不需要管,只需要返回值不等于NULL即可,所以我们直接在前端构造一个pnum参数试试
现在不报错了,这里为什么会不报错主要是因为在数据库中有id为1的用户,可以看一眼数据库
这里的值是我自己加的,所以就不会报错了
这里已经任意用户登录了吧
然后在common.js
中可以看到,其实之前可能有登录功能
但是下载下来的源码并没有,开始以为是作者删了,我又去看了一遍commit,发现根本就没有上传登录的前端源码
看一眼后台的样子
目前所有的搭建都已经完成了
我这里是直接搜关键字找执行SQL语句的地方
一眼望去就可以看到SQL注入一个
test接口,参数uid
直接构造参数去请求接口
这里直接查询会显示没有这个表,所以按照这里SQL语句的参数去构造表字段即可
随后再请求接口
这里只会返回FALSE或者TURE
但是这里的是会回显报错的,所以构造报错语句的参数
看一下这里报错的语句,首先需要闭合前面的单引号,然后构造and后面跟报错的函数最后注释即可,payload如下
返回了当前用户,这里注释后面需要加上单引号,因为如果不加上,后面那个单引号无法闭合,可以看下图
这里可以看到注释后面的单引号跟 up_time字段的单引号合在一起了,导致了报错,所以需要加上单引号就可以成功
那么想一下,有没有方法调了canDog方法,鼠标放在canDog方法上按下Command(Windows下是alt或者ctrl)可以看到有哪些方法调用了该方法lottery
接口,那么肯定也是有sql注入的,因为这里将UID传给了下面
那么这里用同样的payload也可以注入
严格来说这里算是一处SQL注入,因为在业务上只需要修canDog这一个接口就可以了
没什么好说的
直接取这里的返回数据展示在前端了,并且在list接口也没过滤
看到请求中有JSON,马上想到会不会有FastJson,在pom.xml
中搜了一下,笑嘻了,1.2.41,全局搜一下开没开autype
但是有个通杀payload可以试试 <1.2.47的双键绕过
这里插件也扫到了
这里的payload用了Unicode编码,解码之后如下
看上去和常规的payload差不多,这里只是变成了两个键值对,打ldap的话,那就尝试复现一下
成功RCE
推荐文章: [FastJson<=1.2.47RCE细枝末节详细分析 - 安全客,安全资讯平台](https://www.anquanke.com/post/id/224820
跟过去可以看到这里的接口是/luck/lup/update
在前端页面中可以看到
构造参数
这里报错了,看一下具体的接口实现代码
首选从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)
所以这里需要将pname的值更改一下
可以打上一个断点,随后再次请求这个接口,然后还是会报错String类型的错误,这时候DEBUG看一眼,会发现漏了一个点
这里的UID需要是String类型,我们传入的是int,所以修改为String再次请求
随后发现这里没有这个表,自己去新增
添加了6个字段才成功,还是一样白盒模式下可以打印SQL语句
修改完之后重启
可以看到加上单引号之后报错
构造参数,成功注入
这里注入有一个前提,来看一代码,这里下面的UPDATE也是直接+号拼接了语句,但是上面有一个if判断,判断返回的map是不是空或者大小是不是0,那么Debug调一下会发现
首先在codepay_order这个表中pay_id要等于1,并且pay_tar大于或者等于0,并且Money需要大于或等于200,并且floor计算出来的值需要小于Pay_tag,那么这里看一下floor的值是多少
这里的条件肯定就已经不成立了,因为这里的pay_tag是1,那么这里想让条件成立的话,就把数据库中的pay_tag改为0即可
重新请求之后可以看到,已经走到了update的流程
从这里的报错可以看出来,好像没有这个字段,去数据库里面添加
再次请求接口会返回TRUE
上述报错知道了SQL语句
因为这里没办法返回具体的值,只会显示FALSE和TRUE
在终端的print可以看到,没有办法,所以这里盲注,白盒下其实可以改一下代码,输出一下SQL语句
加两行代码,然后重启,然后试了半天才发现,这里没办法到UPDATE那里
因为如果报错注入的话,上面的jdbcTemplate.queryForList
就已经报错回显了,但是如果不报错的话,payload又没办法到UPDATE语句,所以这里:)
var uid
=
getQueryString(
"pnum"
);
if
(uid
=
=
""||uid
=
=
null||uid
=
=
=
undefined){
alert(
"非法参数错误,请重新打开页面!"
);
window.location.
reload
();
}
var uid
=
getQueryString(
"pnum"
);
if
(uid
=
=
""||uid
=
=
null||uid
=
=
=
undefined){
alert(
"非法参数错误,请重新打开页面!"
);
window.location.
reload
();
}
function getQueryString(name) {
var reg
=
new RegExp(
"(^|&)"
+
name
+
"=([^&]*)(&|$)"
,
"i"
);
var r
=
window.location.search.substr(
1
).match(reg);
if
(r !
=
null)
return
unescape(r[
2
]);
return
null;
}
function getQueryString(name) {
var reg
=
new RegExp(
"(^|&)"
+
name
+
"=([^&]*)(&|$)"
,
"i"
);
var r
=
window.location.search.substr(
1
).match(reg);
if
(r !
=
null)
return
unescape(r[
2
]);
return
null;
}
select
*
from
codepay_order where pay_id
=
'1'
' and pay_tag>=0 and money>=200 and pay_tag <FLOOR(money/200) and up_time >='
2019
-
1
-
15
0
:
0
:
0
select
*
from
codepay_order where pay_id
=
'1'
' and pay_tag>=0 and money>=200 and pay_tag <FLOOR(money/200) and up_time >='
2019
-
1
-
15
0
:
0
:
0
GET
/
luck
/
wheel
/
test?uid
=
1
'and+extractvalue(1,concat(0x5c,user()))--'
HTTP
/
1.1
GET
/
luck
/
wheel
/
test?uid
=
1
'and+extractvalue(1,concat(0x5c,user()))--'
HTTP
/
1.1
select
*
from
codepay_order where pay_id
=
'1'
and
extractvalue(
1
,concat(
0x5c
,user()))
-
-
' and pay_tag>=0 and money>=200 and pay_tag <FLOOR(money/200) and up_time >='
2019
-
1
-
15
0
:
0
:
0
'
select
*
from
codepay_order where pay_id
=
'1'
and
extractvalue(
1
,concat(
0x5c
,user()))
-
-
' and pay_tag>=0 and money>=200 and pay_tag <FLOOR(money/200) and up_time >='
2019
-
1
-
15
0
:
0
:
0
'
@RequestMapping
(
"lottery"
)
public JSONObject wheelLottery(String uid){
boolean flag
=
canDog(uid);
if
(!flag){
return
fail(
"没有可抽奖次数"
);
}
LuckUser luckUser
=
new LuckUser();
luckUser.setUid(uid);
LuckUser luckUser1
=
luckUserService.queryOne(luckUser);
if
(luckUser1
=
=
null){
luckUserService.add(luckUser);
}
@RequestMapping
(
"lottery"
)
public JSONObject wheelLottery(String uid){
boolean flag
=
canDog(uid);
if
(!flag){
return
fail(
"没有可抽奖次数"
);
}
LuckUser luckUser
=
new LuckUser();
luckUser.setUid(uid);
LuckUser luckUser1
=
luckUserService.queryOne(luckUser);
if
(luckUser1
=
=
null){
luckUserService.add(luckUser);
}
{
"id"
:
1
,
"exchange"
:
1
,
"pname"
:
1
,
"uid"
:
1
}
{
"id"
:
1
,
"exchange"
:
1
,
"pname"
:
1
,
"uid"
:
1
}
@Override
protected void afterUpdate(
Map
map
) {
if
(
map
!
=
null){
Integer exchange
=
(Integer)
map
.get(
"exchange"
);
if
(exchange!
=
null){
String pname
=
(String)
map
.get(
"pname"
);
if
(pname.contains(
"元"
)){
String money
=
pname.substring(
0
, pname.indexOf(
"元"
));
int
anInt
=
Integer.parseInt(money);
String uid
=
(String)
map
.get(
"uid"
);
jdbcTemplate.update(
"update login_user set user_money=user_money+"
+
anInt
+
" where app_login_id='"
+
uid
+
"'"
);
}
}
}
super
.afterUpdate(
map
);
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课