关于SQL注入的监测

网站被挂马,有一个方式就是对某个帐号进行提权操作,比如针对非管理用户提权至最高管理员的权限。

事后可以发现被提权的用户帐号,但检查经由哪个文件注入的是个麻烦的事情,特别是针对开源的文件结构比较复杂的程序来说。

以前有个思路,一直未实现,今天处理了一个,是针对Discuz!5.5(很老的一个版本了^^)的,处理思路就是事后诸葛亮。

对于此类的提权操作,其根本原因就是通过SQL去变更待提权的帐号的管理标识,如果程序对于涉及变更帐号权限的SQL查询都能有一个记录的话,这样日后一旦发现有未知的提权操作的时候便会相对容易的发现注入口是什么,大体也能进行应对处理,总比盲目的去查找注入文件要容易一些,当然,这个其实只是“事后诸葛亮”的做法,并不是根本的防御措施,真正想要防御其实还是程序开发的时候多考虑一些,比如,外部的输入要验证数据类型、进行字符转义等等,这不在本文讨论范围。

就Discuz!而言,所有的SQL操作都是经由数据类层进行操作的,这样就提供了一个比较方便的方式,我们可以在数据查询方法中加入一个判断,判断当前进行的操作是否是在更新用户表的权限字段,如果是,则记录下来相关的信息并保存到日志文件内,方便日后查询处理,针对Discuz!5.5我是按照如下的方式处理的,其实其他版本的Discuz!或者Discuz!X乃至其他类似的程序都可以利用这样的方式进行。

针对Discuz!5.5代码如下:

<?php
/*
@ By Deepseath 2011-09-26
@ 记录用户管理权限发生变动时的相关信息
*/
if ( preg_match('/UPDATE\s+[`]*'.$GLOBALS['tablepre'].'members[`]*\s+SET(.+)/is',$sql,$match) &amp;&amp; preg_match('/[`]*\s*adminid[`]*\s*=\s*/is',$match[1],$m) ) {
	//判断当前执行语句是否为members表包含adminid的更新
	$sql_md5	=	md5($sql);//唯一查询md5值
	$logfile	=	DISCUZ_ROOT.'./forumdata/log_adminid/'.$sql_md5.'.log';//日志储存的文件
	if ( !file_exists($logfile) || time() - filemtime($logfile) &gt; 1800 ) {
		//如果查询日志文件不存在或者日志文件距离当前时间超过1800秒,则记录查询日志
		if ( !is_dir(DISCUZ_ROOT.'./forumdata/log_adminid/') ) {
			//日志目录不存在则创建
			@mkdir(DISCUZ_ROOT.'./forumdata/log_adminid/',0777,true);
		}
		if ( $fp = @fopen($logfile,'ab') ) {
			//写入日志内容
			$content	=	"&lt;!--### LOG START ########################################\r\n";
			$content	.=	$sql."\r\n";
			$content	.=	"\t::@DEEPSEATH_LOG@::time::".gmdate('Y-m-d H:i:s',time() + ($GLOBALS['timeoffset'] * 3600));
			$content	.=	"\t::@DEEPSEATH_LOG@::user::".$GLOBALS['_DSESSION']['discuz_user'];
			$content	.=	"\t::@DEEPSEATH_LOG@::ip::".$GLOBALS['onlineip'];
			$content	.=	"\t::@DEEPSEATH_LOG@::url::".'http'.(strtolower($_SERVER['HTTPS']) == 'on' ? 's' : '').'://'.$_SERVER['HTTP_HOST'].($_SERVER['SERVER_PORT'] != '80' ? ':'.$_SERVER['SERVER_PORT'] : '').$_SERVER["REQUEST_URI"];
			$content	.=	"\t::@DEEPSEATH_LOG@::referer::".(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '[none]');
			$content	.=	"\r\n######################################## LOG End ###--&gt;\r\n\r\n\r\n";
			flock($fp,LOCK_EX);
			fwrite($fp, $content);
			flock($fp, LOCK_UN);
			fclose($fp);
			unset($content);
		}
	}
	unset($sql_md5,$logfile);
}
/*~~~~~~~~~~~~~~~~~~结束修改~~*/
?>

将上面的代码加入到db_mysql.class.php中的query方法内,在输入查询前就行。

这样,当有涉及到 members 表的adminid进行更新时便会记录到日志内,每个查询都是唯一的日志文件,储存于/forumdata/log_adminid/内,日志记录了,操作的SQL语句、操作的时间、操作的用户名、操作的ip地址、操作的url以及来路url($_SERVER[''HTTP_REFERER])

另外,如果有必要也可以加入insert更新的记录。不过一般而言insert更新的注入相对比较少一些。

标签:Discuz, MySQL, PHP, 代码, 思路

添加一条评论 »本文共 3 条评论

  1. 登录时,360显示安全证书有问题,是什么原因?难道是应该uc整合的那一个站不是同一个服务器的关系?求解~

    • 呵呵,不是很清楚你的问题描述*_*,是哪个站点?可以给我发mail我帮你看看?
      UC跨站的话一般也不会出现安全证书的问题的,除非涉及到https和http之间的交互,一般IE会提示有站点信任的问题。这样的情况也不是UC引起的是由浏览器检测发出的。

  2. 不错,受用,收藏了,哈哈

Deepseath Modified from Green Hope Theme · Proudly powered by WordPress · 津ICP备09005418号-1  津公网安备 12010302001005号