骑⼠cms-通读全⽂-代码审计
版本号:3.5.1
下载地址:
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('&', '"', '<', '>'), 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', 'laye r', 'bgsound', 'title', 'base');
$parm2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbefore editfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmen u', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ond ragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'o nhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmouse move', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystat echange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselection change', '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','alt
er','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'];
}
php是文件什么
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$TABLENAME$INSERTSQLARR$RETURNID$REPLACE FALSE$SILENT
)
FUNCTION$TABLENAME$SETSQLARR$WHERESQLARR$SILENT
再往下⾛则是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__HEADER)
;
DEFINE TRUE);
$ALIAS;
REQUIRE_ONCE DIRNAME__FILE__
跟进到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);
再往下看到⼀个包含⽂件的操作

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。