一、前言
1、系统重装漏洞的种类
-
自动删除安装文件
常见的系统安装后,会生成一个lock文件来判断是否安装,如果系统会自动删除这一个文件的话,就会导致系统重装漏洞产生。
-
无安装结果验证
这种情况就是不会删除.lock文件,也不会生成.lock安装文件。
-
安装检测可绕过
这种情况就是,在安装过程中,系统安装检测是一个单独的页面,检测完成后302跳转到安装的情况。 在这种情况下,我们就可以直接get请求安装页面来达到绕过检测的目的。
-
变量覆盖导致重装
可以使用GET或者POST等方式提交一个变量名$lockFile,并为其赋空值,覆盖掉$lockFile,从而让file_existe()判断结果为false,达到绕过安装文件检测的目的。
-
判断lock文件后,无exit
判断是否存在lock文件,如果lock文件存在则会跳转到某个页面,但是跳转后并没有exit检测程序。
-
存在解析漏洞
在安装完成后会将install.php重命名为install.php.bak,但是由于Apache的解析漏洞:如果无法识别到最后一个后缀的话,就会向上解析,那么就又变成了php了,然后结合安装时的变量覆盖又成重装了。
2、近期漏洞列表
- CNVD-2020-58513: ZZCMS 2020版系统重装漏洞
- CNVD-2020-33196:盾灵1.0系统重装漏洞
- CNVD-2020-31454:Heybbs微社区install.php文件重装漏洞
- CNVD-2020-02229:发货100系统重装漏洞
- CNVD-2019-43815:s-cms企业建站系统重装漏洞
- CNVD-2018-05690:hpyun人才系统4.5版本重装漏洞
二、ZZCMS 2020 重装漏洞分析与复现
1、漏洞分析
首先我们看到存在漏洞的代码:install/index.php。
在代码的第11行,有一个$step变量,该变量的值,是由客户端传入的。
6:include '../inc/config.php';
7:include 'conn.php';
8:if($_POST) extract($_POST, EXTR_SKIP);//把数组中的键名直接注册为了变量。就像把$_POST[ai]直接注册为了$ai。
9:if($_GET) extract($_GET, EXTR_SKIP);
10:$submit = isset($_POST['submit']) ? true : false;
11:$step = isset($_POST['step']) ? $_POST['step'] : 1;
在此处使用了一个三元操作符来进行判断,如果没有传入step参数的话,就会默认赋值为1。
也就是说,在初始访问index.php的时候,会默认赋值$step=1。
接下来我们再把视线放到第50行:
50:switch($step) {
51: case '1'://协议
52: include 'step_'.$step.'.php';
53: break;
54: case '2'://环境
55: $pass = true;
56: $PHP_VERSION = PHP_VERSION;
57: if(version_compare($PHP_VERSION, '4.3.0', '<')) {
58: $php_pass = $pass = false;
59: } else {
60: $php_pass = true;
61: }
在此处,使用了一个switch语句来判断$step,当$setp=1时,就会包含step_1.php文件,该文件的作用就是通过检测install.lock文件来判断我们的系统是否已经安装。其代码如下:
if(file_exists("install.lock")){
echo "<div style='padding:30px;'>安装向导已运行安装过,如需重安装,请删除 /install/install.lock 文件</div>";
}
而当step为2或其他是,就会进入系统安装步骤,所以,当我们抓包修改$step的值为2或者3之后,就会直接进入系统安装步骤,从而绕过了系统安装检测。
2、漏洞复现
如下图,是我们已经安装了的系统:
我们访问/install/index.php,然后使用burpsuite抓包,构造step=2,并修改传参方式为POST方式提交,成功绕过系统安装检测,进入系统安装步骤:
三、盾灵V.10系统重装漏洞
注: 盾灵系统包含了公众号推广系统、新闻发布系统、信息分享系统等,此次用作代码审计的是盾灵的公众号推广系统。
1、漏洞分析
首先我们先访问your-IP/install/index.php,效果如下,提供了两个检测步骤,一个是数据库连接检测,一个是文件写入权限检测。
然后我们把目光放到/install/index.php文件。里面主要是一些JS代码,使用了jqurey的ajax请求来发送数据到后端接口上。可以看到,在第36行到第42行,是使用了ajax请求发送数据库信息到mysql.php。
36: $.post("mysql.php",
37: {
38: mysqlip:mysqlip,
39: mysqlusername:mysqlusername,
40: mysqluserpass:mysqluserpass,
41: mysqldb:mysqldb
42: },
然后我们顺着思路,再看看mysql.php文件,发现该文件的作用就是检测我们的数据库能否正常连接。
<?php
error_reporting(0);
header("Content-type: text/html; charset=utf-8");
$ip = $_POST['mysqlip'];
$username = $_POST['mysqlusername'];
$userpass = $_POST['mysqluserpass'];
$db = $_POST['mysqldb'];
$ok1 = mysql_connect($ip,$username,$userpass)or die("数据库连接错误,请检查账号或密码是否有误!");
$ok2 = mysql_select_db($db)or die("无法找到数据库,请检查数据库名称是否填写正确");
if($ok1 or $ok2){
echo "ok";
}
?>
然后我们顺着思路,再来看看第二步的文件读写权限检测,在/install/index.php文件中,同样使用了ajax来请求后端接口,但是请求的文件是is_writable.php,并且出入了file参数为config.php.
$("#install_2").click(function(){
$.post("is_writable.php",{
file:'config.php'
}
然后发现if_writeable.php判断文件是否具有读写权限的方式就是通过is_writable()函数来判断config.php文件是否可写。
isset($_POST['file'])?$file = $_POST['file']:$file="config.php";
$is = is_writable($file);
当着两个检测项都检测成功后,就会弹出立即安装系统的选项。
而该选型的执行结果就是通过ajax发送连接数据库相关的参数到后端“steup.php文件”。
$.post("setup.php",
{
mysqlip:mysqlip,
mysqlusername:mysqlusername,
mysqluserpass:mysqluserpass,
mysqldb:mysqldb
},
在setup.php中,先是获取了连接数据库的各个参数,然后再次检测了数据库是否可以连接。如果数据库连接检测结果为OK,则会创建config.php文件。并且插入管理员的账号和密码,这里的密码是通过MD5加密了的,但是在安装成功后,会提示admin账户的密码是英文键盘第一行,所以也算是一个密码硬编码漏洞。
而在第29行,我们看到,它将/install目录下的index.php文件重命名为了index.lock文件。
16:if($ok){
17:$mysqlconfig = $ip.'@dunling@'.$username.'@dunling@'.$userpass.'@dunling@'.$db;
18:$config_t = file_get_contents("mysql_config.dll");
19:$config_a = array("@server@","@mysqlname@","@mysqlpass@","@mysqldb@");
20:$config_b = array($ip,$username,$userpass,$db);:
21:$mysqlconfig = str_replace($config_a,$config_b,$config_t);
22:$fileok = file_put_contents("../config.php",$mysqlconfig);:
23:if($fileok){
24: echo "installok";
25:$adminsql = "INSERT INTO `dunling_admin` VALUES ('1', 'admin', '90b9aa7e25f80cf4f64e990b78a9fc5ebd6cecad')";
26:$configsql = "INSERT INTO `dunling_config` VALUES ('1', '盾灵微信加粉联盟系统', 'www.dunling.com', '1.00', '5000.00', '2.00', '10', '0.20', '0.00', '0','25')";
27:mysql_query($configsql,$ok1);
28:mysql_query($adminsql,$ok1);
29:rename("index.php","index.lock");:
30:}else{
31: echo "installno";
32:}
但是通过前面的分析,我们知道,安装系统实际上是在setup.php文件中进行的,但是该文件并没有被删除或者重命名,也就是说,如果我们直接构造数据库连接参数请求setup.php的话,就会绕过前面的数据库检测和文件权限检测,直接重新安装系统。
但是有一个值得注意的地方就是,在我们构造数据库连接参数的时候,相关参数是并不知道的,所以只能依靠社工或者其他的一些手段来获取,这在利用难度上还是相当大的。
2、漏洞复现
注: 杠刚刚提到,正常情况我们是不知道数据库连接参数的,但是这里我们模拟已经获取了相关参数进行实验。
首先我们看到,已经安装好的页面是下面这个样子的:
然后我们请求/install/setup.php文件,并使用burpsuite进行抓包,修改请求方式为POST,并设置传输数据为数据库相关参数,发送请求,可以看到安装成功的提示。
最后总结一下,作为目前被发现的时间上相对较近的两个系统重装漏洞,可见他们的漏洞逻辑都是由于安装和检测在不同的页面导致的安装检测可绕过的情形。可见这确实是系统安装检测中常见的问题,对于像我这种新入门的想要挖掘此类漏洞的可以多试试这种思路。