一聚教程网:一个值得你收藏的教程网站

热门教程

discuz防SQL注入程序代码

时间:2022-06-25 16:32:57 编辑:袖梨 来源:一聚教程网

discuz 是一套通用的 PHP 社区论坛软件系统,在国内占有大量的用户群体,是做论坛的首选系统,在很早的时候就用 discuz 做论坛系统,感触最深的应该就是论坛的安全问题了,以前使用的时候,经常会有大量的信息进行注入,真是防不胜防。

好在 discuz 系统的更新很迅速,每一次的安全问题很快就得到了更新,而最头疼的问题恐怕就是 sql 的注入了,其实不只 discuz 系统,互联网上进行网络攻击的 SQL 注入是很可怕的,虽然 discuz 系统现有的安全问题已经非常完善了,但因为系统功能的强大,还是要注意安全问题。

下面是一段 discuz防sql注入的代码,利用了php提供的引用数据的魔法引用函数 magic_quotes_gpc,这个函数是在我们引用的数据碰到单引号和双引号以及反斜线时自动加上反斜线,帮我们自动转译符号,确保数据操作的正确运行,下面的代码可以细细研究。

 代码如下 复制代码

$magic_quotes_gpc = get_magic_quotes_gpc();
@extract(daddslashes($_COOKIE));
@extract(daddslashes($_POST));
@extract(daddslashes($_GET));
if(!$magic_quotes_gpc) {
$_FILES = daddslashes($_FILES);
}

function daddslashes($string, $force = 0) {
if(!$GLOBALS['magic_quotes_gpc'] || $force) {
if(is_array($string)) {
foreach($string as $key => $val) {
$string[$key] = daddslashes($val, $force);
}
} else {
$string = addslashes($string);
}
}
return $string;
}

上面的方法真的是万能的可以防sql注入了吗,最近又专家发现discuz一些问题。

Discuz防注入分析如下:

  先看防注入配置:

 代码如下 复制代码

$_config['security']['querysafe']['status']    = 1;        // 是否开启SQL安全检测,可自动预防SQL注入攻击
$_config['security']['querysafe']['dfunction']    = array('load_file','hex','substring','if','ord','char');
$_config['security']['querysafe']['daction']    = array('intooutfile','intodumpfile','unionselect','(select', 'unionall', 'uniondistinct');
$_config['security']['querysafe']['dnote']    = array('/*','*/','#','--','"');
$_config['security']['querysafe']['dlikehex']    = 1;
$_config['security']['querysafe']['afullnote']    = 0;   

Discuz 执行SQL语句之前会调用{sourceclassdiscuzdiscuz_database.php} 文件discuz_database_safecheck类下面的checkquery($sql)函数进行过滤。但是过滤并不严谨,我们发现可以绕过改防注入函数。

 代码如下 复制代码

