前一篇博客结语中立下的Flag,找了一套内容管理系统ZZCMS8.1。从简单的CMS下手吧。
ZZCMS8.1 后台/admin/adclass.php注入在路径 /admin/adclass.php 有如下代码:
$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"> </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 变量的来源。
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 如下代码:
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 做了一定的处理, $title 用 trim() 函数移除字符串两侧的空白字符, $content 用到了一个 stripfxg() 函数。找到这个函数的代码
function stripfxg($string) {//去反斜杠 $string=stripslashes($string);//去反斜杠,不开get_magic_quotes_gpc 的情况下,在stopsqlin中都加上了,这里要去了 $string=htmlspecialchars_decode($string);//转html实体符号 return $string; }
根据函数的注释,该函数的功能是用来去除反斜杠和转html实体符号的。
这里不能利用到 $title 来进行插入 payload ,因为全局的单引号转义,但是可以利用 $content 这个变量,它传入值的单引号被加斜杠转移了,但是有被 stripfxg() 函数把单引号去除了。所以可以利用这个变量插入 payload 。
以以上方式插入 payload , PHPStrom 跟进调试。 $content 的内容先转义单引号后再被函数除去反斜杠导致可以利用到
之后传入上面代码说到的 insert 操作语句 query("insert into zzcms_about (title,content)VALUES('$title','$content') "); 。
构造成如下语句:
insert into zzcms_about (title,content)VALUES('$title','1),((select version()),'1')
注入成功。
union注入
同样在路径 /admin/about.php 如下代码:
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 文件中有如下代码:
$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 的来源。
$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注入
和前面找到的注入是一样的,代码如下:
$sql="Select * from zzcms_jobclass where classid=" .$classid.""; $rs=query($sql); $row=fetch_array($rs);
/install/index.php重装漏洞
在路径 /install/index.php 中有如下代码:
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 变量的来源
$step = isset($_POST['step']) ? $_POST['step'] : 1;一个冒号运算符,这里可以用 POST 方式提交一个 step 值。
跟进 switch-case 语句的执行。 case '1': 进入 \install\step_1.php 中,在该文件中的开头有验证 /install/install.lock 文件 的过程。
<?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 注入。后台的注入相较于来还是比较鸡肋的。