首页 网络安全 正文

Cotonti CMS PHP 对象注入漏洞CVE-2026-71294

摘要

栋科技漏洞库关注到 Cotonti CMS 不受信任数据反序列化(PHP 对象注入漏洞),现已追踪为CVE-2026-71294,CVSS 3.X评分 7.6。

Cotonti 是基于 PHP+MySQL、BSD 协议开源轻量 CMS/CMF 内容管理框架,主打轻量化、模块化,体积小巧、服务器资源占用极低

一、基本情况

Cotonti CMS自带论坛社区能力兼顾建站与二次开发,适合论坛社群、游戏公会站点、小型社区门户、个人博客、企业简易官网等站点。

Cotonti CMS 不仅代码简洁易懂,适合新手学习 CMS 开发,且原生社区能力强劲,搭建论坛站点效率极高;模板改造灵活,自由度高。

Cotonti CMS PHP 对象注入漏洞CVE-2026-71294

栋科技漏洞库关注到 Cotonti CMS 不受信任数据反序列化(PHP 对象注入漏洞),现已追踪为CVE-2026-71294,CVSS 3.X评分 7.6。

二、漏洞分析

CVE-2026-71294漏洞是 Cotonti CMS 相关版本中存在的,注释插件 PHP 对象通过创建/编辑操作中的无限制 unserialize() 注入 漏洞。

漏洞位于Cotonti 评论插件,发表评论、编辑评论两处接口直接接收用户可控参数,解码后直接执行 unserialize(),未限制可实例化类。

普通注册会员即可发起攻击,构造恶意序列化对象,借助 PHP 魔术方法执行数据库写入、构造链路实现任意代码执行获取服务器权限。

具体来说,该漏洞源于 Cotonti CMS 的 Comments 插件反序列化用户提供的数据,而不限制可能实例化的类。

在plugins/comments/controllers/actions/CreateAction.php中,

通过cot_import(“ci”、“P”、“TXT”)(仅限修剪的净化)获得的“ci”POST参数被传递给“unserialize(base64_decode($ci))”,

没有allowed_classes限制,任何具有注释写权限成员都可访问(plugins/comments/setup.php中的默认Auth_members=>RW设置)。

在plugins/comments/controllers/actions/EditAction.php中,

`cb`参数在prepareComeBack()中通过`unserialize(base64_decode($this->revolution))`进行反序列化,

任何编辑自己注释的成员都可以访问。因为unserialize()是在没有allowed_classes的情况下调用的,

所以攻击者可以构造Cotonti(PHP对象注入原语)加载的任何类的序列化PHP对象。

使用Cotonti自己的MySQL_cache类在实践中证明了这一点:

一个精心制作的序列化MySQL_cach对象,一旦反序列化并稍后进行垃圾收集,就会触发其__destruct()->flush()链,

导致攻击者使用攻击者选择的行值控制INSERT INTO cot_cache,这证实了真正的POP链利用,

进一步的影响(包括潜在的RCE)取决于给定Cotonti安装的加载类中可用的其他小工具链。

DeleteAction.php中的第三个接收器包含相同的unserialize()模式,但在仅限管理员的授权检查后被门控,普通成员无法访问。

plugins/comments/controllers/actions/CreateAction.php
<?php
/**
 * Comments system for Cotonti
 * Add new comment action
 * @package Comments
 * @copyright (c) Cotonti Team
 * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt
 */

declare(strict_types=1);

namespace cot\plugins\comments\controllers\actions;

use Cot;
use cot\controllers\BaseAction;
use cot\exceptions\ForbiddenHttpException;
use cot\exceptions\NotFoundHttpException;
use cot\plugins\comments\controllers\IndexController;
use cot\plugins\comments\inc\CommentsControlService;
use cot\plugins\comments\inc\CommentsDictionary;
use cot\plugins\comments\inc\CommentsNotificationService;
use cot\plugins\comments\inc\CommentsService;
use cot\users\UsersHelper;

defined('COT_CODE') or die('Wrong URL');

/**
 * @property-read IndexController $controller
 */