public static function checkquery($sql) {
        if (self::$config === null) {
            self::$config = getglobal('config/security/querysafe');
        }
        if (self::$config['status']) {
            $cmd = trim(strtoupper(substr($sql, 0, strpos($sql, ' '))));
            if (in_array($cmd, self::$checkcmd)) {
                $test = self::_do_query_safe($sql);
                if ($test < 1) {
                    throw new DbException('It is not safe to do this query', 0, $sql);
                }
            }
        }
        return true;
    }    private static function _do_query_safe($sql) {
        $sql = str_replace(array('\', ''', '"', ''''), '', $sql);
        $mark = $clean = '';
       
        if (strpos($sql, '/') === false && strpos($sql, '#') === false && strpos($sql, '-- ') === false) {
            $clean = preg_replace("/'(.+?)'/s", '', $sql);
        } else {
           
            $len = strlen($sql);
            $mark = $clean = '';
            for ($i = 0; $i < $len; $i++) {
                $str = $sql[$i];
                switch ($str) {
                    case ''':
                        if (!$mark) {
                            $mark = ''';
                            $clean .= $str;
                        } elseif ($mark == ''') {
                            $mark = '';
                        }
                        break;
                    case '/':
                        if (empty($mark) && $sql[$i + 1] == '*') {
                            $mark = '/*';
                            $clean .= $mark;
                            $i++;
                        } elseif ($mark == '/*' && $sql[$i - 1] == '*') {
                            $mark = '';
                            $clean .= '*';
                        }
                        break;
                    case '#':
                        if (empty($mark)) {
                            $mark = $str;
                            $clean .= $str;
                        }
                        break;
                    case "n":
                        if ($mark == '#' || $mark == '--') {
                            $mark = '';
                        }
                        break;
                    case '-':
                        if (empty($mark) && substr($sql, $i, 3) == '-- ') {
                            $mark = '-- ';
                            $clean .= $mark;
                        }
                        break;

                    default:

                        break;
                }
                $clean .= $mark ? '' : $str;
            }
        }

        $clean = preg_replace("/[^a-z0-9_-()#*/"]+/is", "", strtolower($clean));

        if (self::$config['afullnote']) {
            $clean = str_replace('/**/', '', $clean);
        }

        if (is_array(self::$config['dfunction'])) {
            foreach (self::$config['dfunction'] as $fun) {
                if (strpos($clean, $fun . '(') !== false)
                    return '-1';
            }
        }

        if (is_array(self::$config['daction'])) {
            foreach (self::$config['daction'] as $action) {
                if (strpos($clean, $action) !== false)
                    return '-3';
            }
        }

        if (self::$config['dlikehex'] && strpos($clean, 'like0x')) {
            return '-2';
        }

        if (is_array(self::$config['dnote'])) {
            foreach (self::$config['dnote'] as $note) {
                if (strpos($clean, $note) !== false)
                    return '-4';
            }
        }

        return 1;
    }
   该防注入函数的关键绕过代码在:

    if (strpos($sql, '/') === false && strpos($sql, '#') === false && strpos($sql, '-- ') === false) {
            $clean = preg_replace("/'(.+?)'/s", '', $sql);
}
else
{

在discuz v63积分商城插件注入漏洞exp中并不需要斜杠、#号和—注释符。所以会执行$clean = preg_replace("/'(.+?)'/s", '', $sql);

  原来SQL语句中两个单引号中间的内容就会被替换为空。并不会进入到下面的else分支。Else下面的所有操作均是对$clean变量的操作。所以绕过的思路就是把SQL语句放在两个单引号中间。对于mysql的一个特性,@`’` 是为空的,所以我们的攻击语句可以放到两个@`’`中间,即使GPC开启,单引号被转义为’,而@`’`变成@`’`对注入也是没有影响的,所以此绕过方法无限制。

即针对该注入漏洞的攻击EXP为:

 代码如下 复制代码

http://localhost/discuz/plugin.php?id=v63shop:goods&pac=info&gid=110  or @`'` and (select * from (select count(*),concat(floor(rand(0)*2),(select user()))a from information_schema.tables group by a)b) or @`'`  

调试输出SQL语句:

详解discuz防注入研究报告:SQL注入漏洞


可以看到我们的注入语句被替换掉了,所以后门的检查字符的时候并没有发现注入语句。最终成功利用:

详解discuz防注入研究报告:SQL注入漏洞

上面的办法我自己写了一个函数

 代码如下 复制代码

/*
函数名称:inject_check()
函数作用:检测提交的值是不是含有SQL注射的字符,防止注射,保护服务器安全
参  数:$sql_str: 提交的变量
返 回 值:返回检测结果,ture or false
*/
function inject_check($sql_str) {
return eregi('select|insert|and|or|update|delete|'|/*|*|../|./|union|into|load_file|outfile', $sql_str); // 进行过滤
}

/*
函数名称:verify_id()
函数作用:校验提交的ID类值是否合法
参  数:$id: 提交的ID值
返 回 值:返回处理后的ID
*/
function verify_id($id=null) {
if (!$id) { exit('没有提交参数!'); } // 是否为空判断
elseif (inject_check($id)) { exit('提交的参数非法!'); } // 注射判断
elseif (!is_numeric($id)) { exit('提交的参数非法!'); } // 数字判断
$id = intval($id); // 整型化

return $id;
}

热门栏目