前言 前一篇博客结语中立下的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"> </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
做了一定的处理,$title
用trim()
函数移除字符串两侧的空白字符,$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
。
以以上方式插入payload
,PHPStrom
跟进调试。$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
注入。后台的注入相较于来还是比较鸡肋的。