BossCMSv1.3

03-BossCMS v1.3

温州互引信息技术有限公司(以下简称互引信息、BossCMS、老板建站)是一家专注于软件开发、网站建设、模板开发、平面设计及网站安全运维的企业,主要为中小型企业及政企单位还有需要使用CMS建站系统的个人或开发团队提供新一代合规建站产品和服务。多年来我们始终围绕着互联网相关软件进行有关业务的开展和产品研究,凭借持续的研发投入及不断的创新,已发展为建站系统领域中比较完整的的CMS软件供应商。至今,BossCMS产品经过了上百多次的调整,产品操作简单、功能完善、安全可靠、可视化编辑、多语言、文件存储(支持本地或外部存储)、自定义页面URL、网站优化(SEO)设置、广告违禁词及城市分站站群等功能,BossCMS是一款基于自主研发PHP框架+MySQL架构的网站管理系统,适应各类网站的内容管理,同时备受终端用户和开发者好评的CMS建站系统。

理清目录结构

通过官方介绍,我们可以明白,BossCMS是自主研发的PHP框架。

看README文件,发现其采用了MVC设计模式,所以重点在于找C

image

image

这说明前后台存在分离不同的功能点。所以需要区分前后台的路由访问方式。

路由分析

通过访问入口文件,发现其定义了四个常量,这四个常量都是前台的路由,同时直接包含了enter.php文件

image

进入enter.php文件,发现其包含了一个into.class.php的文件,同时进行了into::load()操作,很明显,这里是实例化类。

image

跟进load()函数。调用了load_class()函数,并且传入了参数进行了defined()判断,回到index.php中,发现BOSSCMS_TYPE定义的是web,这说明前台index.php中定义了一些路由的常量。

image

image

跟进到load_class()方法,并且有注释说明是加载class类文件,同时注意其第一次if判断,它判断了$type这个变量,给出admin还是web,这说明$type变量是路由中用于区分后台还是前台web。

image

再分析下面包含的文件部分,可以发现被包含的文件,由传入的三个变量组成,$type$mold$part从而包含了一个类文件

从一开始的分析,我们知道$type是控制前后台的,而$part前面有/且后面直接拼接.class.php说明,这是决定文件名的,则由此判断$mold是控制目录的

image

包含完文件后,我们看一下下面的函数,判断传入的$func变量,既然文件都包含了,这个多半是调用文件中的方法。利用call_user_func(),通过对象的方式回调类中的方法。

形如:

1
2
3
//通过对象的方式回调
$myobject = new myclass();
call_user_func(array($myobject, 'say_hello'));

image

路由分析结果

$type控制前后台,$mold 控制目录 $part控制类文件 $func控制类中调用方法

直接访问index.php,会自定义三种变量使其访问前台内容

如果访问/admin/index.php,也会自定义三种变量分别是:mold、part、func,只不过这三个变量可控,而$type变量直接由IS_INSIDE常量控制实现访问后台。

漏洞挖掘

文件上传

漏洞分析

先找文件上传点,找到对应路由文件(这里有js前端写的验证,但是如果禁用js,整个上传点都废掉了)。就不从这里分析了。

image

下面还有一个编辑器,里面存在图片上传(也是前端验证)和附件上传(后端验证),那么就追踪附件上传的运行逻辑

image

追踪到controller.php文件,检测action函数,发现该文件中定义了mold,type,part,func等常量,这说明存在固定路由,访问对应文件extend/ueditor/php/ueditor.class.php中的init()方法

image

在init()方法中,找到了附件上传的函数,追踪对应代码

image

追踪到对应函数,查看其执行逻辑,主要先找用于上传的函数,判断的地方可以先不看。

很明显,这里调用了一个上传的静态方法,先追踪其使用方式到system/basic/class/upload.class.php文件中的file()函数

image

找到上传相关函数,这里调用的是move_upload_file()函数。根据该函数的使用方式,可以直接看上面的判断,进行溯源,看能否构成完整的逻辑调用链,从调用链中寻找绕过漏洞。

image

注:在后台安全设置中找到一个允许上传后缀的设置,通过抓包发现,该功能点可以控制对应config文件中的*upload_extension*字段

image

继续看upload.class.php文件中的函数,通过pathinfo()函数获取后缀名,$extension变量是直接读取config文件中的,而这个允许上传后缀的$extension可以通过后台功能点设置,所以是可控的。

那么下面第一个判断in_array($ext,$extension)是可以控制的判断,再看第二个判断,这里需要!$type为true或者($type && !$in)为true,追踪上传时传入的函数参数传递,有传入type,所以$type不为null。这就要看第一个if()判断函数,其中涉及的$type变量的更改。

image

image

这里只需要$type为False或者$type && !$in为True即可。

分析第一个if()判断函数,传入的是code|zip|word|excel|powerpoint|audio|text|pdf,利用foreach()函数遍历extension.json文件中的函数,通过对比可以知道其中if(in_array($k,$tarr))是True,无法控制的,那么只要if(in_array($ext,$v))能够true就能实现$type=null,而通过观察extension.json文件,发现只需要传入允许的后缀即可,所以可以传入php文件。至此upload.class.php文件分析结束

image

回到ueditor.class.php文件

image

这里的判断都是可控的,而且没有进行过滤,所以不用太在意。

所以整体调用链:调用uploadfile()函数,判断是否有参数upfile传入,且将其值赋值给$file=> 判断文件大小 =>调用文件上传函数upload::file,需要进入第一个if()判断,有文件上传且没有错误(即上传成功) =>判断后缀,这里需要借助安全设置中的配置添加php文件上传。从而绕过判断。

