yccms v3.4 代码审计

程序版本:

20250708090010810

从这个目录结构注意到这是一个MVC模式

20250708135815537

通读代码

admin

index.php

后台入口

require 引入文件 /config/run.inc.php 完成网站的初始化

路由:GET /admin?a=

ceshi1&ceshi2&compile

都是一些页面模板

config

config.inc.php

数据库、Smarty以及其他的系统配置

count.php

通过计算根目录,加载 run.inc.php文件

run.inc.php

初始化文件、入口文件(进入后台会调用)

功能:

  • 开启session,设置编码和时区

  • 引入配置文件和模板:config/config.inc.php

    ​ /public/smarty/Smarty.class.php

  • 自动加载类:__autoload()方法用于自动加载类

    ​ Action的类加载controller

    ​ Model的类加载model

    ​ 其他的类加载public/class/

  • 单入口:Factory::setAction()->run();调用控制器的run(),

安全问题:入口文件的Factory给下面的RCE提供了入口点

配置文件会有漏洞吗?配置文件会不会泄露数据库信息?

contrller

Action.class.php

所有控制器的父类

功能:

  • 定义属性
  • 构造函数
  • 分页、静态分页功能
  • 控制器运行入口

AdminAction.class.php

管理员控制器

功能:

  • 加载后台首页

  • 更改密码:使用sha1加密不安全

  • 系统信息显示:

    系统信息页:

    20250711132213642

  • 退出时清理缓存:删除 compile 目录文件

路由:?a=admin&m=update -> 调用 update()

ArticleAction.class.php

文章控制器,

功能:显示文章列表

​ 提供搜索功能

​ 增删修改文章

​ nav、attr(文章的修饰属性)等功能

CallAction.class.php

功能: 验证码生成

​ 后台文件上传

​ 编辑器上传

HtmlAction.class.php

生成静态控制器

生成静态页面:

首先调用模型,取出数据库内容,之后通过模板引擎渲染页面,输出HTML页面,然后使用工具类Tool::HtmlFile($filename, $content),把HTML内容保存到指定路径,就可以在前端页面查看了。

静态页面作用:

  • 直接访问.html文件,不需要调动数据库查询;
  • 页面生成后不再执行动态代码,防止SQL注入;
  • 当然,这种把内容提前准备好的方式,对于提升性能、减少算力、节约服务器资源、服务器更稳定等等有一定的优势。

本网站后台修改文章内容后需要静态生成,之后方可在首页显示。

功能: 生成首页

​ 生成文章

​ 栏目列表

​ 模板渲染

​ 静态化输出

​ 分步处理

20250711154124943

LinkAction.class.php

链接控制器

功能:后台管理友情链接,修改链接、排序功能

LoginAction.class.php

登录控制器

功能:登录验证:sha1加密密码

​ 验证码

​ 记住密码

​ AJAX 验证

分类控制器

功能:查看、排序、增删修改分类列表

PicAction.class.php

图片控制器

功能:

  • 后台图片展示:读取根目录中的uploads文件夹,但是没有过滤文件非法后缀,此处可能上传 .php 文件

  • 删除:没有检验图片路径,存在任意文件删除漏洞

  • 这里并没有进行用户权限的判断,非管理员(未登录)用户也可访问到图片列表并删除

SearchAction.class.php

搜索控制器

功能:用于前端页面的内容搜索

SystemAction.class.php

系统设置控制器

功能:后台系统信息、设置首页文字内容

记一些知识点、函数

PHP 中 private、public、protected区别

PHP 之 Smarty 模板引擎使用汇总

Smarty 是 PHP 的一个引擎模板,可以将 MVC 中的 C 分离出来。

JavaScript之Ajax

AJAX (Asynchronous JavaScript and XML) 异步 JS 和 XML,在不刷新整个页面的情况下,与服务器交换数据的技术。

htmlspecialchars()

htmlspecialchars() 函数把预定义的字符转换为 HTML 实体。

预定义的字符是:

  • & (和号)成为 &
  • " (双引号)成为 "
  • ’ (单引号)成为 '
  • < (小于)成为 <
  • > (大于)成为 >

历史漏洞

YCCMS存在文件上传漏洞(CNVD-2021-47137)

YCCMS存在文件上传漏洞(CNVD-2021-46794)

YCCMS存在逻辑缺陷漏洞

RCE漏洞复现

