版本号:3.5.1

下载地址:http://103.45.101.75:66/2/201412/74cms.rar

1.审计方法

通读审计

1.1查看文件结构

首先需要看看有哪些文件和文件夹,寻找名称里有没有带有api、admin、manage、include一类关键字的文件和文件夹,通常这些文件比较重要,在这个程序里,可以看到并没有什么PHP文件,就一个index.php,看到有一个名为include的文件夹,一般比较核心的文件都会放在这个文件夹中,我们先来看看大概有哪些文件

 
image.png

1.2 查看关键文件
在include里面,common.fun.php就是本程序的核心,大多数功能都在这里实现。

我们来看一下里面都有哪些关键函数

一开始就看到SQL注入过滤函数

function addslashes_deep($value)
{
if (empty($value))
{
return $value;
}
else
{
if (!get_magic_quotes_gpc())
{
$value=is_array($value) ? array_map('addslashes_deep', $value) : mystrip_tags(addslashes($value));
}
else
{
$value=is_array($value) ? array_map('addslashes_deep', $value) : mystrip_tags($value);
}
return $value;
}
}

该函数将传人的变量使用addslashes()函数进行过滤,也就过滤掉了单引号、双引号、NULL字符以及斜杠,现在我们要记住,在挖掘SQL注入等漏洞时,只要参数在拼接到SQL语句前,除非有宽字节注入或者其他特殊情况,否则使用了这个函数就不能注入了。

再往下走是一个XSS过滤的函数mystrip_tags

function mystrip_tags($string)
{
$string = new_html_special_chars($string);
$string = remove_xss($string);
return $string;
}

下面调用了new_html_special_chars和remove_xss函数去处理。

在new_html_special_chars()函数中可以看到,这个函数对&符号、双引号以及尖括号进行了html实体编码,并且使用striptags()函数进行了二次过滤。而remove_xss()函数则是对一些标签关键字、事件关键字以及敏感函数关键字进行了替换。

function new_html_special_chars($string) {

   $string = str_replace(array('&amp;', '&quot;', '<', '>'), array('&', '"', '<', '>'), $string);

   $string = strip_tags($string);

   return $string;

}

function remove_xss($string) {

   $string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $string);

   $parm1 = Array('javascript', 'union','vbscript', 'expression', 'applet', 'xml', 'blink', 'link', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');

   $parm2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload','style','href','action','location','background','src','poster');

   $parm3 = Array('alert','sleep','load_file','confirm','prompt','benchmark','select','update','insert','delete','alter','drop','truncate','script','eval');

   $parm = array_merge($parm1, $parm2, $parm3);

   for ($i = 0; $i < sizeof($parm); $i++) {

       $pattern = '/';

       for ($j = 0; $j < strlen($parm[$i]); $j++) {

           if ($j > 0) {

               $pattern .= '(';

               $pattern .= '(&#[x|X]0([9][a][b]);?)?';

               $pattern .= '|($#0([9][10][13]);?)?';

               $pattern .= ')?';

           }

           $pattern .= $parm[$i][$j];

       }

       $pattern .= '/i';

       $string = preg_replace($pattern, '****', $string);

   }

   return $string;

}

再往下就有获取IP的函数,此处可以伪造IP。其它程序在获取IP时没有验证IP格式,也可能利用获取IP进行注入。

function getip()
{
if (getenv('HTTP_CLIENT_IP') and strcasecmp(getenv('HTTP_CLIENT_IP'),'unknown')) {
$onlineip=getenv('HTTP_CLIENT_IP');
}elseif (getenv('HTTP_X_FORWARDED_FOR') and strcasecmp(getenv('HTTP_X_FORWARDED_FOR'),'unknown')) {
$onlineip=getenv('HTTP_X_FORWARDED_FOR');
}elseif (getenv('REMOTE_ADDR') and strcasecmp(getenv('REMOTE_ADDR'),'unknown')) {
$onlineip=getenv('REMOTE_ADDR');
}elseif (isset($_SERVER['REMOTE_ADDR']) and $_SERVER['REMOTE_ADDR'] and strcasecmp($_SERVER['REMOTE_ADDR'],'unknown')) {
$onlineip=$_SERVER['REMOTE_ADDR'];
}
preg_match("/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/",$onlineip,$match);
return $onlineip = $match[0] ? $match[0] : 'unknown';
}

