PHP代码审计之bosscms
2023-2-20 09:33:12 Author: 亿人安全(查看原文) 阅读量:18 收藏

文章首发在:先知社区

https://xz.aliyun.com/t/12106

一、环境搭建


二、代码审计
路由分析:
第一步首先去找到index.php文件,上面定义了一些常量,这里的常量决定了该index.php路由。
我们接着去看enter.php文件。

在该文件中定义了一些常量,包含了into.class.php文件并调用了 into 类下的 load() 方
法。我们继续跟进该方法

我们继续跟进 load_class() 方法。

在load_class()方法中需要传入四个参数,第一个参数 $type 是判断该功能点是前台功能点还是后台功能
点,也就是决定代码19行的$type路径是在哪个文件夹下,由于SYSTEM_PATH常量定义为system目录
$mold 参数定位功能目录, $part 决定调用目录下哪个
php文件, $func 则是定位文件下调用的方法


1.ueditor编辑器文件上传漏洞
我们在后台查看功能点的时候,发现一处可以编辑上传类型的地方,这里添加一个允许上传后缀.php,

这里可以添加成功。

进行.php文件的上传,但这里提示后缀名不允许上传,
这里我们去抓包查看。


通过路由找到对应源码

这里调用了controller.php,6-10行定义了常量也就是路由的走向,通过包含 enter.php 调用路由文件
进行路由选择,也就是我们上面分析的路由。

通过路由找到功能点文件ueditor.php下ueditor类下的init()方法,上面的路由中传入了
uploadimage ,分支的选择是通过读取config[]来进行选择的,config的内容是从哪里获取的

在类的最上面通过 __construct() 调用 load_json() 方法进行 config 内容的获取,
继续跟进load_json()方法


通过前面传入的config.json获取该json文件中的文件内容。

前面action中传入了uploadimage,所以进入了该分支,并调用了上面的uploadimage()方法
我们接着上面继续跟进分支中的uploadimage()方法。

在 uploadimage() 方法中使用$FILES接上上传文件,而这里调用的upload::files()方法才是上传的关
键,我们继续跟进files()方法。

在files()方法中的60行看到了我们刚开始的提示信息,那我们从下往上去追是什么情况导致条件不满足。