根据Action.class.php审计,发现method_exists($this, $_m) ? eval('$this->'.$_m.'();') : $this->index();

eval() 可以执行任意字符串形式的 PHP 代码,此处通过搜索发现 run()函数 出现在 config/run.inc.php 这个文件,存在rce

1、绕过 file_exists()

这个函数在进行检查时,比如/controller/admin;/../,函数允许路径中有一些特殊字符,并且遇到/../会返回到上级目录,可以利用这个绕过 file_exists()函数检查。

那么我们构造poc:Factory();phpinfo();//../

2、入口点

调用Factory() 的入口点在 Factory::setAction()->run(); 这里,在admin/index.php 这个文件中得知,它包含了/config/run.inc.php,可以利用

3、POC

/admin?a=Factory();phpinfo();//../

20250710234248614

任意文件删除

根据审计PicAction.class.php时遇到的delall()函数,无验证造成的任意文件删除漏洞,进行复现。

来到对应的功能点:

20250712193547260

先给 upload 随便上传一张图片

20250712193717272

20250712193754284

删除,进行抓包:

20250712193827895

20250712193843074

URL编码:pid%5B0%5D=1.png&chkall=on&send=%E5%88%A0%E9%99%A4%E9%80%89%E4%B8%AD%E5%9B%BE%E7%89%87

解码:

pid[0]=1.png&chkall=on&send=删除选中图片

chkall=on是一个复选框

20250712194432977

可以看到只要在pid后的文件名进行路径拼接就可以跳到任意目录去删除文件

接下来就可以根据上面的数据构造POC:

pid[0]=/../1.txt&chkall=on&send=删除选中图片

pid%5B0%5D=/../1.txt&chkall=on&send=%E5%88%A0%E9%99%A4%E9%80%89%E4%B8%AD%E5%9B%BE%E7%89%87

路由:admin/?a=pic&m=delall

在根目录准备一个1.txt

20250712194601104

退出登录

20250712194749344

POST传参:

20250712195154540

上图的浏览器删不掉,换了一个成功了

20250712195540507

20250712195612613

任意文章/文件(no)删除

这里想到审计ArticleAction.class.php时也有delall()函数,看看这里有没有文章删除漏洞。

delete()

GET传参,

找到一篇文章的id

20250712200511140

构造POC:admin/?a=article&m=delete&id=2450

20250712200604332

执行后发现这篇文章被删除了

20250712200619491

delall()

功能点:此处多选删除

20250712201015073

抓包:

20250712201031989

showid%5B%5D=2449&showid%5B%5D=2448&showid%5B%5D=2447&showid%5B%5D=2446&chkall=on&send=%E6%89%B9%E9%87%8F%E5%88%A0%E9%99%A4

同样的,构造POC:

showid%5B%5D=%2F..%2F1.txt&showid%5B%5D=2448&showid%5B%5D=2447&showid%5B%5D=2446&chkall=on&send=%E6%89%B9%E9%87%8F%E5%88%A0%E9%99%A4

路由:admin/?a=article&m=delall

20250712201614722

但是只删除了文章并没有删除1.txt ??

再认真分析一遍:

直接传递/../1.txt作为ID值,系统会将/../1.txt当作文章ID去数据库查询,显然这个ID不存在

想要利用这里的漏洞,只能去更改数据库中HTML的内容,

添加、修改、删除都进行了用户验证,所以这个也不算一个漏洞了。

任意文件上传

controller/CallAction.class.php

根据Content-Type的值来判断是否是图片格式,只要Content-Type是这两种类型就可以,那直接伪造Content-Type就可以了

任意文件上传-2

controller/CallAction.class.php

代码中的文件名以时间+100到1000之间的随机数进行重命名

同样也是检查的传入的Content-Type的值

未授权更改管理员账号密码

首先来看一下漏洞利用过程,在未登录的情况下构造url,只需要更改username password notpassword的值即可更改数据库中admin账号的相关信息

根据url来定位一下漏洞函数,函数位于controller\AdminAction.class.php中的update函数

可以看到前面都是一些判断,重点关注下editAdmin()函数,该函数位于model\AdminModel.class.php

该函数的父类为Model, 位于model\Model.class.php,看一下update函数

调用execute函数去执行sql语句

这一系列的操作主要是用来生成SQL语句然后执行SQL语句,editAdmin函数直接把传进来的username password拼接到sql语句中,然后去更新相关表中id=1的数据,这也就造成了任意更改管理员账号密码