下面是inserttable和updatetable函数,这里有大量的SQL语句进行查询,主要看有没有过滤问题。

function inserttable($tablename, $insertsqlarr, $returnid=0, $replace = false, $silent=0)

function updatetable($tablename, $setsqlarr, $wheresqlarr, $silent=0)

再往下走则是wheresql()函数,是SQL语句查询的Where条件拼接的地方,我们可以看到参数都使用了单引号进行包裹。

function wheresql($wherearr='')

{

   $wheresql="";

   if (is_array($wherearr))

       {

       $where_set=' WHERE ';

           foreach ($wherearr as $key => $value)

           {

           $wheresql .=$where_set. $comma.$key.'="'.$value.'"';

           $comma = ' AND ';

           $where_set=' ';

           }

       }

   return $wheresql;

}

还有一个访问令牌生成的函数asyn_userkey(),拼接用户名、密码salt以及密码进行一次md5,访问的时候只要在GET参数key的值里面加上生成的这个key即可验证是否有权限,被用在注册、找回密码等验证过程中,也就是我们能看到的找回密码链接里面的key。

function asyn_userkey($uid)

{

   global $db;

   $sql = "select * from ".table('members')." where uid = '".intval($uid)."' LIMIT 1";

   $user=$db->getone($sql);

   return md5($user['username'].$user['pwd_hash'].$user['password']);

}

同目录下是具体功能的文件,可以先不看

 
image.png

1.3 查看配置文件

查找目录下的config文件

 
image.png

发现/data下的cache_config和config才是配置文件。

<?php

$dbhost   = "localhost";

$dbname   = "74cms";

$dbuser   = "root";

$dbpass   = "root";

$pre    = "qs_";

$QS_cookiedomain = '';

$QS_cookiepath =  "/74cms/";

$QS_pwdhash = "H@g24Q6xa:AewjJD";

define('QISHI_CHARSET','gb2312');

define('QISHI_DBCHARSET','GBK');

?>

可以看到是,QISHI_DBCHARSET常量是GBK编码的,因此上面和数据相关的双引号解析代码处,可能存在宽字节注入。不过需要看数据库连接时设置的编码

接着找数据库连接文件/include/mysql.class.php中的connect函数

function connect($dbhost, $dbuser, $dbpw, $dbname = '', $dbcharset = 'gbk', $connect=1){
$func = empty($connect) ? 'mysql_pconnect' : 'mysql_connect';
if(!$this->linkid = @$func($dbhost, $dbuser, $dbpw, true)){
$this->dbshow('Can not connect to Mysql!');
} else {
if($this->dbversion() > '4.1'){
mysql_query( "SET NAMES gbk");
if($this->dbversion() > '5.0.1'){
mysql_query("SET sql_mode = ''",$this->linkid);
mysql_query("SET character_set_connection=".$dbcharset.", character_set_results=".$dbcharset.", character_set_client=binary", $this->linkid);
}
}
}
if($dbname){
if(mysql_select_db($dbname, $this->linkid)===false){
$this->dbshow("Can't select MySQL database($dbname)!");
}
}
}

也就是当MySQL版本大于4.1时执行“set names gbk”,但当小于5.0.1时,下面不执行。

只执行set names gbk。

但set names gbk 等价于
设置客户端的编码

set character_set_client=gbk
设置连接器编码
set character_set_connection=gbk
设置返回值编码
set character_set_results=gbk
client(客户端)、connection(连接器)、results(返回值)

所以,在MySQL4.1-5.0.1之间都存在宽字节注入。

1.4 阅读首页文件

通过对系统文件大概的了解,我们对这套程序的整体架构已经有了一定的了解,但是还不够,所以我们得跟读一下index.php文件,看看程序运行的时候会调用哪些文件和函数。

