前言

前一篇博客结语中立下的Flag,找了一套内容管理系统ZZCMS8.1。从简单的CMS下手吧。

ZZCMS8.1

后台/admin/adclass.php注入

在路径/admin/adclass.php有如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$sql="Select * from zzcms_adclass where classid=" .$classid."";
$rs=query($sql);
$row=fetch_array($rs);
?>
<div class="admintitle">修改大类</div>
<form name="form1" method="post" action="?dowhat=modifybigclass" onSubmit="return CheckForm();">
<table width="100%" border="0" cellpadding="5" cellspacing="0">
<tr>
<td width="322" align="right" class="border">大类ID:</td>
<td class="border"><?php echo $row["classid"]?> <input name="classid" type="hidden" id="classid" value="<?php echo $row["classid"]?>"> </td>
</tr>
<tr>
<td align="right" class="border">大类名称:</td>
<td class="border"> <input name="classname" type="text" id="classname" value="<?php echo $row["classname"]?>" size="60" maxlength="30">
<input name="oldclassname" type="hidden" id="oldclassname" value="<?php echo $row["classname"]?>" size="60" maxlength="30"></td>
</tr>

<tr>
<td class="border">&nbsp;</td>
<td class="border"> <input name="action" type="hidden" id="action" value="modify">
<input name="save" type="submit" id="save" value=" 修 改 "> </td>
</tr>
</table>

以上代码中的$sql="Select * from zzcms_adclass where classid=" .$classid."";看似是能进行联合查询注入的。找到$classid变量的来源。

1
2
3
4
5
if (isset($_REQUEST['classid'])){
$classid=trim($_REQUEST['classid']);
}else{
$classid="";
}

$classid仅用trim()函数移除字符串两侧的空白字符 ,虽然对$_GET$_POST$REUQEST的变量进行的单引号转义操作,但是这里是不需要逃逸单引号,直接使用联合查询注入。

测试利用常见的union型注入手注。

查询结果直接用echo显示到html

PHPstorm下断点进行调试

后台/admin/about.php注入

INSERT 注入

在路径/admin/about.php如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if ($action=="savedata" ){
checkadminisdo("bottomlink");
$saveas=trim($_REQUEST["saveas"]);
$title=trim($_POST["title"]);
$content=stripfxg(rtrim($_POST["info_content"]));
$link=trim($_POST["link"]);
if ($saveas=="add"){
query("insert into zzcms_about (title,content)VALUES('$title','$content') ");
$go=1;
//echo "<script>location.href='about_manage.php'<//script>";
}elseif ($saveas=="modify"){
query("update zzcms_about set title='$title',content='$content',link='$link' where id=". $_POST['id']." ");
$go=1;
//echo "<script>location.href='about_manage.php'<//script>";
}
}

这段代码中有两处SQL语句操作,query("insert into zzcms_about (title,content)VALUES('$title','$content') "); 这里是用的insert语句,和我上次看的那套bluecms的注入是相似的,主要是闭合构造payload

$title$content做了一定的处理,$titletrim()函数移除字符串两侧的空白字符,$content用到了一个stripfxg()函数。找到这个函数的代码

1
2
3
4
5
function stripfxg($string) {//去反斜杠 
$string=stripslashes($string);//去反斜杠,不开get_magic_quotes_gpc 的情况下,在stopsqlin中都加上了,这里要去了
$string=htmlspecialchars_decode($string);//转html实体符号
return $string;
}

根据函数的注释,该函数的功能是用来去除反斜杠和转html实体符号的。

这里不能利用到$title来进行插入payload,因为全局的单引号转义,但是可以利用$content这个变量,它传入值的单引号被加斜杠转移了,但是有被stripfxg()函数把单引号去除了。所以可以利用这个变量插入payload

以以上方式插入payloadPHPStrom跟进调试。$content的内容先转义单引号后再被函数除去反斜杠导致可以利用到

之后传入上面代码说到的insert操作语句query("insert into zzcms_about (title,content)VALUES('$title','$content') ");

构造成如下语句:

