Sqli-labs之Less-36

                                                 Less-36

GET-绕过 Mysql_real_escape_string 函数

mysql_real_escape_string()这个函数是 PHP 过滤的常见函数,在 Less 17 中有详细介绍:

mysql_real_escape_string()函数

mysql_real_escape_string(string,connection)

参数描述
string必需,规定要转义的字符串
connection可选,规定MySQL连接。如果未规定,则使用上一个连接

mysql_real_escape_string()函数转义 SQL 语句中使用的字符串中的特殊字符:

  • \x00
  • \n
  • \r
  • \
  • '
  • "
  • \x1a

如果成功,则该函数返回被转义的字符串。如果失败,则返回FALSE

本函数将字符串中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于mysql_query(),可使用本函数来预防数据库攻击。

 

mysql_query() 函数

定义和用法

mysql_query() 函数执行一条 MySQL 查询。

语法

mysql_query(query,connection)
 
 
参数描述
query必需。规定要发送的 SQL 查询。注释:查询字符串不应以分号结束。
connection可选。规定 SQL 连接标识符。如果未规定,则使用上一个打开的连接。

说明

如果没有打开的连接,本函数会尝试无参数调用 mysql_connect() 函数来建立一个连接并使用之。

返回值

mysql_query() 仅对 SELECT,SHOW,EXPLAIN 或 DESCRIBE 语句返回一个资源标识符,如果查询执行不正确则返回 FALSE。

对于其它类型的 SQL 语句,mysql_query() 在执行成功时返回 TRUE,出错时返回 FALSE。

非 FALSE 的返回值意味着查询是合法的并能够被服务器执行。这并不说明任何有关影响到的或返回的行数。很有可能一条查询执行成功了但并未影响到或并未返回任何行。

提示和注释

注释:该函数自动对记录集进行读取和缓存。如需运行非缓存查询,请使用 mysql_unbuffered_query()

例子 :


 
 
  1. <?php
  2. $con = mysql_connect("localhost","mysql_user","mysql_pwd");
  3. if (!$con)
  4. {
  5. die('Could not connect: ' . mysql_error());
  6. }
  7. $sql = " SELECT * FROM Person ";
  8. mysql_query($sql,$con);
  9. // 一些代码
  10. mysql_close($con);
  11. ?>

在《注入天书》中提到:

但是因 MySQL 我们并没有设置成 GBK,所以mysql_real_escape_string()依旧能够被突破,方法和上述addslashes()是一样的。

在使用mysql_real_escape_string()时,若想防范这种问题,需要将 MySQL 设置为 GBK:)( PS:要使用mysql_set_charset()函数,先编码,再转义
Mysql_set_charset(‘gbk’,’$conn’)

注意 addslashes()能被宽字节注入的原因就是,它进行转义,进行GBK编码。

原理

对于宽字节编码,有一种最好的修补就是:

(1)使用mysql_set_charset(GBK)指定字符集

(2)使用mysql_real_escape_string进行转义

原理是,mysql_real_escape_stringaddslashes的不同之处在于其会考虑当前设置的字符集,不会出现前面df 和 5c拼接为一个宽字节的问题,但是这个“当前字符集”如何确定呢?

就是使用mysql_set_charset进行指定。

上述的两个条件是“与”运算的关系,少一条都不行。

知识的补充:

0x01:宽字节注入

  尽管现在呼吁所有的程序都使用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。

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

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

那么mysql怎么判断一个字符是不是汉字,根据gbk编码,第一个字节ascii码大于128,基本上就可以了。比如我们不用%df,用%a1也可以,%a1%5c他可能不是汉字,但一定会被mysql认为是一个宽字符,就能够让后面的%27逃逸了出来。

但需要注意的是

gb2312和gbk应该都是宽字节家族的一员,却不能注入,这归结于gb2312编码的取值范围。它的高位范围是0xA1~0xF7,低位范围是0xA1~0xFE,而\是0x5c,是不在低位范围中的。所以,0x5c根本不是gb2312中的编码,所以自然也是不会被吃掉的。

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

 

0x02:宽字符注入的修复

先调用mysql_set_charset函数设置连接所使用的字符集为gbk,再调用mysql_real_escape_string函数来过滤用户输入。

(也就是说,先不进行转义,首先以GBK编码的形式对提交上来的参数进行编码,然后再进行转义,(先编码也意味着反斜杠即5c不会出现,到下一步的转义引号才出现)这就造成了编码过后转义引号的反斜杠即字符5c不会和字符df变成一个宽字符,阻止了df 和 5c 的拼接,于是防宽字符注入成功)

这个方式是可行的,但有部分老的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,就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字符注入。

 

接下来我们来看核心源码:

从源代码中我们可以看到,先对提交的id参数进行转义,在进行GBK编码。这就造成了宽字节注入。注入过程同Less-32

我们先看一下别人的:

利用的 ‘ 的 utf-16 进行突破

利用%df 

结果到我这直接转义了df,哎呦我去,怎么和别人的不一样呢?过滤怎么这么严格?(按照原理是不会转义这种单字节的,他是如何发现并转义的?太多疑问了)难道环境出问题了?

再换一种:

?id=0%bb%5c%5c%27 union select 1,2,3-- #

我们知道 addslashes()会在两个%5c%27前都加上\%5c,变为%bb %5c%5c %5c%5c %5c%27但宽字符集认为%bb%5c是一个字符即,则变为%bb%5c %5c%5c %5c%5c %27籠\\\\',四个\正好转义为两个\,即'未被转义

按理说mysql_real_escape_string()作为转义函数应该和addslashes() 一样的操作

但是我这里的mysql_real_escape_string()却是这样做了,%bb%5c%5c%27,变为%bb%5c  %5c%27这两个部分,把%bb%5c作为宽字节,然后在%5c%27的前面各自添加一个5c进行转义,于是就变成了 bb5c5c5c5c27 

问题是别人使用这些方法都能通关,就我不能,我这里的mysql_real_escape_string()函数这么猛的吗?看来只能先放一放,继续学习之后再来解决吧。(PS:感觉的环境的问题,可有挑不出环境的毛病,太难了)

这里附上别人的完整通关过程:

可以通过宽字节 %df ,%E6,%99或者utf-16

2.爆破:

  (1)爆库:?id=-1%E6' union select 1,2,database()--+

    

  (2)爆表:?id=-1%E6' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=0x7365637572697479 --+

    

  (3)爆列名:?id=-1%E6' union select 1,group_concat(column_name),3 from information_schema.columns where table_name=0x7573657273--+

    

  (4)爆值:?id=-1%E6' union select 1,group_concat(username,0x7e,password),3 from security.users --+

    

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值