沉舟侧畔千帆过,病树前头万木春。
什么是宽字节注入?
当后端对用户的输入做了转义时,比如利用addslashes()或mysql_escape_string()函数。此时输入id=1’其中的单引号会被转义而变成id=1\'
,这样就使得单引号无法闭合数据库中的语句,一般情况下是很难绕过的。但是有两种情况可以通过宽字节注入绕过。
第一中情况是当数据库的编码为GBK时,注入格式为id=1%df'
,因为反斜杠\
的编码为%5c,所以参数传到后端经过转义后就会变成id=1%df%5c'
,其中GBK会将%df%5c编码成一个繁体字,这样反斜杠就失去了转义的效果,使得单引号逃逸出来闭合了语句。第二种情况是,若后端的代码中有转换字符编码的函数,道理其实一样,就是将用户输入的参数用宽字节去解释了,从而使单引号逃逸。
靶场练习
1、pikachu之宽字节注入
- 测试流程
输入%df' or 1=1#
直接遍历出数据库内容:
order by测出主查询字段数:
order by 2回显正常 | order by 3出现报错 |
---|---|
说明主查询字段数为2,接下来直接union注入:
测出回显点为1,2
查询数据库信息:
如图所示,查询出数据库名为pikachu,用户为root,数据库版本为5.5.53。
查询表名:
查询字段名:
查询字段值:
整个过程的关键点在于单引号前面的%df,造成宽字节注入使得单引号逃逸处理闭合语句,才有了后面的操作。
-
源码分析
核心代码:
if(isset($_POST['submit']) && $_POST['name']!=null){ $name = escape($link,$_POST['name']); $query="select id,email from member where username='$name'";//这里的变量是字符型,需要考虑闭合 //设置mysql客户端来源编码是gbk,这个设置导致出现宽字节注入问题 $set = "set character_set_client=gbk"; execute($link,$set); //mysqi_query不打印错误描述 $result=mysqli_query($link, $query); if(mysqli_num_rows($result) >= 1){ #返回结果集中行的数量 while ($data=mysqli_fetch_assoc($result)){ #从结果集中取得一行作为关联数组 $id=$data['id']; $email=$data['email']; $html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>"; } }else{ $html.="<p class='notice'>您输入的username不存在,请重新输入!</p>"; } }
如上述代码的第6,7行,设置为将用户输入的参数用GBK去编码,虽然第3行escape()有转义,但是依然可以用宽字节绕过,这属于在代码中有字符编码的转换函数而造成的,属于开篇提到的第二种情况。
2、sqli-labs之Less-36
-
测试流程
输入
?id=1%df' and 1=1--+
成功回显,说明单引号逃逸出来闭合了语句。
然后和前面一样,order by 二分法测出主查询字段数为3。
直接union注入查数据库名、表名、字段名、字段值等等,进一步获取管理员账号和密码。
这里就不再赘述。
-
源码分析
核心代码:
function check_quotes($string) { $string= mysql_real_escape_string($string); return $string; } // take the variables if(isset($_GET['id'])) { $id=check_quotes($_GET['id']); //echo "The filtered request is :" .$id . "<br>"; //logging the connection parameters to a file for analysis. $fp=fopen('result.txt','a'); fwrite($fp,'ID:'.$id."\n"); fclose($fp); // connectivity mysql_query("SET NAMES gbk"); $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result);
如上述代码第10行,虽然用mysql_real_escape_string()进行了转义,但是后面第20行将字符集编码设置成了GBK,所以直接宽字节就可以绕过,与上例大同小异。