新手第一次发文,欢迎各位师傅们指点和交流。

0x00 前言

DophinPHP(海豚PHP)是一个基于ThinkPHP5.1.41 LTS开发的一套开源PHP快速开发框架,官方地址是DolphinPHP(海豚PHP)

0x01 漏洞分析

通过查看Release,可以发现在最新版V1.5.1中修复了来自V1.5.0版本的行为日志执行漏洞。

进一步追踪,定位到了漏洞触发点。

代码是开源的,直接拖到本地进行搭建并分析。

首先查看目录结构,典型的TP框架目录结构。

相关的漏洞触发点在application/common.php文件的1086行。

可以知道的是,call_user_func($callback, ...$parameters)函数用于调用回调函数,在其参数可控的情况下,可导致代码执行等漏洞。

<?php
    call_user_func('system', 'whoami'); # 执行系统命令 whoami
?>

回到漏洞触发点,对于如下代码,我们要做的就是控制call_user_func()函数的2个参数。

先追溯$param[1],定位到1084行。

$param = explode('|', $value);

explode()函数将|竖线作为分隔符,对$value进行分割,将处理后的字符串变成数组赋值给$param

追溯$value,定位到1084行。

foreach($match[1] as $value){

这句话的意思就是将$match[1]数组中的值逐个赋值给$value

追溯$match[1],定位到1072行。

if(preg_match_all('/\[(\S+?)\]/', $action_info['log'], $match)){

$match位于preg_match_all()函数的第三个参数,第三个参数用于存储匹配的结果。而正则/\[(\S+?)\]/匹配包含在方括号中的任何非空白字符序列,并将其作为捕获组进行提取。

所以$match[1]的值为不带方括号的字符串,其值从$action_info['log']中匹配而来。

继续往上追溯,定位到1055行。

$action_info = model('admin/action')->where('module', $module)->getByName($action);

model('admin/action'),故定位到application/admin/model/Action.php,可以看到当前模型对应的完整数据表名称为:admin_action。

整条语句要表达的意思就是从admin_action数据表中提取出满足module=$modulename=$action的记录,所以$action_info['log']的值自然就是记录中列名为log的值。可以看一下数据库存储信息。

对于$param[1]的追溯到这其实已经差不多可以结束了,从admin_action表中取出module=$modulename=$action的记录,提取其log字段方括号内的值,再将|竖线作为分割符号,取出第二个字符串作为call_user_func()函数的第一个参数,可以简化为如下demo:

<?php
    $log = '[test|system]demo';
    preg_match_all('/\[(\S+?)\]/', $log, $match);
    foreach ($match[1] as $value){
        $param = explode('|', $value);
        var_dump($param[1]); // 打印call_user_func()第一个参数值
    }
?>

下一步我们只需要控制log字段的值即可。很方便的是,Dolphin管理员后台很贴心的为我们准备了相应的功能用于自定义action_log,相应的功能点在:系统->系统功能->行为管理。

点击编辑,可以对日志规则进行自定义。

日志规则正好对应admin_action数据表中的log字段。

解决完第一个参数后,再看到$log[$param[0]],这段表达式用于取出$log数组中下标为$param[0]的值。$param[0]我们可以很轻松地进行控制,而对于$log,则需要往前进行追溯。

定位到1073行~1080行。

$log = [
    'user'    => $user_id,
    'record'  => $record_id,
    'model'   => $model,
    'time'    => request()->time(),
    'data'    => ['user' => $user_id, 'model' => $model, 'record' => $record_id, 'time' => request()->time()],
    'details' => $details
];

同样寻找可控变量,以$details为例。

查找调用action_log方法的地方,可以看到该方法被大量调用。以application/user/admin/Role.php为例,定位到246行。

这是一个编辑角色的功能点,$details的值来自于$data['name'],而$data的值从POST传参中获取,也就是说我们可以通过POST传参name从而修改$details的值。简化一下代码:

<?php
    ... # 省略大量代码
    $data = $this->request->post();
    ... # 省略大量代码
    action_log('role_edit', 'admin_role', $id, UID, $data['name']);
    ... # 省略大量代码

梳理一下思路:call_user_func()的第一个参数由行为管理功能点控制,第二个参数由角色编辑控制。

0x02 漏洞复现

登录后台,默认账号密码:admin/admin

来到行为管理处,搜索role_edit,编辑相关字段。

编辑日志规则:[details|system],其中systemcall_user_func()第一个参数;details是键名,该键对应的值则是call_user_func()的第二个参数。

修改完毕后来到角色管理:用户->权限管理->角色管理。

首先新增一个角色。


编辑该角色,同时抓取相关数据包,将name参数的值修改为系统命令。

成功执行命令。

0x03 参考文章

https://xz.aliyun.com/t/11118
https://www.xx5xx.top/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/DolphinPHP-RCE%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/index.html

点击收藏 | 0 关注 | 1 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