1
insert into zzcms_about (title,content)VALUES('$title','1),((select version()),'1')

注入成功。

union注入

同样在路径/admin/about.php如下代码:

1
2
3
4
5
if ($action=="modify") {
$sql="select * from zzcms_about where id=".$_REQUEST["id"]."";
$rs=query($sql);
$row=fetch_array($rs);
?>

$sql="select * from zzcms_about where id=".$_REQUEST["id"]."";这个SQL操作语句也没有做任何过滤,可以直接进行联合查询注入。注入结果输出到html显示。

成功注入。

后台/admin/help_manage.php注入

union注入

在路径/admin/help_manage.php文件中有如下代码:

1
2
3
4
5
6
7
8
9
10
$sql="select * from zzcms_help where classid=".$b." ";
if ($keyword<>"") {
$sql=$sql." and title like '%".$keyword."%' ";
}
$rs = query($sql,$conn);
$totlenum= num_rows($rs);
$totlepage=ceil($totlenum/$page_size);

$sql=$sql . " order by id desc limit $offset,$page_size";
$rs = query($sql,$conn);

$sql="select * from zzcms_help where classid=".$b." ";这个SQL语句操作可以利用,找到变量$b的来源。

1
$b=isset($_REQUEST["b"])?$_REQUEST["b"]:'';

变量没有经过处理,可以直接利用。只是一个简单冒号判断式。

$sql=$sql . " order by id desc limit $offset,$page_size";这个拼接可以利用#注释掉,然后通过while($row = fetch_array($rs))输出到html

返回注入结果

后台/admin/jobclassmanage.php注入

union注入

和前面找到的注入是一样的,代码如下:

1
2
3
$sql="Select * from zzcms_jobclass where classid=" .$classid."";
$rs=query($sql);
$row=fetch_array($rs);

/install/index.php重装漏洞

在路径/install/index.php中有如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
switch($step) {
case '1'://协议
include 'step_'.$step.'.php';
break;
case '2'://环境
$pass = true;
$PHP_VERSION = PHP_VERSION;
if(version_compare($PHP_VERSION, '4.3.0', '<')) {
$php_pass = $pass = false;
} else {
$php_pass = true;
}
$PHP_MYSQL = '';
if(extension_loaded('mysql')) {
$PHP_MYSQL = '支持';
$mysql_pass = true;
} else {
$PHP_MYSQL = '不支持';
$mysql_pass = $pass = false;
}
$PHP_GD = '';
if(function_exists('imagejpeg')) $PHP_GD .= 'jpg';
if(function_exists('imagegif')) $PHP_GD .= ' gif';
if(function_exists('imagepng')) $PHP_GD .= ' png';
if($PHP_GD) {
$gd_pass = true;
} else {
$gd_pass = false;
}
$PHP_URL = @get_cfg_var("allow_url_fopen");//是否支持远程URL,采集有用
$url_pass = $PHP_URL ? true : false;
include 'step_'.$step.'.php';
break;
case '3'://查目录属性
include 'step_'.$step.'.php';
break;
case '4'://建数据库
include 'step_'.$step.'.php';
break;
case '5'://安装进度
function dexit($msg) {
echo '<script>alert("'.$msg.'");window.history.back();</script>';
exit;
}

上面的代码是用了一个switch-case来进行一个安装的过程。回溯到step变量的来源

1
$step = isset($_POST['step']) ? $_POST['step'] : 1;

一个冒号运算符,这里可以用POST方式提交一个step值。

跟进switch-case语句的执行。case '1':进入\install\step_1.php中,在该文件中的开头有验证/install/install.lock 文件的过程。

1
2
3
4
5
<?php
if(file_exists("install.lock")){
echo "<div style='padding:30px;'>安装向导已运行安装过,如需重安装,请删除 /install/install.lock 文件</div>";
}else{
?>

继续跟进switch-case语句的执行。在\install\step_2.php中,但是在该文件中并没有验证/install/install.lock 文件的过程。而且在后续的case值里也没有验证。

所以可以直接提交POST数据step=2,跳过验证进行重装。

结语

这套源码还是相当于简单一点,前台的SQL注入都是做了过滤的。但是在后台的数字型的SQL注入都是没有做过滤的。同时后台也对单引号进行了转义操作,在文章提到的insert注入是因为CMS先对单引号进行的转义操作,后来又把反斜杠给除去了。所以可以利用insert注入。后台的注入相较于来还是比较鸡肋的。