某开源企业站CMS审计报告

admin 2023年11月27日08:04:53评论21 views字数 4050阅读13分30秒阅读模式

前言

最近渗透测试某站点的时候触发了一个报错,然后发现了站点使用的CMS,百度了一下是一个国产开源的企业级CMS。从官网拉下来审了一下,再此记录一下

入口

下面是index.php入口文件

<?php
if( !file_exists(dirname(__FILE__) . "/include/config.db.php") )
{
header("Location:install/index.php");
exit();
}
require_once( "include/common.inc.php" );
$mod = str_replace( '../', '', $mod );

if( empty( $mod ) )
{
$mod = 'index';
}

$action_file = WEB_INCLUDE . '/action/' . $mod . '.php';
file_exists($action_file) && require_once($action_file);

$cls_tpl = cls_app:: get_template( $mod );
$cls_tpl->display();

?>

很常见的cms入口形式,但是可以注意到第八行将../替换为空,这里怀疑会不会存在目录穿越,可以采用..././这样的形式来穿越到上一层。但是第15行限制了后缀必须为php,且由于前缀也被限制于是不能使用zip伪协议拿shell,如果php版本为5.2可以采用00截断包含任意文件,这里暂时卡住,继续审计,第2行应该为配置文件略过,跟进第7行的common.inc.php
某开源企业站CMS审计报告
common.inc.php开头先定义了许多常量,然后更改了一些php配置,接着又引入了两个文件,跟进发现配置了一些变量,先不管,继续向下审计common.inc.php

<?php

$req_data = array();
foreach( array('_GET', '_POST', '_COOKIE') as $_request )
{
foreach( $$_request as $_k => $_v )
{
${$_k} = _get_request($_v);

if( '_COOKIE' != $_request )
{
$req_data[$_k] = _get_request($_v);
}
}
}
unset($_GET, $_POST);
?>

上面代码可以很明显的发现,cms把$_GET,$_POST,$_COOKIE注册为了全局变量。所以之后可能存在变量覆盖,之后的代码引入了全局函数和全局类。这时候CMS入口以审计结束,可以开始审计函数和类

重安装漏洞(Getshell)

由于安装文件一般是漏洞的重灾地,于是这里直接跳到了安装文件,果然找到了漏洞点。
在install_action.php中,安装完成后会把前端文件重命名,但是后端逻辑文件依旧存在,所以如果知道安装文件位置即可重安装

<?php
function install_end()
{
//安装收尾

//把安装文件的名字换了
@rename('index.php', 'index.php_bak');
}

这里只重命名了前端文件

而且在文件280行,存在写文件操作,文件名为php且文件内容可控

<?php

$db_tablepre = $_POST['tablepre'];

write_db_config($db_type, $db_host, $db_name, $db_pass, $db_table, $db_tablepre);

function write_db_config($db_type, $db_host, $db_name, $db_pass, $db_table, $db_tablepre)
{
//写入数据库配置
global $db_code;
$db_config = "";
$db_config .= "<?phpnn";
$db_config .= "$db_type = '" . $db_type . "';n";
$db_config .= "$db_host = '" . $db_host . "';n";
$db_config .= "$db_name = '" . $db_name . "';n";
$db_config .= "$db_pass = '" . $db_pass . "';n";
$db_config .= "$db_table = '" . $db_table . "';n";
$db_config .= "$db_ut = '" . $db_code . "';n";
$db_config .= "$db_tablepre = '" . $db_tablepre . "';nn";
$db_config .= "?>";
require_once("../include/class/class.file.php");
$cls_file = new cls_file('../include/config.db.php');
$cls_file-> set_text($db_config);

return $cls_file-> write();
}

由于文件内容可控,我们可以通过tablepre=exp来写入一个恶意php文件

tablepre=dcr_qy_';?><?php phpinfo()?>
由于写入的是配置文件,所以访问站点任意文件都会包含此文件,所以还可以当后门来用。

某开源企业站CMS审计报告

审计过程还发现,如果采用sqlite安装,sqlite数据库文件名会以php结尾,并且我们可以控制数据库名数据库表,但是cms开始会新建一个名为<?php的数据表,sqlite文件的文件头会存在多个<?php,php解析到这里直接报错了。不知道怎么绕过。

sql注入

在install_action.php中还存在sql注入

<?php
$db_table = $_POST['table'];
$sql_db_exists = "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='$db_table'";

很明显的注入,就不细讲了

任意文件删除漏洞x2

全局搜索危险函数unlink
某开源企业站CMS审计报告
一个个跟进审计,查找拿些变量可控,最终找到两个任意文件删除漏洞

在fmanage_action.php中提供了文件删除功能,但是未对文件路径做过滤,于是可以删除任意文件
某开源企业站CMS审计报告
上面提到cms把所有url变量注册为了全局变量,于是只需访问action=del_file&cpath=../../../../../../../1.txt即可删除任意文件

cls_dir类中也存在一个任意文件删除漏洞
某开源企业站CMS审计报告
cache_clear.php引用了这个类

<?php
$cls_dir = new cls_dir();
$cls_dir-> delete_dir( WEB_CACHE . "/template/{$tpl_dir}" );

同理,通过控制url参数tpl_dir即可任意文件删除

失败的审计

在db.class.php中的构造方法里,可以进行数据库连接

<?php
class cls_db
{
private $pdo;

private $db_type;
private $host;
private $name;
private $pass;
private $table;
private $ut;
private $conn;

private $result;
private $rs;

private $str_error; //错误信息

/**
* 构造函数
* @param string $db_type 数据库类型
* @param string $host 数据库地址
* @param string $name 数据库用户名
* @param string $pass 数据库密码
* @param string $table 数据库名
* @param string $ut 数据库编码
* @return resource 成功返回一个连接的resource
*/
function __construct( $db_type, $db_host, $db_name, $db_pass, $db_table, $db_ut )
{
$this->db_type = $db_type;
$this->host = $db_host;
$this->name = $db_name;
$this->pass = $db_pass;
$this->table = $db_table;
$this->ut = $db_ut;
if( !$this->conn )
{
$this->connect();
}
}

因为构造方法只有在实例化新类才会被执行,所以理论上,如果我们可以任意实例化任意类,我们可以控制数据库连接的ip和端口,再通过mysql任意文件读取漏洞,即可达到任意文件读取,全局搜索new $
某开源企业站CMS审计报告
遗憾的是,这三个变量审计后发现我们都不可控,于是这条路没有走通,但是我觉得思路还是很不错的

失败的审计*2

class.email.php文件中会存在任意ip建立套接字,发送数据可控,于是我们可以通过crlf来SSRF,可以攻击内网的php-fpm,redis等应用,但是在刚开始建立套接字的时候,cms会判断对应ip是否返回2或者3,

<?php
function smtp_ok()
{
$response = str_replace("rn", "", fgets($this->sock, 512));
$this->smtp_debug( $response . "n" );


if (!ereg("^[23]", $response))
{
fputs($this->sock, "QUITrn");
fgets($this->sock, 512);
cls_app::log("Error: Remote host returned "" . $response . ""n");
return false;
}
return true;
}
?>

如果攻击内网,必须要对应内网服务在建立连接时,返回数据中带有2或者3,我们才能发送数据,否者程序会直接退出。

来源:https://xz.aliyun.com/ 感谢【evoA


原文始发于微信公众号(衡阳信安):某开源企业站CMS审计报告

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年11月27日08:04:53
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   某开源企业站CMS审计报告https://cn-sec.com/archives/2242356.html

发表评论

匿名网友 填写信息