if(!file_exists(dirname(__FILE__).'/data/install.lock')) header("Location:install/index.php");

define('IN_QISHI', true);

$alias="QS_index";

require_once(dirname(__FILE__).'/include/common.inc.php');

跟进到common.inc.php查看

require_once(QISHI_ROOT_PATH.'data/config.php');

header("Content-Type:text/html;charset=".QISHI_CHARSET);

require_once(QISHI_ROOT_PATH.'include/common.fun.php');

require_once(QISHI_ROOT_PATH.'include/74cms_version.php');

可以看到引用了config.php为配置文件,common.fun.php是核心功能文件,74cms_version.php是版本文件

继续往下,可以看到对传输的数据进行过滤

if (!empty($_GET))

{

$_GET  = addslashes_deep($_GET);

}

if (!empty($_POST))

{

$_POST = addslashes_deep($_POST);

}

$_COOKIE   = addslashes_deep($_COOKIE);

$_REQUEST  = addslashes_deep($_REQUEST);

再往下看到一个包含文件的操作

require_once(QISHI_ROOT_PATH.'include/tpl.inc.php');

跟进到tpl.inc.php文件

include_once(QISHI_ROOT_PATH.'include/template_lite/class.template.php');

$smarty = new Template_Lite;

$smarty -> cache_dir = QISHI_ROOT_PATH.'temp/caches/'.$_CFG['template_dir'];

$smarty -> compile_dir =  QISHI_ROOT_PATH.'temp/templates_c/'.$_CFG['template_dir'];

$smarty -> template_dir = QISHI_ROOT_PATH.'templates/'.$_CFG['template_dir'];

$smarty -> reserved_template_varname = "smarty";

$smarty -> left_delimiter = "{#";

$smarty -> right_delimiter = "#}";

$smarty -> force_compile = false;

$smarty -> assign('_PLUG', $_PLUG);

$smarty -> assign('QISHI', $_CFG);

$smarty -> assign('page_select',$page_select);

可以看到进行了一个模版文件映射,并对smarty进行赋予属性。

现在回到index.php文件

if(!$smarty->is_cached($mypage['tpl'],$cached_id))

{

require_once(QISHI_ROOT_PATH.'include/mysql.class.php');

$db = new mysql($dbhost,$dbuser,$dbpass,$dbname);

unset($dbhost,$dbuser,$dbpass,$dbname);

$smarty->display($mypage['tpl'],$cached_id);

}

else

{

$smarty->display($mypage['tpl'],$cached_id);

}

判断是否存在缓存,然后用display将页面展示。

到这里发现了特定数据库版本存在宽字节注入,并对骑士CMS的代码有了更多了解。

 

骑士cms-通读全文-代码审计的更多相关文章

  1. 怎么修复网站漏洞 骑士cms的漏洞修复方案

    骑士CMS是国内公司开发的一套开源人才网站系统,使用PHP语言开发以及mysql数据库的架构,2019年1月份被某安全组织检测出漏洞,目前最新版本4.2存在高危网站漏洞,通杀SQL注入漏洞,利用该网站 ...

  2. 骑士CMS<6.0.48 模板注入文件包含漏洞复现及遇到的坑

    1.坑 payload:variable=1&tpl=<?php phpinfo(); ob_flush();?>/r/n<qscms/company_show 列表名=&q ...

  3. 骑士cms(74cms)个人版 整合UC

    1.安装74cms完成后登录总后台在菜单条工具选项中添加uc整合菜单. 在admin/templates/sys/admin_left_tools.htm这个文件中添加 <li >< ...

  4. [CMS漏洞]EmpireCMS_V7.5的一次审计【转载】

    i春秋作家:Qclover 原文来自:EmpireCMS_V7.5的一次审计 0x01 概述 最近在做审计和WAF规则的编写,在CNVD和CNNVD等漏洞平台寻找各类CMS漏洞研究编写规则时顺便抽空对 ...

  5. php代码审计一些笔记

    之前学习了seay法师的代码审计与及80sec的高级审计,整理了一些笔记在印象里面,也发到这里作为记录 1,漏洞挖掘与防范(基础篇) sql注入漏洞            挖掘经验:注意点:登录页面, ...

  6. PHP代码审计4-漏洞挖掘思路

    漏洞挖掘思路 漏洞形成的条件 1.变量可控制 2.变量可到达有利用价值的函数(危险函数) 漏洞造成的效果 漏洞的利用效果取决于最终的函数功能,变量进入什么样的函数就导致什么样的效果 危险函数 文件包含 ...

  7. PHP代码审计(初级篇)

    一.常见的PHP框架 1.zendframwork: (ZF)是Zend公司推出的一套PHP开发框架 功能非常的强大,是一个重量级的框架,ZF 用 100%面向对象编码实现. ZF 的组件结构独一无二 ...

  8. PHP代码审计入门(敏感函数回溯参数过程)

    最近开始啃<代码审计企业级web代码安全架构>这本书,这一章内容看了2天很多内容都理解最主要的是对PHP不熟练所以现在理解了大概 然后进行实地环境搭建最主要的是源码百度真不好找 最后找到一 ...

  9. espcms代码审计第一弹

    以前的代码审计都是在CTF比赛题里面进行对于某一段代码的审计,对于后端php整体代码和后端整体架构了解的却很少,所以有空我都会学习php的代码审计,以提高自己 环境就直接用的是phpstudy,学习的 ...

