yccms代码审计学习
字数 4180 2025-10-26 18:21:34

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

文档概述

本教学文档旨在通过剖析YCCMS(一个内容管理系统)v3.4版本的源代码,系统地讲解代码审计的流程、常见漏洞的挖掘方法、漏洞原理及修复方案。YCCMS是一个典型的MVC(Model-View-Controller)架构的PHP应用程序,非常适合用于学习PHP代码审计。

审计目标: YCCMS v3.4
核心发现: 存在未授权访问、任意文件删除、文件上传绕过、代码执行等高危漏洞。


一、 基础信息收集与分析

在开始深入审计前,需要对目标应用有一个基本的了解。

  1. URL结构分析

    • 通过观察后台管理功能的URL,可以发现其采用am参数分别指代控制器(Controller)和方法(Method)。
    • 示例http://127.0.0.1/admin/?a=article&m=add
      • a=article 表示调用 ArticleAction 控制器。
      • m=add 表示调用该控制器下的 add() 方法。
    • 对应的文件路径为:/controller/ArticleAction.class.php
  2. 框架识别

    • 尝试搜索 versionthinkphp 等关键字,未发现使用已知主流框架(如ThinkPHP)的痕迹。结论:这是一个原生PHP编写的、自研的MVC框架应用。

二、 漏洞审计详解

漏洞1:未授权访问导致的管理员密码篡改

  • 漏洞类型: 访问控制缺失

  • 危险等级: 高危

  • 漏洞位置admin/?a=admin&m=update

  • 涉及文件

    • /controller/AdminAction.class.php 中的 update() 方法。
    • /model/AdminModel.class.php 中的 editAdmin() 方法。
  • 漏洞原理

    1. AdminAction.class.phpupdate()方法中,直接调用了$this->_model->editAdmin()来修改管理员信息。
    2. 整个调用链没有任何的会话验证或权限检查(例如,没有检查用户是否已登录、是否是管理员)。
    3. 因此,攻击者无需登录,即可直接通过访问该URL并传入新用户名和密码的参数,篡改任意管理员账号的密码。
  • 审计技巧

    • 入口扫描: 重点关注所有/admin/?a=...的入口点。
    • 权限校验缺失: 检查每个控制器方法的首部是否存在统一的权限验证代码(如$this->checkLogin();)。在本案例中,整个应用缺乏这种全局或基类的控制。
  • 举一反三
    通过此漏洞可以发现,整个后台的权限校验体系是缺失的。审计员应系统性地检查所有后台功能(如文章删除a=article&m=delete),很可能都存在类似的未授权访问问题。

漏洞2:未授权任意文件删除

  • 漏洞类型: 权限绕过、路径遍历

  • 危险等级: 高危

  • 漏洞位置admin/?a=pic&m=delall

  • 涉及文件/controller/PicAction.class.php 中的 delall() 方法。

  • 漏洞原理

    1. 该方法用于批量删除图片。它接收一个名为pid[]的数组参数,数组中包含要删除的图片文件名。
    2. 代码直接将文件名与基础路径ROOT_PATH.'/uploads/'进行拼接,形成完整的文件路径 $_filePath
    3. 随后直接调用 unlink($_filePath) 进行删除,未对文件名进行任何过滤
    4. 由于该功能同样缺乏权限校验,攻击者可以构造特殊的文件名进行路径遍历,删除服务器上的任意文件。
  • 攻击复现(PoC)

    • URL: http://127.0.0.1/admin/?a=pic&m=delall
    • POST数据:
      pid[0]=../../../config/database.config.php
      chkall=on
      send=删除选中图片
      
    • 此Payload可以删除网站根目录下config文件夹中的数据库配置文件,导致网站瘫痪。

漏洞3/4:文件上传漏洞(两处)

这两处漏洞原理相似,都是由于对上传文件的MIME类型验证存在缺陷。

  • 漏洞类型: 文件上传绕过

  • 危险等级: 高危

  • 漏洞位置1admin/?a=call&m=upfile&type=content (最终调用 upLoad 方法)

  • 漏洞位置2admin/?a=call&m=xhUp&type=content

  • 涉及文件/controller/CallAction.class.php 中的 upLoad()xhUp() 方法。

  • 漏洞原理

    1. 代码在验证文件类型时,依赖于 $_FILES['pic']['type'] 的值。
    2. $_FILES['pic']['type'] 是由浏览器端提交的,可以被攻击者随意篡改
    3. 验证逻辑 in_array($this->type, $this->typeArr) 只是简单判断这个值是否在允许的类型数组中(如 image/png)。
    4. 攻击者只需上传一个恶意文件(如.php后缀),然后在Burp Suite等工具中抓包,将 Content-Type 头部修改为 image/png,即可绕过检查,成功上传Webshell。
  • 修复方案对比

    • 错误方式: 信任客户端提交的MIME类型。
    • 正确方式: 使用服务器端验证,例如检查文件的真实类型(使用 finfo_file(FILEINFO_MIME_TYPE) 函数)和文件扩展名白名单机制。

