DolphinPHP RCE漏洞分析

  参考:https://xz.aliyun.com/t/11118

漏洞点定位

  跟修复的 Commit ​diff一下

  ​image​​​

  可以看到对call_user_func​函数的调用增加了is_disable_func()​的判断,所以漏洞点就是application\common.php​的call_user_func​函数

漏洞分析

判断参数是否可控

  从call_user_func​函数开始回溯,param[1]​是回调函数名称,log[$param[0]]​是传递给回调函数的参数。

  ​image

  **==$param[1]==**​:这个参数的值来自于解析日志规则时的 $match[1]​ 数组元素,而 $match[1]​ 是由正则表达式 /(\[\S+?\])/​ 匹配

  $action_info['log']​得出的。这个正则表达式的含义是匹配方括号内的非空白字符,但不包括空格、制表符、换行符等。而$action_info['log']​是一个数据库查询操作的返回值。

  ​​image​​

  ​**==$log[$param[0]]==**​:在上文中的代码中,$param[0]​ 的值是从日志规则中解析出来的。在正常情况下,$param[0]​ 代表的是一个特定的日志数据字段值

  ​image

  ‍

  ​model('admin/action')->where('module', $module)->getByName($action)​的作用是根据特定的$module​和$action​,在数据库中查找对应的信息。找一下model,发现model('admin/action')​对应的数据表是admin_action

  ​image

  用phpstorm的插件连一下数据库,看一下表的结构是咋样的,如下图

  ​image

  那么我们现在就知道了,$action_info['log']​对应的就是指定的$module​和$action​对应的 log 值

  那么log数据我们是否可控呢?可以在后台看到,action_log()​对应的功能是系统中的行为管理

  ​​image​​

  且log值可控,没有任何校验

  ​image

$param[1]​​参数构造

  我们现在已经可以确定call_user_func()​的回调函数名称$param[1]​是可控的,只需要将指定的 log 数据根据 “|” 分割后的第二个数据替换为恶意的函数名称即可,如[xxxxx|phpinfo]

  ‍

$log[$param[0]]​​参数构造

  根据上述分析,$param[0]​也是可控的,只需要将指定的 log 数据根据 “|” 分割后的第一个数据替换即可。

  那么我们看一下$log[]​有哪些字段值是可控的?答案是$model​和$details​是可控的

  ​image因此我们只需要令$param[0]​的值为$model​或$details​即可控制$log[$param[0]]​参数,也就是控制传递给回调函数的参数。

  那么再进行回溯,看看哪里调用了action_log()​,且$model​或$details​可控

  先查找action_log()​的用法

\app\admin\controller\Attachment::disable 处调用分析

  查找用法发现了这个setStatus​方法,根据注释可以知道​@param string $type 操作类型:enable,disable,delete

  这个setStatus​方法通过call_user_func_array​调用了​action_log

  ​image

  ​image

  然后回溯一下setStatus​方法,发现\app\admin\controller\Attachment::disable​方法调用setStatus​并传参$type​为disable​,然后setStatus​方法从请求中获取要操作的记录ID(ids​),然后将这些ID转换为逗号分隔的字符串。接着,它调用了父类的 setStatus​ 方法,并传递了一些参数,包括$ids​也就是上文中提到我们需要可控的$details​参数,且该处对这个$ids​没有任何过滤和判断

  即$ids​->$details​->$log[$param[0]]​可控

  ​image

  ‍

  ‍

漏洞利用

  上述分析中采用的皆为回溯调用的方式进行分析,现在从功能点开始入手进行完整的漏洞利用分析。

  根据\app\admin\controller\Attachment::disable​方法的注释可知,此处对应的功能点为禁用附件

  先打个断点调一下

  ​image

  功能点位于后台中系统的附件管理

  ​image根据代码需要禁用存在的附件,因此在个人信息处上传一个头像作为附件

  ​image

  可以看到附件处多出了一个附件

  ​image点击禁用并抓包

  ​image

  进入断点后一直跟进,成功进入到action_log​方法,并且看到$model​是 attachment_disable$details​是我们可控的值

  ​image​​image

  那么我们就在行为管理处修改 attachment_disable ​的值

  ​image

  根据漏洞分析,将log值修改为如下

  ​​image

  ‍

  点击禁用附件

  ​image

  抓包修改ids​值,也就是action_log​方法中$log​数组的details​值

  ​image

  ‍

  进入断点跟一下,先看看数据库操作

  ​image

  找一下对应的值,成功匹配到我们上面修改的规则​[details|system]

  ​image

  继续跟进代码直到call_user_func​处

因为在行为管理处将attachment_disable​的日志规则修改为[details|system]
因此$param[0]='details'$param[1]='system'

  并且抓包修改了ids​为calc​,即$details​为calc

  ‍

  因此最终的执行语句为call_user_func("system","calc");

  ‍

  ​image

  漏洞利用成功

  ​image

  ‍

官方漏洞修复分析

看了一下,官方修复方案就是加了黑名单检测,然后黑名单漏了不少php命令执行的函数(打过ctf的应该一眼能看出来了
一直到最新版也没有修复,直接交cnvd了(大概率重复了


更新一下,结果居然没有重复,还是水了一个cnvd

image-20230927204100525