随机推荐

  1. Centos 7 使用Securecrt 配置Public key 登录

    环境:Centos 7 SecureCRT 版本:8.0.4 需求:配置使用Public key 登录服务器禁用密码登录 1. 配置使用SecureCRT,生成Public key 跟私钥 2. 配置 ...

  2. Python爬虫-scrapyd

    1.什么是scrapyd Scrapyd是一个服务,用来运行scrapy爬虫的. 它允许你部署你的scrapy项目以及通过HTTP JSON的方式控制你的爬虫. 官方文档:http://scrapyd ...

  3. go语言指南之切片练习

    题目: 实现 Pic.它应当返回一个长度为 dy 的切片,其中每个元素是一个长度为 dx,元素类型为 uint8 的切片.当你运行此程序时,它会将每个整数解释为灰度值(好吧,其实是蓝度值)并显示它所对 ...

  4. Typecho 主题制作记录

    模板制作快速入门 模板的制作并非难事,只要你写好了HTML和CSS,嵌套模板就非常简单了,你无需了解标签的内部结构,你只要会使用,模板就能迅速完成.这篇文章只简单的介绍了常用标签的使用方法,希望能带你 ...

  5. Eureka在有虚拟网卡的情况下获取正确的IP

    发现问题 最近项目在Eureka注册时,发现一个问题:注册的IP地址不是 192.168.0.XXX 的网络IP,而是另外一个网段的地址,如图 通过 ipconfig 命令查看本机的IP地址发现,该I ...

  6. proteus pro 8.9 安装及汉化教程

    最近由于网上上课老师要求我们自己安装proteus这款仿真软件,所以笔者也安装了最新款版的proteus pro 8.9,分享给大家安装心得,也包含汉化过程,希望大家能用软件好好学习. 备注:感谢博主 ...

  7. 小巧开源的 baresip VOIP 项目

    Baresip is a modular SIP User-Agent with audio and video support https://github.com/alfredh/baresip ...

  8. 【音视频连载-001】基础学习篇- SDL 介绍以及工程配置

    技术开发故事会连载 这是音视频基础学习系列的第一篇文章,主要讲解 SDL 是什么以及为什么要用到它,看似和音视频没啥卵关系,其实必不可少. SDL 简介 SDL 是 "Simple Dire ...

  9. 用libvlc 抓取解码后的帧数据

    vlc是一套优秀的开源媒体库,其特点是提供了完整的流媒体框架, 用它可以非常方便的实现抓取解码帧的功能. 与此功能有关的关键API为 libvlc_video_set_callbacks /*设置回调 ...

  10. (转)SpringBoot :(has no explicit mapping for /error)

    转载自:https://www.cnblogs.com/panchanggui/p/9945261.html 异常:This application has no explicit mapping f ...