class CreateAction extends BaseAction
{
    public function run(): string
    {
        global $cot_captcha;

        [$auth['read'], $auth['write'], $auth['admin']] = cot_auth('plug', 'comments');

        if (!$auth['write']) {
            throw new ForbiddenHttpException();
        }

        cot_shield_protect();

        $source = cot_import('source', 'P', 'ALP');
        $sourceId = cot_import('source-id', 'P', 'TXT');
        $extensionCode = cot_import('extension', 'P', 'ALP');
        $categoryCode = cot_import('category', 'P', 'TXT');
        $ci = cot_import('ci', 'P', 'TXT');

        // Commented Item url params
        $ciExtensionCode = $ciUrlParams = null;
        if (!empty($ci)) {
            $ci = unserialize(base64_decode($ci));
            $ciExtensionCode = $ci[0];
            $ciUrlParams = $ci[1];
            if (empty($extensionCode)) {
                $extensionCode = $ciExtensionCode;
            }
        }

        if (empty($source) || empty($sourceId)) {
            throw new NotFoundHttpException();
        }

        // Check if comments are enabled for specific category/item
        if (!empty($extensionCode)) {
            cot_block(
                CommentsService::getInstance()->isEnabled($extensionCode, $categoryCode)
            );
        }

        $comment = [
            'com_area' => $source,
            'com_code' => $sourceId,
            'com_author' => Cot::$usr['id'] === 0
                ? cot_import('comment_author', 'P', 'TXT')
                : UsersHelper::getInstance()->getFullName(Cot::$usr['profile']),
            'com_authorid' => Cot::$usr['id'],
            'com_text' => cot_import('comment_text', 'P', 'HTM'),
        ];

        // Extra fields
        if (!empty(Cot::$extrafields[Cot::$db->com])) {
            foreach (Cot::$extrafields[Cot::$db->com] as $extraField) {
                $comment['com_' . $extraField['field_name']] = cot_import_extrafields(
                    'comment_' . $extraField['field_name'],
                    $extraField,
                    'P',
                    '',
                    'comments_'
                );
            }
        }

        /* == Hook == */
        foreach (cot_getextplugins('comments.add.first') as $pl) {
            include $pl;
        }
        /* ===== */

        if (isset(Cot::$cfg['legacyMode']) && Cot::$cfg['legacyMode']) {
            // @deprecated in 0.9.26
            /* == Hook == */
            foreach (cot_getextplugins('comments.send.first') as $pl) {
                include $pl;
            }
            /* ===== */
        }

        $service = CommentsService::getInstance();
        $service->validateWithMessages($comment);

        if (empty($comment['com_author']) && Cot::$usr['id'] === 0) {
            cot_error(Cot::$L['comments_authorTooShort'], 'comment_author');
        }

        if (Cot::$usr['id'] === 0 && !empty($cot_captcha)) {
            $rverify = cot_import('rverify', 'P', 'TXT');
            if (!cot_captcha_validate($rverify)) {
                cot_error(Cot::$L['captcha_verification_failed'], 'rverify');
            }
        }

        /* == Hook == */
        foreach (cot_getextplugins('comments.add.validate') as $pl) {
            include $pl;
        }
        /* ===== */

        $errors = $this->controller->getErrors();
        if ($errors !== []) {
            return $this->controller->errorResult($errors);
        }

        $id = CommentsControlService::getInstance()->save(null, $comment, $ciExtensionCode, $ciUrlParams);

        $comment['com_id'] = $id;

        $_SESSION['cot_comments_edit'][$id] = Cot::$sys['now'];

        /* == Hook == */
        foreach (cot_getextplugins('comments.add.done') as $pl) {
            include $pl;
        }
        /* ===== */

        if (Cot::$cfg['plugin']['comments']['mail']) {
            CommentsNotificationService::getInstance()->notifyAdmins(
                $comment,
                CommentsDictionary::EVENT_CREATE,
                $ciExtensionCode,
                $ciUrlParams
            );
        }

        cot_message(Cot::$L['comments_added']);

        cot_shield_update(20, 'New comment');

        return $this->controller->successResult(Cot::$L['comments_added']);
    }
}

 

三、影响范围

未知

四、修复建议

未知

五、参考链接

管理员已设置登录后刷新可查看



扫描二维码,在手机上阅读
评论
更换验证码
友情链接