向上追溯的时候,我们发现要想上传文件这里需要满足两个条件:

  1. in_array($ext,$extension) ,

  2. (!$type || ($type&& !$in) 。
    我们首先去看第一个条件 $ext 在 $extension 数组中,我们继续向上追溯

从这里我们发现 $ext 是获取我们上传的后缀名,而这里的 $extension 是从哪里获取到的呢
全局搜索 upload_extension 可以发现该处正是我们上面通过自定义设置的.php后缀,所以
第一个条件是满足的。

而第二个条件 (!$type || ($type && !$in) 我们只需满足一个条件即可 !$type=true 或者
($type=true && !$in=true) , !$type=true 那么$type的值为NULL即可。

我们全局搜索extension中定义的类型,发现extension.json文件中定义了我们上传的类型


所以想要实现php文件的上传,我们需要传入的type类型为code才行

我们全局查看files()方法调用,发现该处功能点传入的类型中有code,我们跟进该方法进行查看。

而在uploadfile()方法中就一目了然了,该处调用files()方法并且可以上传code类型下的后缀文件,而
code下包含我们想要上传的.php后缀。所以此处功能点满足上面的两个条件可以实现任意文件上传。


漏洞复现:在编辑器上传附件的地方,找到一个使用编辑器的地方,通过附
件上传.php文件,这也就满足了第二个条件此处调用了uploadfile()方法。


2.任意文件删除漏洞

我们继续查看ueditor.class.php文件时发现该文件下还存在一个delete()方法,这个是编辑
器中删除文件的一个方法,我们去分析一下该方法。

通过全局搜索store_type发现该处功能为设置存储方式,默认为0,所以上面默认会走dir类下的delete()方法。

由于在上面只通过正则限制我们开头必须为upload这里很好绕过,我们跟进dir类下的delete()方法查看
该方法:
传入的path参数经过replace()方法后直接进行了unlink()删除,所以我们继续跟进
replace()方法查看该方法是否存在过滤

这里的replace()方法并未做过滤,而是一些路径的优化。所以这里的path就可以控制造成任意文件删除。


漏洞复现:

我们可以直接构造出该方法的路由


3.目录遍历漏洞
我们接着翻找 ueditor.class.php 文件下的方法时,找到一处 lists() 方法,在代码327行处
$folder 参数是通过GET传入且没有进行任何过滤就拼接到$path路径中。

在read()方法中我们可以看到代码129-130行通过opendir()、readdir()打开目录并读取目录中的内容,
而上面的replace()方法我们在上面任意文件删除也进行了分析,是做路径优化的。

我们在回到lists()方法中,由于该方法通过路由不能直接调用,所以我们去查看lists()的调用情况,我们发现有三处调用了lists()方法

我们找到一处 listfile() 方法进行分析,在该方法的312行处调用了lists()方法。

我们去全局搜索 fileManagerListPath ,找到该值是upload/路径,也就是说上面要读取的文件是upload目录下的文件,我们从上面的分析中可以知道lists()方法中传入的 $folder 参数没有进行过滤就拼接到 $path 中所以这里是存在目录遍历的。

我们如何进行调用 listfile() 方法实现目录遍历,我们知道 ueditor.class.php 的路由。

通过同级目录下的controller.php进行方法的调用的通过路由我们知道首先要调用这里的init()方法,在该方法中通过传入action参数实现类中方法的调用。

漏洞复现:使用这个接口进行漏洞复现
GET /system/extend/ueditor/php/controller.php?action=listfile&folder=../../../ HTTP/1.1



4.任意文件下载漏洞
在测试功能点的时候我们发现 安全设置- 数据备份- 备份列表- 下载处存在一处任意文件下载,我们抓包去分析该处下载功能点。


通过上面的路由分析我们不难找到这里的 download() 方法,这里通过66行GET传入 id 参数,在67行将
传入的id参数拼接到路径中,这里的sql为上面定义的路径,最后在代码73行处使用readfile()函数进行文件读取。


漏洞复现:
通过 ../ 实现目录跳转,读取mysql配置文件。


5.权限校验处存在逻辑缺陷
我们从第一处编辑器任意文件上传来分析,我们发现在ueditor类中继承了一个admin类,而在文件的最上面通过 basic_class() 来加载 admin.class.php 文件。


在代码85行处判断是否设置 $func 参数,如果没有则将 $func 默认设置为 init ,然后在最下面调用
load_class() 加载类文件,然后加载admin类文件下的 init() 方法。

在调用admin类下的 init() 方法中通过session类下的 get() 方法获取登录后的session,如果读取不到session的话get()方法则返回false,那么这里就会进入到16行的if条件,在17行判断是否定义IS_LOGIN 常量,如果未定义则通过header()进行跳转,在跳转后代码仍然会继续向下执行而没有使用die()函数结束下面语句的执行。

那么会继续走ueditor类下的 init() 方法,实现文件上传。

如果在代码中加入 die() 函数,那么获取不到session后直接退出而不会向下执行后面的代码。


漏洞复现:

在后台中发现允许上传类型中出现了.php文件类型

最后通过编辑器的文件上传功能进行文件上传,从下图可以看到文件上传已经成功然后进行302跳转


6,任意文件上传漏洞
进入站点设置,然后进行文件上传。



审计源码

在/system/basic/class/upload.class.php下


上传php文件开始调试
发现两个条件都不满足
1php后缀名不在$extension中
2!$type!=true也就是$type不为null
第一个条件
首先解决第一个问题,在文件29行处可以看到$extension变量值的获取


全局搜索upload_extension

存在允许上传类型,那么直接添加.php然后保存,回到源码继续上传.php调试


存在允许上传类型,那么直接添加.php然后保存,回到源码继续上传.php调试
可以看出这时候$extension数组中多了一个值即.php,成功满足第一个要求
第二个条件
要让!$type!=true,也就是让$type=null即可,也就是执行到函数的35行


$t是遍历$arrary获得的,而$array是分割$type获得的,可以看到files函数调用时$type默认值是null,那么就是调用时指定code值


在code键值中看到了我们想要上传的.php


所以的我们的$t应该为code,再回到upload.class.php


跟踪函数,定位到/system/extend/ueditor/php/ueditor.class.php中第246行,调用files函数并且指定了code值


漏洞复现:



7.未授取任意文件删除
对用户是否登录的验证在system/basic/class/admin.class.php文件init函数中


当判断未登录时通过header进行页面跳转,但是没有exit()或者die()终止程序运行
所以还是能够得到自己的结果后才跳转(这一点可以在BP中体现)
在未登录状态下
先执行删除,成功执行得到结果





8.未授权用户操作
用户操作相对于来说也算敏感操作,就顺带写了
确定位置
定位到/system/admin/manager/manager.class.php
其中的add,edit,delete三个函数参数都是由请求获得的(可控的)


根据规则构造请求包,以下为关键点
mold=manager&part=manager&func=add  /system/admin/manager/manager.class.php中的add函数
POST传参
username-用户名
password-密码
password-确认密码
level-权限 (2为系统管理员)
请求包


成功添加管理员用户


成功登录,且为管理员权限



文章来源: http://mp.weixin.qq.com/s?__biz=Mzk0MTIzNTgzMQ==&mid=2247503934&idx=1&sn=b65dcd41292b3d00b0db72f449da4624&chksm=c2d71b26f5a09230b8076099e5c3e604c0fa99804514b257650c8f6add87ce980edb6564b6d4#rd
如有侵权请联系:admin#unsafe.sh