image

利用方式

现在安全设置中添加允许上传的php后缀。image

寻找调用uploadfile()函数的功能点,根据调用的文件名ueditor.class.php可以猜测这里使用的ueditor编辑器。所以从编辑器中找文件上传,一般是附件上传image

imageimageimage

任意文件删除

漏洞分析

同样在ueditor.class.php文件中,里面存在一个delete()函数

image

通过POST方法传入path参数,获取config文件中的store_type参数来判断是否进行下一步,这边动用了两个方法进行删除,一个是dir::deleteoss::delete。全局搜索**store_type**,发现这个是用于控制存储方式的参数,默认为0,即直接调用dir::delete方法

image

在dir.class.php文件中,可以看到delete()方法中调用了unlink()函数,可以看到这里除了调用replace()方法外,就是直接判断是否为文件,从而执行unlink()函数

image

现在只需要追溯replace()方法的执行结果。

看注释是替换路径分割字符,通过分析其正则替换,只是对路径的一些字符进行替换,并没有设置什么过滤,所以可以初步判断存在任意文件删除,现在只需要根据路由构造payload,看能否成功。

image

漏洞利用

  1. 判断是否利用post方法,提交path参数
  2. 提交的参数是否以upload开头
  3. 默认采用dir::delete()方法,先进行replace()函数,过滤掉多余的,例如//,然后就直接进行unlink()函数

构造Payload如下,因为这里是ueditor文件中的,所以delete功能点一定也在这个编辑器中,所以构造payload时的路由需要根据这个编辑器中的调用方式来构造。

image

image

目录遍历

漏洞分析

浏览lists()函数,发现这里对于传入的$path参数,并没有进行过滤,而是直接传入到read()函数中进行读取。通过分析,发现这里调用了两种read(),其中oss::read()是远程读取,dir::read()是本地读取,所以优先看本地读取函数

image

image

看似是判断数组是否存在,实际上看$val = $val[$v] 有点变量覆盖的意思在其中,实现函数调用。

看dir::read()方法,里面调用了opendir()readdir()函数,可以看到,对于传入的$path也是直接调用,并没有进行过滤等操作,最后通过return,返回读取到的文件名

image

image

回到lists()方法,发现这个函数并没有直接输出,而是通过return返回了遍历得到的数组名,所以无法直接通过路由构造出目录遍历漏洞。需要去找调用点。–>查找方法。

image

找到一处调用lists()方法的地方,发现最后还是return了一组json::encode 的数据,同时这里也没有对传入的数据进行过滤。但是这里也没有对数据进行输出,而且这里listfile()方法也没有传入参数,这表明存在一个地方调用该方法。=>全文查找。

image

最后在init()函数中找到了调用方式,通过get得到的action,利用switch跳转到对应的分支,用$result接收函数,最后利用echo输出callback()函数回调的结果

image

image

此时只需要分析config['fileManagerActionName']是什么即可,追溯到config.json文件中,里面制定了action接收的是listfile时,调用listfile()方法。并且规定了默认列出的文件目录为upload/

image

小结

整理一下整体实现逻辑链

  1. 传入action参数,用于指定调用方法(这里调用的是listfile()
  2. 调用listfile()方法时,直接调用lists()方法没有经过任何判断,默认传入upload/目录
  3. 在lists()方法中,有拼接的操作,通过审核arrExist()方法,发现其进行了简单的变量覆盖,通过传入folder,实现参数传递。
  4. 因为store_type值默认为0,所以会自动执行本地读取文件操作,即dir::read(),同时传入默认的upload/以及folder参数拼接而成的路径。
  5. dir::read()方法中,通过调用opendir()readdir()两个函数,读取目录,同时返回读取到的内容。
  6. 最后再在init()方法中的echo输出。

漏洞利用

分析过程中,我们知道了这是附件上传可能会调用到的函数。只需要控制action和添加folder这个拼接参数,就可以实现绕过。找到功能点在在线附件中。

image

通过前面的分析,我们知道只需要传入action和folder两个参数即可,删除其他参数吗,避免造成干扰

image

image

image

任意文件下载

漏洞分析

在后台找功能点,但凡含有下载的字样的,都可以试一试

image

找到对应的函数位置

image

发现函数执行部分没有任何过滤,直接通过拼接的方式获取文件路径,然后调用readfile()函数,读取文件内容到输出缓存(这里没有接收,相当于直接下载),那么只需要直接修改接收的id值,就可以实现任意文件下载了

漏洞利用

image

image

权限校验逻辑缺陷

漏洞分析

ueditor.class.php文件中,定义的ueditor类继承了admin,这表示需要通过admin身份验证才能够执行的操作。

image

其次继承的类,通过into::basic_class()方法加载对应的类,追溯basic_class()方法。

因为没有传入$func,所以会设置$func='init',同时最后会进行load_class()方法,追溯到load_class()方法,这里前面分析过,就是判断是否为后端来加载类中方法。因为传入的$name='admin'所以会加载admin.class.php文件中的$func,也就是init()方法

image

追溯到admin.class.php文件中的init()方法。

这里可以看到通过session来判断是否登录,如果判断为空,且没有设置IS_LOGIN就会实现跳转。但是并没有其他函数执行,例如die()函数,结束执行init()中的代码。这就意味着如果构造后台操作路由,还是会导致执行,只不过会触发跳转到登录界面。而不是die()直接结束加载类

image

漏洞实现

image

image

image

image

image