漏洞5:代码执行漏洞

  • 漏洞类型: 代码注入

  • 危险等级: 严重

  • 漏洞位置: 多个入口文件,如 /admin/index.php, /search/index.php, /config/count.php

  • 涉及文件/public/class/Factory.class.php

  • 漏洞原理

    1. Factory.class.php 是程序的调度核心。它通过 $_a = self::getA(); 获取URL中的 a 参数。
    2. 然后使用 file_exists() 检查对应的控制器文件是否存在。
    3. 最后,使用 eval() 函数动态实例化控制器类:eval('self::$_obj = new '.ucfirst($_a).'Action();');
    4. 这里的 $_a 参数未经任何过滤就直接拼接进了eval语句,造成了代码注入漏洞。
  • 漏洞利用详解

    • 目标: 闭合 new 语句,并注入恶意代码。
    • Payload构造a=Factory();phpinfo();//../
    • 代码解释
      1. ucfirst($_a)a 的值变为 Factory();phpinfo();//../
      2. 拼接后的eval语句为:self::$_obj = new Factory();phpinfo();//../Action();
      3. // 在PHP中是注释符,其后的 ../Action(); 会被注释掉。
      4. 最终执行的代码是:
        self::$_obj = new Factory(); // 实例化Factory类(如果该类存在且可实例化)
        phpinfo();                   // 执行phpinfo()函数
        
    • 利用结果: 访问 http://127.0.0.1/admin/index.php?a=Factory();system('calc');//../ 甚至可以在服务器上弹出计算器(如果服务器是Windows系统且权限允许),证明漏洞危害极大。
  • 关键技巧

    • 利用 file_exists() 在Windows下的路径解析特性(// 被当作 \.. 用于目录遍历),使检查能够通过,指向一个实际存在的文件(如../Action.class.php),从而绕过 file_exists() 的检查。

三、 漏洞修复方案总结

针对以上发现的漏洞,提出以下系统性的修复建议:

  1. 强化权限校验体系

    • 建立一个基类控制器(如BaseAction.class.php),在其构造函数中调用统一的权限验证方法(如checkPermission())。
    • 让所有后台的控制器都继承自这个基类,确保每个后台操作在执行前都经过了权限验证。
  2. 安全的文件删除

    • 不要直接使用用户输入拼接路径。建议通过加密的ID映射到具体的文件路径,或对文件名进行严格的白名单过滤(只允许字母、数字、短横线、下划线等)。
    • 在删除前,再次验证目标文件是否位于允许操作的目录内(如/uploads/)。
  3. 加固文件上传功能

    • 采用白名单机制: 只允许指定的、安全的文件扩展名(如.jpg, .png, .gif)。
    • 服务器端验证: 使用 finfo_file() 函数检测文件的真实MIME类型,绝不信任 $_FILES['file']['type']
    • 重命名文件: 将上传的文件重命名为随机字符串(如UUID),并保留原有扩展名,防止文件名注入和恶意文件被执行。
    • 隔离存储: 将上传的文件存储在Web根目录之外,通过专门的脚本来访问,避免直接执行。
  4. 杜绝代码注入

    • 绝对禁止使用eval(): 尤其是在动态实例化类的地方。应使用安全的反射机制或预先定义好的映射数组来替代。
    • 示例(安全方式)
      $allowedControllers = ['article', 'admin', 'pic', ...];
      $controllerName = $_GET['a'];
      if (!in_array($controllerName, $allowedControllers)) {
          die('Invalid controller');
      }
      $className = ucfirst($controllerName) . 'Action';
      self::$_obj = new $className(); // 安全的方式,无需eval
      

四、 代码审计方法论总结

通过本次YCCMS的审计,可以总结出以下方法论:

  1. 信息收集: 理解应用的URL路由、目录结构和技术栈。
  2. 入口点定位: 梳理所有用户可控的输入点(GET/POST参数、Cookie、HTTP头)。
  3. 敏感函数追踪: 全局搜索 unlink(文件删除)、move_uploaded_file(文件上传)、eval/assert(代码执行)、system/exec(命令执行)、SQL查询函数等,逆向追踪用户输入是否能够不受控制地到达这些函数。
  4. 权限校验审查: 系统性检查关键功能是否存在权限校验,是否存在越权访问(水平越权、垂直越权)。
  5. 逻辑漏洞挖掘: 关注业务流程,如验证码绕过、密码修改、订单支付等环节是否存在逻辑缺陷。

希望这份详尽的教学文档能帮助你深入理解代码审计的思维和技巧。

YCCMS v3.4 代码审计教学文档 文档概述 本教学文档旨在通过剖析YCCMS(一个内容管理系统)v3.4版本的源代码,系统地讲解代码审计的流程、常见漏洞的挖掘方法、漏洞原理及修复方案。YCCMS是一个典型的MVC(Model-View-Controller)架构的PHP应用程序,非常适合用于学习PHP代码审计。 审计目标: YCCMS v3.4 核心发现: 存在未授权访问、任意文件删除、文件上传绕过、代码执行等高危漏洞。 一、 基础信息收集与分析 在开始深入审计前,需要对目标应用有一个基本的了解。 URL结构分析 : 通过观察后台管理功能的URL,可以发现其采用 a 和 m 参数分别指代控制器(Controller)和方法(Method)。 示例 : http://127.0.0.1/admin/?a=article&m=add a=article 表示调用 ArticleAction 控制器。 m=add 表示调用该控制器下的 add() 方法。 对应的文件路径为: /controller/ArticleAction.class.php 。 框架识别 : 尝试搜索 version 、 thinkphp 等关键字,未发现使用已知主流框架(如ThinkPHP)的痕迹。结论:这是一个原生PHP编写的、自研的MVC框架应用。 二、 漏洞审计详解 漏洞1:未授权访问导致的管理员密码篡改 漏洞类型 : 访问控制缺失 危险等级 : 高危 漏洞位置 : admin/?a=admin&m=update 涉及文件 : /controller/AdminAction.class.php 中的 update() 方法。 /model/AdminModel.class.php 中的 editAdmin() 方法。 漏洞原理 : 在 AdminAction.class.php 的 update() 方法中,直接调用了 $this->_model->editAdmin() 来修改管理员信息。 整个调用链 没有任何的会话验证或权限检查 (例如,没有检查用户是否已登录、是否是管理员)。 因此,攻击者无需登录,即可直接通过访问该URL并传入新用户名和密码的参数,篡改任意管理员账号的密码。 审计技巧 : 入口扫描 : 重点关注所有 /admin/?a=... 的入口点。 权限校验缺失 : 检查每个控制器方法的首部是否存在统一的权限验证代码(如 $this->checkLogin(); )。在本案例中,整个应用缺乏这种全局或基类的控制。 举一反三 : 通过此漏洞可以发现, 整个后台的权限校验体系是缺失的 。审计员应系统性地检查所有后台功能(如文章删除 a=article&m=delete ),很可能都存在类似的未授权访问问题。 漏洞2:未授权任意文件删除 漏洞类型 : 权限绕过、路径遍历 危险等级 : 高危 漏洞位置 : admin/?a=pic&m=delall 涉及文件 : /controller/PicAction.class.php 中的 delall() 方法。 漏洞原理 : 该方法用于批量删除图片。它接收一个名为 pid[] 的数组参数,数组中包含要删除的图片文件名。 代码直接将文件名与基础路径 ROOT_PATH.'/uploads/' 进行拼接,形成完整的文件路径 $_filePath 。 随后直接调用 unlink($_filePath) 进行删除, 未对文件名进行任何过滤 。 由于该功能同样 缺乏权限校验 ,攻击者可以构造特殊的文件名进行路径遍历,删除服务器上的任意文件。 攻击复现(PoC) : URL : http://127.0.0.1/admin/?a=pic&m=delall POST数据 : 此Payload可以删除网站根目录下 config 文件夹中的数据库配置文件,导致网站瘫痪。 漏洞3/4:文件上传漏洞(两处) 这两处漏洞原理相似,都是由于对上传文件的MIME类型验证存在缺陷。 漏洞类型 : 文件上传绕过 危险等级 : 高危 漏洞位置1 : admin/?a=call&m=upfile&type=content (最终调用 upLoad 方法) 漏洞位置2 : admin/?a=call&m=xhUp&type=content 涉及文件 : /controller/CallAction.class.php 中的 upLoad() 和 xhUp() 方法。 漏洞原理 : 代码在验证文件类型时,依赖于 $_FILES['pic']['type'] 的值。 $_FILES['pic']['type'] 是由浏览器端提交的, 可以被攻击者随意篡改 。 验证逻辑 in_array($this->type, $this->typeArr) 只是简单判断这个值是否在允许的类型数组中(如 image/png )。 攻击者只需上传一个恶意文件(如 .php 后缀),然后在Burp Suite等工具中抓包,将 Content-Type 头部修改为 image/png ,即可绕过检查,成功上传Webshell。 修复方案对比 : 错误方式 : 信任客户端提交的MIME类型。 正确方式 : 使用服务器端验证,例如检查文件的真实类型(使用 finfo_file(FILEINFO_MIME_TYPE) 函数)和文件扩展名白名单机制。 漏洞5:代码执行漏洞 漏洞类型 : 代码注入 危险等级 : 严重 漏洞位置 : 多个入口文件,如 /admin/index.php , /search/index.php , /config/count.php 。 涉及文件 : /public/class/Factory.class.php 。 漏洞原理 : Factory.class.php 是程序的调度核心。它通过 $_a = self::getA(); 获取URL中的 a 参数。 然后使用 file_exists() 检查对应的控制器文件是否存在。 最后,使用 eval() 函数动态实例化控制器类: eval('self::$_obj = new '.ucfirst($_a).'Action();'); 这里的 $_a 参数 未经任何过滤就直接拼接进了eval语句 ,造成了代码注入漏洞。 漏洞利用详解 : 目标 : 闭合 new 语句,并注入恶意代码。 Payload构造 : a=Factory();phpinfo();//../ 代码解释 : ucfirst($_a) 将 a 的值变为 Factory();phpinfo();//../ 。 拼接后的eval语句为: self::$_obj = new Factory();phpinfo();//../Action(); // 在PHP中是注释符,其后的 ../Action(); 会被注释掉。 最终执行的代码是: 利用结果 : 访问 http://127.0.0.1/admin/index.php?a=Factory();system('calc');//../ 甚至可以在服务器上弹出计算器(如果服务器是Windows系统且权限允许),证明漏洞危害极大。 关键技巧 : 利用 file_exists() 在Windows下的路径解析特性( // 被当作 \ , .. 用于目录遍历),使检查能够通过,指向一个实际存在的文件(如 ../Action.class.php ),从而绕过 file_exists() 的检查。 三、 漏洞修复方案总结 针对以上发现的漏洞,提出以下系统性的修复建议: 强化权限校验体系 : 建立一个基类控制器(如 BaseAction.class.php ),在其构造函数中调用统一的权限验证方法(如 checkPermission() )。 让所有后台的控制器都继承自这个基类,确保每个后台操作在执行前都经过了权限验证。 安全的文件删除 : 不要直接使用用户输入拼接路径 。建议通过加密的ID映射到具体的文件路径,或对文件名进行严格的白名单过滤(只允许字母、数字、短横线、下划线等)。 在删除前,再次验证目标文件是否位于允许操作的目录内(如 /uploads/ )。 加固文件上传功能 : 采用白名单机制 : 只允许指定的、安全的文件扩展名(如 .jpg , .png , .gif )。 服务器端验证 : 使用 finfo_file() 函数检测文件的真实MIME类型,绝不信任 $_FILES['file']['type'] 。 重命名文件 : 将上传的文件重命名为随机字符串(如UUID),并保留原有扩展名,防止文件名注入和恶意文件被执行。 隔离存储 : 将上传的文件存储在Web根目录之外,通过专门的脚本来访问,避免直接执行。 杜绝代码注入 : 绝对禁止使用 eval() : 尤其是在动态实例化类的地方。应使用安全的反射机制或预先定义好的映射数组来替代。 示例(安全方式) : 四、 代码审计方法论总结 通过本次YCCMS的审计,可以总结出以下方法论: 信息收集 : 理解应用的URL路由、目录结构和技术栈。 入口点定位 : 梳理所有用户可控的输入点(GET/POST参数、Cookie、HTTP头)。 敏感函数追踪 : 全局搜索 unlink (文件删除)、 move_uploaded_file (文件上传)、 eval / assert (代码执行)、 system / exec (命令执行)、SQL查询函数等,逆向追踪用户输入是否能够不受控制地到达这些函数。 权限校验审查 : 系统性检查关键功能是否存在权限校验,是否存在越权访问(水平越权、垂直越权)。 逻辑漏洞挖掘 : 关注业务流程,如验证码绕过、密码修改、订单支付等环节是否存在逻辑缺陷。 希望这份详尽的教学文档能帮助你深入理解代码审计的思维和技巧。